Skip to content

Commit 4e358bb

Browse files
committed
Places: Improve handling of unknown S2 cell ids
1 parent 1ee3609 commit 4e358bb

5 files changed

Lines changed: 61 additions & 29 deletions

File tree

internal/entity/cell.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func (m *Cell) Refresh(api string) (err error) {
7474
cellMutex.Lock()
7575
defer cellMutex.Unlock()
7676

77-
// Query geodata API.
77+
// Retrieve location details from Places API.
7878
if err = l.QueryApi(api); err != nil {
7979
return err
8080
}
@@ -150,6 +150,7 @@ func (m *Cell) Find(api string) error {
150150
cellMutex.Lock()
151151
defer cellMutex.Unlock()
152152

153+
// Retrieve location details from Places API.
153154
if err := l.QueryApi(api); err != nil {
154155
return err
155156
}

internal/entity/cell_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func TestLocation_Find(t *testing.T) {
6262
t.Fatal("error expected")
6363
}
6464

65-
assert.Equal(t, "maps: reverse lookup disabled", err.Error())
65+
assert.Equal(t, "maps: location lookup disabled", err.Error())
6666
})
6767
}
6868

internal/entity/photo_location.go

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -358,36 +358,37 @@ func (m *Photo) GetTakenAtLocal() time.Time {
358358
// UpdateLocation updates location and labels based on latitude and longitude.
359359
func (m *Photo) UpdateLocation() (keywords []string, labels classify.Labels) {
360360
if m.HasLatLng() {
361-
var location = NewCell(m.PhotoLat, m.PhotoLng)
361+
var loc = NewCell(m.PhotoLat, m.PhotoLng)
362362

363-
err := location.Find(GeoApi)
364-
365-
if err != nil {
363+
if loc.Unknown() {
364+
// Empty or unknown S2 cell id... should not happen, unless coordinates are invalid.
365+
log.Warnf("photo: unknown cell id for lat %f, lng %f (uid %s)", m.PhotoLat, m.PhotoLng, m.PhotoUID)
366+
} else if err := loc.Find(GeoApi); err != nil {
366367
log.Errorf("photo: %s (find location)", err)
367-
} else if location.Place == nil {
368-
log.Warnf("photo: failed fetching geo data (uid %s, cell %s)", m.PhotoUID, location.ID)
369-
} else if location.ID != UnknownLocation.ID {
370-
changed := m.CellID != location.ID
368+
} else if loc.Place == nil {
369+
log.Warnf("photo: failed fetching geo data (uid %s, cell %s)", m.PhotoUID, loc.ID)
370+
} else if loc.ID != UnknownLocation.ID {
371+
changed := m.CellID != loc.ID
371372

372373
if changed {
373-
log.Debugf("photo: changing location of %s from %s to %s", m.String(), m.CellID, location.ID)
374+
log.Debugf("photo: changing location of %s from %s to %s", m.String(), m.CellID, loc.ID)
374375
m.RemoveLocationLabels()
375376
}
376377

377-
m.Cell = location
378-
m.CellID = location.ID
379-
m.Place = location.Place
380-
m.PlaceID = location.PlaceID
381-
m.PhotoCountry = location.CountryCode()
378+
m.Cell = loc
379+
m.CellID = loc.ID
380+
m.Place = loc.Place
381+
m.PlaceID = loc.PlaceID
382+
m.PhotoCountry = loc.CountryCode()
382383

383384
if changed && m.TakenSrc != SrcManual {
384385
m.UpdateTimeZone(m.GetTimeZone())
385386
}
386387

387-
FirstOrCreateCountry(NewCountry(location.CountryCode(), location.CountryName()))
388+
FirstOrCreateCountry(NewCountry(loc.CountryCode(), loc.CountryName()))
388389

389-
locCategory := location.Category()
390-
keywords = append(keywords, location.Keywords()...)
390+
locCategory := loc.Category()
391+
keywords = append(keywords, loc.Keywords()...)
391392

392393
// Append category from reverse location lookup
393394
if locCategory != "" {

internal/hub/places/location.go

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type Location struct {
2525
Cached bool `json:"-"`
2626
}
2727

28+
// ApiName is the backend API name.
2829
const ApiName = "places"
2930

3031
var Key = "f60f5b25d59c397989e3cd374f81cdd7710a4fca"
@@ -36,18 +37,30 @@ var Retries = 3
3637
var RetryDelay = 33 * time.Millisecond
3738
var client = &http.Client{Timeout: 60 * time.Second}
3839

40+
// FindLocation retrieves location details from the backend API.
3941
func FindLocation(id string) (result Location, err error) {
40-
if len(id) > 16 || len(id) == 0 {
41-
return result, fmt.Errorf("invalid cell %s (%s)", id, ApiName)
42+
// Normalize S2 Cell ID.
43+
id = s2.NormalizeToken(id)
44+
45+
// Valid?
46+
if len(id) == 0 {
47+
return result, fmt.Errorf("empty cell id")
48+
} else if n := len(id); n < 4 || n > 16 {
49+
return result, fmt.Errorf("invalid cell id %s", txt.Quote(id))
4250
}
4351

52+
// Remember start time.
4453
start := time.Now()
54+
55+
// Convert S2 Cell ID to latitude and longitude.
4556
lat, lng := s2.LatLng(id)
4657

58+
// Return if latitude and longitude are null.
4759
if lat == 0.0 || lng == 0.0 {
48-
return result, fmt.Errorf("skipping lat %f, lng %f (%s)", lat, lng, ApiName)
60+
return result, fmt.Errorf("skipping lat %f, lng %f", lat, lng)
4961
}
5062

63+
// Location details cached?
5164
if hit, ok := cache.Get(id); ok {
5265
log.Tracef("places: cache hit for lat %f, lng %f", lat, lng)
5366
cached := hit.(Location)
@@ -58,11 +71,13 @@ func FindLocation(id string) (result Location, err error) {
5871
// Compose request URL.
5972
url := fmt.Sprintf(ReverseLookupURL, id)
6073

74+
// Log request URL.
6175
log.Tracef("places: sending request to %s", url)
6276

6377
// Create GET request instance.
6478
req, err := http.NewRequest(http.MethodGet, url, nil)
6579

80+
// Ok?
6681
if err != nil {
6782
log.Errorf("places: %s", err.Error())
6883
return result, err
@@ -98,89 +113,104 @@ func FindLocation(id string) (result Location, err error) {
98113

99114
// Failed?
100115
if err != nil {
101-
log.Errorf("places: %s (http request)", err.Error())
116+
log.Errorf("places: %s (http request failed)", err.Error())
102117
return result, err
103118
} else if r.StatusCode >= 400 {
104-
err = fmt.Errorf("request failed with code %d (%s)", r.StatusCode, ApiName)
119+
err = fmt.Errorf("request failed with code %d", r.StatusCode)
105120
return result, err
106121
}
107122

108123
// Decode JSON response body.
109124
err = json.NewDecoder(r.Body).Decode(&result)
110125

111126
if err != nil {
112-
log.Errorf("places: %s (decode json)", err.Error())
127+
log.Errorf("places: %s (decode json failed)", err.Error())
113128
return result, err
114129
}
115130

116131
if result.ID == "" {
117-
return result, fmt.Errorf("no result for %s (%s)", id, ApiName)
132+
return result, fmt.Errorf("no result for %s", id)
118133
}
119134

120135
cache.SetDefault(id, result)
121-
log.Tracef("places: cached cell %s [%s]", id, time.Since(start))
136+
log.Tracef("places: cached cell %s [%s]", txt.Quote(id), time.Since(start))
122137

123138
result.Cached = false
124139

125140
return result, nil
126141
}
127142

143+
// CellID returns the S2 cell identifier string.
128144
func (l Location) CellID() string {
129145
return l.ID
130146
}
131147

148+
// PlaceID returns the place identifier string.
132149
func (l Location) PlaceID() string {
133150
return l.Place.PlaceID
134151
}
135152

153+
// Name returns the location name if any.
136154
func (l Location) Name() (result string) {
137155
return strings.SplitN(l.LocName, "/", 2)[0]
138156
}
139157

158+
// Street returns the location street if any.
140159
func (l Location) Street() (result string) {
141160
return strings.SplitN(l.LocStreet, "/", 2)[0]
142161
}
143162

163+
// Postcode returns the location postcode if any.
144164
func (l Location) Postcode() (result string) {
145165
return strings.SplitN(l.LocPostcode, "/", 2)[0]
146166
}
147167

168+
// Category returns the location category if any.
148169
func (l Location) Category() (result string) {
149170
return l.LocCategory
150171
}
151172

173+
// Label returns the location label.
152174
func (l Location) Label() (result string) {
153175
return l.Place.LocLabel
154176
}
155177

178+
// City returns the location address city name.
156179
func (l Location) City() (result string) {
157180
return l.Place.LocCity
158181
}
159182

183+
// District returns the location address district name.
160184
func (l Location) District() (result string) {
161185
return l.Place.LocDistrict
162186
}
163187

188+
// CountryCode returns the location address country code.
164189
func (l Location) CountryCode() (result string) {
165190
return l.Place.LocCountry
166191
}
167192

193+
// State returns the location address state name.
168194
func (l Location) State() (result string) {
169195
return txt.NormalizeState(l.Place.LocState, l.CountryCode())
170196
}
171197

198+
// Latitude returns the location position latitude.
172199
func (l Location) Latitude() (result float64) {
173200
return l.LocLat
174201
}
175202

203+
// Longitude returns the location position longitude.
176204
func (l Location) Longitude() (result float64) {
177205
return l.LocLng
178206
}
179207

208+
// Keywords returns location keywords if any.
180209
func (l Location) Keywords() (result []string) {
181210
return txt.UniqueWords(txt.Words(l.Place.LocKeywords))
182211
}
183212

213+
// Source returns the backend API name.
184214
func (l Location) Source() string {
185215
return "places"
186216
}

internal/maps/location.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ type LocationSource interface {
4343

4444
func (l *Location) QueryApi(api string) error {
4545
switch api {
46-
case "places":
46+
case places.ApiName:
4747
return l.QueryPlaces()
4848
}
4949

50-
return errors.New("maps: reverse lookup disabled")
50+
return errors.New("maps: location lookup disabled")
5151
}
5252

5353
func (l *Location) QueryPlaces() error {

0 commit comments

Comments
 (0)