Places: Support sub-km distances when searching for locations #3558

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer 2023-09-20 18:37:41 +02:00
parent d7e21f449f
commit 33bf0cede6
7 changed files with 39 additions and 16 deletions

View file

@ -48,7 +48,7 @@ type SearchPhotos struct {
Olc string `form:"olc" example:"olc:8FWCHX7W+" notes:"OLC Position (Open Location Code)"`
Lat float64 `form:"lat" example:"lat:41.894043" notes:"GPS Position (Latitude)"`
Lng float64 `form:"lng" example:"lng:-87.62448" notes:"GPS Position (Longitude)"`
Dist uint `form:"dist" example:"dist:50" notes:"Distance to Position (km)"`
Dist float64 `form:"dist" example:"dist:50" notes:"Distance to Position (km)"`
Latlng string `form:"latlng" notes:"GPS Bounding Box (Lat N, Lng E, Lat S, Lng W)"`
Fmin float32 `form:"fmin" notes:"F-number (min)"`
Fmax float32 `form:"fmax" notes:"F-number (max)"`

View file

@ -46,7 +46,7 @@ type SearchPhotosGeo struct {
Olc string `form:"olc" example:"olc:8FWCHX7W+" notes:"OLC Position (Open Location Code)"`
Lat float64 `form:"lat" example:"lat:41.894043" notes:"GPS Position (Latitude)"`
Lng float64 `form:"lng" example:"lng:-87.62448" notes:"GPS Position (Longitude)"`
Dist uint `form:"dist" example:"dist:50" notes:"Distance to Position (km)"`
Dist float64 `form:"dist" example:"dist:50" notes:"Distance to Position (km)"`
Latlng string `form:"latlng" notes:"GPS Bounding Box (Lat N, Lng E, Lat S, Lng W)"`
Person string `form:"person"` // Alias for Subject
Subjects string `form:"subjects"` // Text

View file

@ -107,7 +107,7 @@ func TestSearchPhotosGeo(t *testing.T) {
assert.Equal(t, "fooBar baz", form.Query)
assert.Equal(t, time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC), form.Before)
assert.Equal(t, uint(0x61a8), form.Dist)
assert.Equal(t, 25000.0, form.Dist)
assert.Equal(t, 33.45343166666667, form.Lat)
})
t.Run("valid query path empty folder not empty", func(t *testing.T) {
@ -125,7 +125,7 @@ func TestSearchPhotosGeo(t *testing.T) {
assert.Equal(t, "test", form.Path)
assert.Equal(t, "", form.Folder)
assert.Equal(t, time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC), form.Before)
assert.Equal(t, uint(0x61a8), form.Dist)
assert.Equal(t, 25000.0, form.Dist)
assert.Equal(t, 33.45343166666667, form.Lat)
})
t.Run("valid query with filter", func(t *testing.T) {

View file

@ -82,14 +82,14 @@ func GPSBounds(bounds string) (latN, lngE, latS, lngW float32, err error) {
}
// GPSLatRange returns a range based on the specified latitude and distance in km, or an error otherwise.
func GPSLatRange(lat float64, km uint) (latN, latS float32, err error) {
func GPSLatRange(lat float64, km float64) (latN, latS float32, err error) {
// Latitude (from +90 to -90 degrees).
if lat == 0 || lat < -90 || lat > 90 {
return 0, 0, fmt.Errorf("invalid latitude")
}
// Approximate range radius.
r := float64(km) * 0.75
r := km * 0.75
// Approximate longitude range,
// see https://en.wikipedia.org/wiki/Decimal_degrees
@ -108,14 +108,14 @@ func GPSLatRange(lat float64, km uint) (latN, latS float32, err error) {
}
// GPSLngRange returns a range based on the specified longitude and distance in km, or an error otherwise.
func GPSLngRange(lng float64, km uint) (lngE, lngW float32, err error) {
func GPSLngRange(lng float64, km float64) (lngE, lngW float32, err error) {
// Longitude (from -180 to +180 degrees).
if lng == 0 || lng < -180 || lng > 180 {
return 0, 0, fmt.Errorf("invalid longitude")
}
// Approximate range radius.
r := float64(km) * 0.75
r := km * 0.75
// Approximate longitude range,
// see https://en.wikipedia.org/wiki/Decimal_degrees

View file

@ -5,9 +5,9 @@ import (
)
const (
DistLimit uint = 5000
ScopeDistLimit uint = 50
DefaultDist uint = 2
DistLimit float64 = 5000
ScopeDistLimit float64 = 50
DefaultDist float64 = 2
)
// Deg returns the approximate distance in decimal degrees,

View file

@ -3,9 +3,9 @@ package s2
// DefaultLevel specifies the default S2 cell size.
var DefaultLevel = 21
// Level returns the S2 cell level based on the approximate cell size in km.
// see https://s2geometry.io/resources/s2cell_statistics.html
func Level(km uint) (level int) {
// Level returns the applicable cell level based on the search range in km,
// see https://s2geometry.io/resources/s2cell_statistics.html.
func Level(km float64) (level int) {
switch {
case km >= 7842:
return 0
@ -35,7 +35,21 @@ func Level(km uint) (level int) {
return 12
case km >= 1:
return 13
default:
case km >= 0.425:
return 14
case km >= 0.212:
return 15
case km >= 0.106:
return 16
case km >= 0.053:
return 17
case km >= 0.027:
return 18
case km >= 0.013:
return 19
case km >= 0.007:
return 20
default:
return DefaultLevel
}
}

View file

@ -109,8 +109,17 @@ func TestLevel(t *testing.T) {
t.Run("150", func(t *testing.T) {
assert.Equal(t, 6, Level(150))
})
t.Run("0.25", func(t *testing.T) {
assert.Equal(t, 15, Level(0.25))
})
t.Run("0.1", func(t *testing.T) {
assert.Equal(t, 17, Level(0.1))
})
t.Run("0", func(t *testing.T) {
assert.Equal(t, 14, Level(0))
assert.Equal(t, 21, Level(0))
})
t.Run("Negative", func(t *testing.T) {
assert.Equal(t, 21, Level(-1))
})
}