Backend: Improve location hashing and categories

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2019-12-20 23:05:44 +01:00
parent f3cf300590
commit 1e7c0c2435
25 changed files with 451 additions and 1346 deletions

1
.gitignore vendored
View file

@ -15,6 +15,7 @@
/assets/backups
/assets/resources/nasnet
/assets/resources/nsfw
/package-lock.json
*.log
# Binaries for programs and plugins

View file

@ -4,14 +4,13 @@
"presets": [
"@babel/preset-env"
],
"plugins": [ ["istanbul", {
"exclude": [
"**/*_test.js"
]
}] ]
"plugins": [
["@babel/plugin-transform-runtime"],
["istanbul", {"exclude": ["**/*_test.js"]}]
]
}
},
"presets": [
"@babel/preset-env"
]
}
}

View file

@ -23,8 +23,9 @@ module.exports = (config) => {
},
files: [
{pattern: "tests/unit/**/*_test.js", watched: false},
"node_modules/@babel/polyfill/dist/polyfill.js",
"node_modules/regenerator-runtime/runtime/runtime.js",
{pattern: "tests/unit/**/*_test.js", watched: false},
],
// Preprocess through webpack

File diff suppressed because it is too large Load diff

View file

@ -17,12 +17,13 @@
"gettext-compile": "gettext-compile --output src/resources/translations.json src/resources/*.po"
},
"dependencies": {
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/cli": "^7.7.7",
"@babel/core": "^7.7.7",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/polyfill": "^7.7.0",
"@babel/preset-env": "^7.7.6",
"@babel/register": "^7.7.4",
"@babel/preset-env": "^7.7.7",
"@babel/register": "^7.7.7",
"@babel/runtime": "^7.7.7",
"@fortawesome/fontawesome-free": "^5.12.0",
"@types/leaflet": "^1.5.7",
"acorn": "^6.4.0",
@ -41,13 +42,13 @@
"clean-webpack-plugin": "^3.0.0",
"connect-history-api-fallback": "^1.3.0",
"copy-webpack-plugin": "^5.1.1",
"core-js": "^3.5.0",
"core-js": "^3.6.0",
"cross-env": "^5.2.1",
"crypto-random-string": "^3.0.1",
"css-loader": "^2.1.1",
"cssnano": "^4.1.10",
"easygettext": "^2.9.0",
"eslint": "^6.7.2",
"eslint": "^6.8.0",
"eslint-config-standard": "^13.0.1",
"eslint-formatter-pretty": "^2.1.1",
"eslint-friendly-formatter": "^4.0.1",
@ -62,6 +63,7 @@
"friendly-errors-webpack-plugin": "^1.7.0",
"html-webpack-plugin": "^3.2.0",
"http-proxy-middleware": "^0.19.1",
"i18n-iso-countries": "^4.3.1",
"inject-loader": "^4.0.1",
"karma": "^4.4.1",
"karma-chrome-launcher": "^3.1.0",
@ -110,10 +112,10 @@
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.11",
"vue2-filters": "^0.6.1",
"vue2-leaflet": "^2.3.2",
"vue2-leaflet": "^2.4.1",
"vuelidate": "^0.7.4",
"vuetify": "^1.5.21",
"webpack": "^4.41.3",
"webpack": "^4.41.4",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-cli": "^3.3.10",
"webpack-hot-middleware": "^2.25.0",

View file

@ -49,7 +49,7 @@
{text: '', value: '', align: 'center', sortable: false, class: 'p-col-select'},
{text: this.$gettext('Title'), value: 'PhotoTitle'},
{text: this.$gettext('Taken At'), value: 'TakenAt'},
{text: this.$gettext('Location'), value: 'LocRegion'},
{text: this.$gettext('Location'), value: 'LocDescription'},
{text: this.$gettext('Camera'), value: 'CameraModel'},
{text: this.$gettext('Favorite'), value: 'PhotoFavorite', align: 'left'},
],

View file

