Backend: Add OpenStreetMap package
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
be2376d986
commit
410e82f5c3
7 changed files with 196 additions and 0 deletions
2
go.mod
2
go.mod
|
@ -32,6 +32,7 @@ require (
|
|||
github.com/leandro-lugaresi/hub v1.1.0
|
||||
github.com/lucasb-eyer/go-colorful v1.0.2
|
||||
github.com/mattn/go-isatty v0.0.4 // indirect
|
||||
github.com/melihmucuk/geocache v0.0.0-20160621165317-521b336a001c
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/montanaflynn/stats v0.0.0-20181214052348-945b007cb92f // indirect
|
||||
|
@ -50,6 +51,7 @@ require (
|
|||
github.com/remyoudompheng/bigfft v0.0.0-20190512091148-babf20351dd7 // indirect
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/sevlyar/go-daemon v0.1.5
|
||||
github.com/shopspring/decimal v0.0.0-20191130220710-360f2bc03045 // indirect
|
||||
github.com/simplereach/timeutils v1.2.0 // indirect
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/soheilhy/cmux v0.1.4 // indirect
|
||||
|
|
4
go.sum
4
go.sum
|
@ -183,6 +183,8 @@ github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK86
|
|||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/melihmucuk/geocache v0.0.0-20160621165317-521b336a001c h1:1ErTnOL2d0OvfUABvEjGcPM8cKSLxYZpJiYS4BfQ3o4=
|
||||
github.com/melihmucuk/geocache v0.0.0-20160621165317-521b336a001c/go.mod h1:CX2bLGC22DrgJTaYvKt+lOi3BACGNA60hbFXh2iWebs=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
|
@ -269,6 +271,8 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
|||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sevlyar/go-daemon v0.1.5 h1:Zy/6jLbM8CfqJ4x4RPr7MJlSKt90f00kNM1D401C+Qk=
|
||||
github.com/sevlyar/go-daemon v0.1.5/go.mod h1:6dJpPatBT9eUwM5VCw9Bt6CdX9Tk6UWvhW3MebLDRKE=
|
||||
github.com/shopspring/decimal v0.0.0-20191130220710-360f2bc03045 h1:8CnFGhoe92Izugjok8nZEGYCNovJwdRFYwrEiLtG6ZQ=
|
||||
github.com/shopspring/decimal v0.0.0-20191130220710-360f2bc03045/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/simplereach/timeutils v1.2.0 h1:btgOAlu9RW6de2r2qQiONhjgxdAG7BL6je0G6J/yPnA=
|
||||
github.com/simplereach/timeutils v1.2.0/go.mod h1:VVbQDfN/FHRZa1LSqcwo4kNZ62OOyqLLGQKYB3pB0Q8=
|
||||
github.com/sirupsen/logrus v0.0.0-20170323161349-3bcb09397d6d/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
|
|
14
internal/osm/address.go
Normal file
14
internal/osm/address.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package osm
|
||||
|
||||
type Address struct {
|
||||
HouseNumber string `json:"house_number"`
|
||||
Road string `json:"road"`
|
||||
Suburb string `json:"suburb"`
|
||||
Town string `json:"town"`
|
||||
City string `json:"city"`
|
||||
Postcode string `json:"postcode"`
|
||||
County string `json:"county"`
|
||||
State string `json:"state"`
|
||||
Country string `json:"country"`
|
||||
CountryCode string `json:"country_code"`
|
||||
}
|
19
internal/osm/cache.go
Normal file
19
internal/osm/cache.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package osm
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/melihmucuk/geocache"
|
||||
)
|
||||
|
||||
var geoCache *geocache.Cache
|
||||
|
||||
func init() {
|
||||
c, err := geocache.NewCache(time.Hour, 5*time.Minute, geocache.WithIn1M)
|
||||
|
||||
if err != nil {
|
||||
log.Panicf("osm: %s", err.Error())
|
||||
}
|
||||
|
||||
geoCache = c
|
||||
}
|
64
internal/osm/location.go
Normal file
64
internal/osm/location.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
package osm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/melihmucuk/geocache"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
var ReverseLookupURL = "https://nominatim.openstreetmap.org/reverse?lat=%f&lon=%f&format=jsonv2&accept-language=en&zoom=18"
|
||||
|
||||
// API docs see https://wiki.openstreetmap.org/wiki/Nominatim#Reverse_Geocoding
|
||||
func FindLocation(lat, long float64) (result Location, err error) {
|
||||
if lat == 0.0 || long == 0.0 {
|
||||
return result, fmt.Errorf("osm: skipping lat %f / long %f", lat, long)
|
||||
}
|
||||
|
||||
point := geocache.GeoPoint{Latitude: lat, Longitude: long}
|
||||
|
||||
if hit, ok := geoCache.Get(point); ok {
|
||||
log.Debugf("osm: cache hit for lat %f / long %f", lat, long)
|
||||
result = hit.(Location)
|
||||
result.Cached = true
|
||||
return result, nil
|
||||
}
|
||||
|
||||
url := fmt.Sprintf(ReverseLookupURL, lat, long)
|
||||
|
||||
log.Debugf("osm: query %s", url)
|
||||
|
||||
r, err := http.Get(url)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("osm: %s", err.Error())
|
||||
return result, err
|
||||
}
|
||||
|
||||
err = json.NewDecoder(r.Body).Decode(&result)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("osm: %s", err.Error())
|
||||
return result, err
|
||||
}
|
||||
|
||||
geoCache.Set(point, result, time.Hour)
|
||||
|
||||
result.Cached = false
|
||||
|
||||
return result, nil
|
||||
}
|
79
internal/osm/location_test.go
Normal file
79
internal/osm/location_test.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
package osm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFindLocation(t *testing.T) {
|
||||
t.Run("Fernsehturm Berlin", func(t *testing.T) {
|
||||
lat := 52.5208
|
||||
long := 13.40953
|
||||
|
||||
l, err := FindLocation(lat, long)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.False(t, l.Cached)
|
||||
assert.Equal(t, 189675302, l.PlaceID)
|
||||
assert.Equal(t, "Fernsehturm Berlin", l.Name)
|
||||
assert.Equal(t, "10178", l.Address.Postcode)
|
||||
assert.Equal(t, "Berlin", l.Address.State)
|
||||
assert.Equal(t, "de", l.Address.CountryCode)
|
||||
assert.Equal(t, "Germany", l.Address.Country)
|
||||
|
||||
l.PlaceID = 123456
|
||||
|
||||
assert.Equal(t, 123456, l.PlaceID)
|
||||
|
||||
cached, err := FindLocation(lat, long)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.True(t, cached.Cached)
|
||||
assert.Equal(t, 189675302, cached.PlaceID)
|
||||
assert.Equal(t, l.Name, cached.Name)
|
||||
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)
|
||||
assert.Equal(t, l.Address.Country, cached.Address.Country)
|
||||
})
|
||||
|
||||
t.Run("Menschen Museum", func(t *testing.T) {
|
||||
lat := 52.52057
|
||||
long := 13.40889
|
||||
|
||||
l, err := FindLocation(lat, long)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.False(t, l.Cached)
|
||||
assert.Equal(t, 48287001, l.PlaceID)
|
||||
assert.Equal(t, "Menschen Museum", l.Name)
|
||||
assert.Equal(t, "10178", l.Address.Postcode)
|
||||
assert.Equal(t, "Berlin", l.Address.State)
|
||||
assert.Equal(t, "de", l.Address.CountryCode)
|
||||
assert.Equal(t, "Germany", l.Address.Country)
|
||||
})
|
||||
|
||||
t.Run("No Location", func(t *testing.T) {
|
||||
lat := 0.0
|
||||
long := 0.0
|
||||
|
||||
l, err := FindLocation(lat, long)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("err should not be nil")
|
||||
}
|
||||
|
||||
assert.Equal(t, "osm: skipping lat 0.000000 / long 0.000000", err.Error())
|
||||
assert.False(t, l.Cached)
|
||||
})
|
||||
}
|
14
internal/osm/osm.go
Normal file
14
internal/osm/osm.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
This package encapsulates the OpenStreetMap API.
|
||||
|
||||
Additional information can be found in our Developer Guide:
|
||||
|
||||
https://github.com/photoprism/photoprism/wiki
|
||||
*/
|
||||
package osm
|
||||
|
||||
import (
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
)
|
||||
|
||||
var log = event.Log
|
Loading…
Reference in a new issue