@ -105,10 +105,8 @@ class Photo extends Abstract {
}
getLocation() {
if (this.LocRegion) {
return this.LocRegion
} else if (this.CountryName) {
return this.CountryName;
if (this.LocDescription) {
return this.LocDescription
}
return "Unknown"

View file

@ -119,8 +119,6 @@
this.$router.push({name: "places", query: {lat: String(photo.PhotoLat), lng: String(photo.PhotoLng)}});
} else if (photo.LocCity) {
this.$router.push({name: "places", query: {q: photo.LocCity}});
} else {
this.$router.push({name: "places", query: {q: photo.CountryName}});
}
},
openPhoto(index) {

View file

@ -109,8 +109,6 @@
this.$router.push({name: "places", query: {lat: String(photo.PhotoLat), lng: String(photo.PhotoLng)}});
} else if (photo.LocCity) {
this.$router.push({name: "places", query: {q: photo.LocCity}});
} else {
this.$router.push({name: "places", query: {q: photo.CountryName}});
}
},
openPhoto(index) {

View file

@ -135,14 +135,14 @@ describe("model/photo", () => {
});
it("should get location", () => {
const values = {ID: 5, PhotoTitle: "Crazy Cat", LocationID: 6, LocType: "viewpoint", LocRegion: "Cape Point, South Africa", LocCountry: "South Africa"};
const values = {ID: 5, PhotoTitle: "Crazy Cat", LocationID: 6, LocType: "viewpoint", LocDescription: "Cape Point, South Africa", LocCountry: "South Africa"};
const photo = new Photo(values);
const result = photo.getLocation();
assert.equal(result, "Cape Point, South Africa");
});
it("should get location", () => {
const values = {ID: 5, PhotoTitle: "Crazy Cat", LocationID: 6, LocType: "viewpoint", LocRegion: "Cape Point, State, South Africa", LocCountry: "South Africa", LocCity: "Cape Town", LocCounty: "County", LocState: "State"};
const values = {ID: 5, PhotoTitle: "Crazy Cat", LocationID: 6, LocType: "viewpoint", LocDescription: "Cape Point, State, South Africa", LocCountry: "South Africa", LocCity: "Cape Town", LocCounty: "County", LocState: "State"};
const photo = new Photo(values);
const result = photo.getLocation();
assert.equal(result, "Cape Point, State, South Africa");
@ -159,7 +159,7 @@ describe("model/photo", () => {
const values = {ID: 5, PhotoTitle: "Crazy Cat", CountryName: "Africa", LocCity: "Cape Town"};
const photo = new Photo(values);
const result = photo.getLocation();
assert.equal(result, "Africa");
assert.equal(result, "Unknown");
});
it("should get camera", () => {

View file

@ -4,7 +4,6 @@ import (
"strings"
"time"
olc "github.com/google/open-location-code/go"
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/maps"
"github.com/photoprism/photoprism/internal/util"
@ -13,7 +12,6 @@ import (
// Photo location
type Location struct {
maps.Location
LocDescription string `gorm:"type:text;"`
LocNotes string `gorm:"type:text;"`
LocFavorite bool
CreatedAt time.Time
@ -23,19 +21,19 @@ type Location struct {
func NewLocation(lat, lng float64) *Location {
result := &Location{}
result.ID = olc.Encode(lat, lng, 11)
result.ID = maps.OlcEncode(lat, lng)
result.LocLat = lat
result.LocLng = lng
return result
}
func (m *Location) Label() string {
return m.LocLabel
func (m *Location) Category() string {
return m.LocCategory
}
func (m *Location) Find(db *gorm.DB) error {
if err := db.First(m, "id = ?", m.ID).Error; err == nil {
if err := db.First(m, "id LIKE ?", m.ID + "%").Error; err == nil {
return err
}
@ -57,7 +55,7 @@ func (m *Location) Keywords() []string {
strings.ToLower(m.LocSuburb),
strings.ToLower(m.LocState),
strings.ToLower(m.CountryName()),
strings.ToLower(m.LocLabel),
strings.ToLower(m.LocCategory),
}
result = append(result, util.Keywords(m.LocTitle)...)

View file

@ -7,8 +7,8 @@ import (
func TestLocation_Label(t *testing.T) {
l := NewLocation(1,1)
l.LocLabel = "restaurant"
result := l.Label()
l.LocCategory = "restaurant"
result := l.Category()
assert.Equal(t, "restaurant", result)
}

View file

@ -17,24 +17,24 @@ import (
type PhotoSearch struct {
Query string `form:"q"`
Title string `form:"title"`
Description string `form:"description"`
Notes string `form:"notes"`
Artist string `form:"artist"`
Hash string `form:"hash"`
Duplicate bool `form:"duplicate"`
Lat float64 `form:"lat"`
Lng float64 `form:"lng"`
Dist uint `form:"dist"`
Fmin float64 `form:"fmin"`
Fmax float64 `form:"fmax"`
Chroma uint `form:"chroma"`
Mono bool `form:"mono"`
Portrait bool `form:"portrait"`
Location bool `form:"location"`
Album string `form:"album"`
Label string `form:"label"`
Country string `form:"country"`
Title string `form:"title"`
Description string `form:"description"`
Notes string `form:"notes"`
Artist string `form:"artist"`
Hash string `form:"hash"`
Duplicate bool `form:"duplicate"`
Lat float64 `form:"lat"`
Lng float64 `form:"lng"`
Dist uint `form:"dist"`
Fmin float64 `form:"fmin"`
Fmax float64 `form:"fmax"`
Chroma uint `form:"chroma"`
Mono bool `form:"mono"`
Portrait bool `form:"portrait"`
Location bool `form:"location"`
Album string `form:"album"`
Label string `form:"label"`
Country string `form:"country"`
Color string `form:"color"`
Camera int `form:"camera"`
Before time.Time `form:"before" time_format:"2006-01-02"`
@ -43,6 +43,7 @@ type PhotoSearch struct {
Public bool `form:"public"`
Story bool `form:"story"`
Safe bool `form:"safe"`
NSFW bool `form:"nsfw"`
Count int `form:"count" binding:"required"`
Offset int `form:"offset"`

View file

@ -4,39 +4,38 @@ import (
"errors"
"strings"
olc "github.com/google/open-location-code/go"
"github.com/photoprism/photoprism/internal/maps/osm"
)
// Photo location
type Location struct {
ID string `gorm:"primary_key"`
CountryID string
LocLat float64
LocLng float64
LocCategory string
LocTitle string
LocRegion string
LocDescription string
LocCity string
LocSuburb string
LocState string
LocCountryCode string
LocLabel string
LocSource string
}
type LocationSource interface {
CountryCode() string
Latitude() float64
Longitude() float64
Category() string
Title() string
City() string
Suburb() string
State() string
CountryCode() string
Label() string
Source() string
}
func NewLocation (lat, lng float64) *Location {
id := olc.Encode(lat, lng, 11)
id := OlcEncode(lat, lng)
result := &Location{
ID: id,
@ -64,19 +63,19 @@ func (l *Location) Assign(s LocationSource) error {
if l.LocLng == 0 { l.LocLng = s.Longitude() }
if l.Unknown() {
l.LocLabel = "unknown"
l.LocCategory = "unknown"
return errors.New("maps: unknown location")
}
if l.ID == "" { l.ID = olc.Encode(l.LocLat, l.LocLng, 11) }
if l.ID == "" { l.ID = OlcEncode(l.LocLat, l.LocLng) }
l.LocTitle = s.Title()
l.LocCity = s.City()
l.LocSuburb = s.Suburb()
l.LocState = s.State()
l.LocCountryCode = s.CountryCode()
l.LocLabel = s.Label()
l.LocRegion = l.region()
l.CountryID = s.CountryCode()
l.LocCategory = s.Category()
l.LocDescription = l.description()
return nil
}
@ -89,7 +88,7 @@ func (l *Location) Unknown() bool {
return false
}
func (l *Location) region() string {
func (l *Location) description() string {
if l.Unknown() {
return "Unknown"
}
@ -139,8 +138,8 @@ func (l Location) State() string {
return l.LocState
}
func (l Location) Label() string {
return l.LocLabel
func (l Location) Category() string {
return l.LocCategory
}
func (l Location) Source() string {
@ -148,13 +147,13 @@ func (l Location) Source() string {
}
func (l Location) Region() string {
return l.LocRegion
return l.LocDescription
}
func (l Location) CountryCode() string {
return l.LocCountryCode
return l.CountryID
}
func (l Location) CountryName() string {
return CountryNames[l.LocCountryCode]
return CountryNames[l.CountryID]
}

View file

@ -19,7 +19,7 @@ func TestLocation_Query(t *testing.T) {
}
assert.Equal(t, "Fernsehturm Berlin", l.LocTitle)
assert.Equal(t, "Berlin, Germany", l.LocRegion)
assert.Equal(t, "Berlin, Germany", l.LocDescription)
})
}
@ -35,7 +35,7 @@ func TestLocation_Assign(t *testing.T) {
}
assert.Equal(t, 189675302, o.PlaceID)
assert.Equal(t, "Fernsehturm Berlin", o.Name)
assert.Equal(t, "Fernsehturm Berlin", o.LocTitle)
assert.Equal(t, "10178", o.Address.Postcode)
assert.Equal(t, "Berlin", o.Address.State)
assert.Equal(t, "de", o.Address.CountryCode)
@ -48,7 +48,7 @@ func TestLocation_Assign(t *testing.T) {
}
assert.Equal(t, "Fernsehturm Berlin", l.LocTitle)
assert.Equal(t, "Berlin, Germany", l.LocRegion)
assert.Equal(t, "Berlin, Germany", l.LocDescription)
})
t.Run("SantaMonica", func(t *testing.T) {
@ -63,7 +63,7 @@ func TestLocation_Assign(t *testing.T) {
assert.False(t, o.Cached)
assert.Equal(t, 79854991, o.PlaceID)
assert.Equal(t, "Santa Monica Pier", o.Name)
assert.Equal(t, "Santa Monica Pier", o.LocTitle)
assert.Equal(t, "90401", o.Address.Postcode)
assert.Equal(t, "California", o.Address.State)
assert.Equal(t, "us", o.Address.CountryCode)
@ -76,7 +76,7 @@ func TestLocation_Assign(t *testing.T) {
}
assert.Equal(t, "Santa Monica Pier", l.LocTitle)
assert.Equal(t, "Santa Monica, California, USA", l.LocRegion)
assert.Equal(t, "Santa Monica, California, USA", l.LocDescription)
})
t.Run("AirportZurich", func(t *testing.T) {
@ -92,7 +92,7 @@ func TestLocation_Assign(t *testing.T) {
assert.False(t, o.Cached)
assert.Equal(t, 115198412, o.PlaceID)
assert.Equal(t, "Dock A", o.Name)
assert.Equal(t, "Dock A", o.LocTitle)
assert.Equal(t, "8302", o.Address.Postcode)
assert.Equal(t, "Zurich", o.Address.State)
assert.Equal(t, "ch", o.Address.CountryCode)
@ -105,7 +105,7 @@ func TestLocation_Assign(t *testing.T) {
}
assert.Equal(t, "Airport", l.LocTitle)
assert.Equal(t, "Kloten, Zurich, Switzerland", l.LocRegion)
assert.Equal(t, "Kloten, Zurich, Switzerland", l.LocDescription)
})
t.Run("AirportTegel", func(t *testing.T) {
@ -121,7 +121,7 @@ func TestLocation_Assign(t *testing.T) {
assert.False(t, o.Cached)
assert.Equal(t, 25410613, o.PlaceID)
assert.Equal(t, "TGL", o.Name)
assert.Equal(t, "TGL", o.LocTitle)
assert.Equal(t, "13405", o.Address.Postcode)
assert.Equal(t, "Berlin", o.Address.State)
assert.Equal(t, "de", o.Address.CountryCode)
@ -134,7 +134,7 @@ func TestLocation_Assign(t *testing.T) {
}
assert.Equal(t, "Airport", l.LocTitle)
assert.Equal(t, "Berlin, Germany", l.LocRegion)
assert.Equal(t, "Berlin, Germany", l.LocDescription)
})
t.Run("PinkBeach", func(t *testing.T) {
@ -150,7 +150,7 @@ func TestLocation_Assign(t *testing.T) {
assert.False(t, o.Cached)
assert.Equal(t, 119616937, o.PlaceID)
assert.Equal(t, "Pink Beach", o.Name)
assert.Equal(t, "Pink Beach", o.LocTitle)
assert.Equal(t, "", o.Address.Postcode)
assert.Equal(t, "Crete", o.Address.State)
assert.Equal(t, "gr", o.Address.CountryCode)
@ -162,9 +162,9 @@ func TestLocation_Assign(t *testing.T) {
t.Fatal(err)
}
assert.Equal(t, "8G757G9P+R5C", l.ID)
assert.Equal(t, "8G757G9P+", l.ID)
assert.Equal(t, "Pink Beach", l.LocTitle)
assert.Equal(t, "Crete, Greece", l.LocRegion)
assert.Equal(t, "Crete, Greece", l.LocDescription)
})
t.Run("NewJersey", func(t *testing.T) {
@ -180,7 +180,7 @@ func TestLocation_Assign(t *testing.T) {
assert.False(t, o.Cached)
assert.Equal(t, 164551421, o.PlaceID)
assert.Equal(t, "", o.Name)
assert.Equal(t, "", o.LocTitle)
assert.Equal(t, "07307", o.Address.Postcode)
assert.Equal(t, "New Jersey", o.Address.State)
assert.Equal(t, "us", o.Address.CountryCode)
@ -192,9 +192,9 @@ func TestLocation_Assign(t *testing.T) {
t.Fatal(err)
}
assert.Equal(t, "87G7PXV2+4G9", l.ID)
assert.Equal(t, "87G7PXV2+", l.ID)
assert.Equal(t, "", l.LocTitle)
assert.Equal(t, "Jersey City, New Jersey, USA", l.LocRegion)
assert.Equal(t, "Jersey City, New Jersey, USA", l.LocDescription)
})
t.Run("SouthAfrica", func(t *testing.T) {
@ -210,7 +210,7 @@ func TestLocation_Assign(t *testing.T) {
assert.False(t, o.Cached)
assert.Equal(t, 98820569, o.PlaceID)
assert.Equal(t, "R411", o.Name)
assert.Equal(t, "R411", o.LocTitle)
assert.Equal(t, "", o.Address.Postcode)
assert.Equal(t, "Eastern Cape", o.Address.State)
assert.Equal(t, "za", o.Address.CountryCode)
@ -222,8 +222,8 @@ func TestLocation_Assign(t *testing.T) {
t.Fatal(err)
}
assert.Equal(t, "4GWF24FX+F5H", l.ID)
assert.Equal(t, "4GWF24FX+", l.ID)
assert.Equal(t, "R411", l.LocTitle)
assert.Equal(t, "Eastern Cape, South Africa", l.LocRegion)
assert.Equal(t, "Eastern Cape, South Africa", l.LocDescription)
})
}

19
internal/maps/olc.go Normal file
View file

@ -0,0 +1,19 @@
package maps
import olc "github.com/google/open-location-code/go"
var OlcLength = 8
func OlcEncode (lat, lng float64) string {
if lat < -90 || lat > 90 {
log.Warnf("olc: latitude out of range (%f)", lat)
return ""
}
if lng < -180 || lng > 180 {
log.Warnf("olc: longitude out of range (%f)", lat)
return ""
}
return olc.Encode(lat, lng, OlcLength)
}

30
internal/maps/olc_test.go Normal file
View file

@ -0,0 +1,30 @@
package maps
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestOlcEncode(t *testing.T) {
t.Run("Wildgehege", func(t *testing.T) {
plusCode := OlcEncode(48.56344833333333, 8.996878333333333)
expected := "8FWCHX7W+"
assert.Equal(t, expected, plusCode)
})
t.Run("LatOverflow", func(t *testing.T) {
plusCode := OlcEncode(548.56344833333333, 8.996878333333333)
expected := ""
assert.Equal(t, expected, plusCode)
})
t.Run("LongOverflow", func(t *testing.T) {
plusCode := OlcEncode(48.56344833333333, 258.996878333333333)
expected := ""
assert.Equal(t, expected, plusCode)
})
}

View file

@ -1,8 +1,6 @@
package osm
import "fmt"
var locationLabels = map[string]string{
var osmCategories = map[string]string{
"aeroway=*": "airport",
"natural=bay": "bay",
"natural=peninsula": "peninsula",
@ -159,21 +157,3 @@ var locationLabels = map[string]string{
"*=water_park": "water park",
"*=wildlife_hide": "wildlife",
}
func (o Location) Label() (result string) {
key := fmt.Sprintf("%s=%s", o.Category, o.Type)
catKey := fmt.Sprintf("%s=*", o.Category)
typeKey := fmt.Sprintf("*=%s", o.Type)
if result, ok := locationLabels[key]; ok {
return result
} else if result, ok := locationLabels[catKey]; ok {
return result
} else if result, ok := locationLabels[typeKey]; ok {
return result
}
log.Debugf("osm: no label found for %s", key)
return ""
}

View file

@ -0,0 +1,21 @@
package osm
import "fmt"
func (o Location) Category() (result string) {
key := fmt.Sprintf("%s=%s", o.LocCategory, o.LocType)
catKey := fmt.Sprintf("%s=*", o.LocCategory)
typeKey := fmt.Sprintf("*=%s", o.LocType)
if result, ok := osmCategories[key]; ok {
return result
} else if result, ok := osmCategories[catKey]; ok {
return result
} else if result, ok := osmCategories[typeKey]; ok {
return result
}
// log.Debugf("osm: no label found for %s", key)
return ""
}

View file

@ -13,15 +13,15 @@ import (
)
type Location struct {
PlaceID int `json:"place_id"`
Lat string `json:"lat"`
Lon string `json:"lon"`
Name string `json:"name"`
Category string `json:"category"`
Type string `json:"type"`
DisplayName string `json:"display_name"`
Address Address `json:"address"`
Cached bool
PlaceID int `json:"place_id"`
LocLat string `json:"lat"`
LocLng string `json:"lon"`
LocTitle string `json:"name"`
LocCategory string `json:"category"`
LocType string `json:"type"`
LocDisplayName string `json:"display_name"`
Address Address `json:"address"`
Cached bool
}
var ReverseLookupURL = "https://nominatim.openstreetmap.org/reverse?lat=%f&lon=%f&format=jsonv2&accept-language=en&zoom=18"
@ -99,7 +99,7 @@ func (o Location) CountryCode() (result string) {
}
func (o Location) Latitude() (result float64) {
result, err := strconv.ParseFloat(o.Lat, 64)
result, err := strconv.ParseFloat(o.LocLat, 64)
if err != nil {
log.Errorf("osm: %s", err.Error())
@ -109,7 +109,7 @@ func (o Location) Latitude() (result float64) {
}
func (o Location) Longitude() (result float64) {
result, err := strconv.ParseFloat(o.Lon, 64)
result, err := strconv.ParseFloat(o.LocLng, 64)
if err != nil {
log.Errorf("osm: %s", err.Error())
@ -119,7 +119,7 @@ func (o Location) Longitude() (result float64) {
}
func (o Location) Keywords() (result []string) {
return util.Keywords(o.DisplayName)
return util.Keywords(o.LocDisplayName)
}
func (o Location) Source() string {

View file

@ -19,7 +19,7 @@ func TestFindLocation(t *testing.T) {
assert.False(t, l.Cached)
assert.Equal(t, 189675302, l.PlaceID)
assert.Equal(t, "Fernsehturm Berlin", l.Name)
assert.Equal(t, "Fernsehturm Berlin", l.LocTitle)
assert.Equal(t, "10178", l.Address.Postcode)
assert.Equal(t, "Berlin", l.Address.State)
assert.Equal(t, "de", l.Address.CountryCode)
@ -37,7 +37,7 @@ func TestFindLocation(t *testing.T) {
assert.True(t, cached.Cached)
assert.Equal(t, 189675302, cached.PlaceID)
assert.Equal(t, l.Name, cached.Name)
assert.Equal(t, l.LocTitle, cached.LocTitle)
assert.Equal(t, l.Address.Postcode, cached.Address.Postcode)
assert.Equal(t, l.Address.State, cached.Address.State)
assert.Equal(t, l.Address.CountryCode, cached.Address.CountryCode)
@ -56,7 +56,7 @@ func TestFindLocation(t *testing.T) {
assert.False(t, l.Cached)
assert.Equal(t, 48287001, l.PlaceID)
assert.Equal(t, "Menschen Museum", l.Name)
assert.Equal(t, "Menschen Museum", l.LocTitle)
assert.Equal(t, "10178", l.Address.Postcode)
assert.Equal(t, "Berlin", l.Address.State)
assert.Equal(t, "de", l.Address.CountryCode)

View file

@ -12,15 +12,15 @@ var labelTitles = map[string]string{
}
func (o Location) Title() (result string) {
result = o.Label()
result = o.Category()
if title, ok := labelTitles[result]; ok {
title = strings.Replace(title, "%name%", o.Name, 1)
title = strings.Replace(title, "%name%", o.LocTitle, 1)
return title
}
if o.Name != "" {
result = o.Name
if o.LocTitle != "" {
result = o.LocTitle
}
result = strings.Replace(result, "_", " ", -1)

View file

@ -388,14 +388,14 @@ func (i *Indexer) indexLocation(mediaFile *MediaFile, photo *entity.Photo, label
}
countryName := photo.Country.CountryName
locLabel := location.Label()
locCategory := location.Category()
keywords = append(keywords, location.Keywords()...)
// Append label from OpenStreetMap
if locLabel != "" {
keywords = append(keywords, locLabel)
labels = append(labels, NewLocationLabel(locLabel, 0, -1))
// Append category from reverse location lookup
if locCategory != "" {
keywords = append(keywords, locCategory)
labels = append(labels, NewLocationLabel(locCategory, 0, -1))
}
if (fileChanged || o.UpdateTitle) && photo.PhotoTitleChanged == false {

View file

@ -30,9 +30,9 @@ func TestMediaFile_Location(t *testing.T) {
assert.Equal(t, "Himeji", location.City())
assert.Equal(t, "Kinki Region", location.State())
assert.Equal(t, "Japan", location.CountryName())
assert.Equal(t, "", location.Label())
assert.Equal(t, "", location.Category())
assert.Equal(t, 34.79745, location.Latitude())
assert.Equal(t, "8Q6PQQW7+XVJ", location.ID)
assert.Equal(t, "8Q6PQQW7+", location.ID)
location2, err := mediaFile.Location()
if err != nil {
@ -68,7 +68,7 @@ func TestMediaFile_Location(t *testing.T) {
assert.Equal(t, "de", location.CountryCode())
assert.Equal(t, "Germany", location.CountryName())
assert.Equal(t, 48.53870833333333, location.Latitude())
assert.Equal(t, "8FWFG2Q6+FVM", location.ID)
assert.Equal(t, "8FWFG2Q6+", location.ID)
})
t.Run("/dog_orange.jpg", func(t *testing.T) {
conf := config.TestConfig()

View file

@ -54,17 +54,15 @@ type PhotoResult struct {
// Country
CountryID string
CountryName string
// Location
LocationID string
LocTitle string
LocDescription string
LocCity string
LocSuburb string
LocState string
LocCountryCode string
LocRegion string
LocLabel string
LocCategory string
LocSource string
LocationChanged bool
LocationEstimated bool
@ -120,13 +118,11 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
files.file_orientation, files.file_main_color, files.file_colors, files.file_luminance, files.file_chroma,
cameras.camera_make, cameras.camera_model,
lenses.lens_make, lenses.lens_model,
countries.country_name,
locations.loc_title, locations.loc_city, locations.loc_suburb, locations.loc_state, locations.loc_country_code,
locations.loc_region, locations.loc_label, locations.loc_source`).
locations.loc_title, locations.loc_description, locations.loc_city, locations.loc_suburb, locations.loc_state,
locations.loc_category, locations.loc_source`).
Joins("JOIN files ON files.photo_id = photos.id AND files.file_primary AND files.deleted_at IS NULL").
Joins("JOIN cameras ON cameras.id = photos.camera_id").
Joins("JOIN lenses ON lenses.id = photos.lens_id").
Joins("LEFT JOIN countries ON countries.id = photos.country_id").
Joins("LEFT JOIN locations ON locations.id = photos.location_id").
Joins("LEFT JOIN photos_labels ON photos_labels.photo_id = photos.id").
Where("photos.deleted_at IS NULL AND files.file_missing = 0").
@ -215,12 +211,16 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
q = q.Where("photos.photo_nsfw = 0")
}
if f.NSFW {
q = q.Where("photos.photo_nsfw = 1")
}
if f.Story {
q = q.Where("photos.photo_story = 1")
}
if f.Country != "" {
q = q.Where("locations.loc_country_code = ?", f.Country)
q = q.Where("photos.country_id = ?", f.Country)
}
if f.Title != "" {