Backend: Add OpenStreetMap package

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2019-12-19 17:17:13 +01:00
parent be2376d986
commit 410e82f5c3
7 changed files with 196 additions and 0 deletions

2
go.mod
View file

@ -32,6 +32,7 @@ require (
github.com/leandro-lugaresi/hub v1.1.0 github.com/leandro-lugaresi/hub v1.1.0
github.com/lucasb-eyer/go-colorful v1.0.2 github.com/lucasb-eyer/go-colorful v1.0.2
github.com/mattn/go-isatty v0.0.4 // indirect 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/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/montanaflynn/stats v0.0.0-20181214052348-945b007cb92f // 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/remyoudompheng/bigfft v0.0.0-20190512091148-babf20351dd7 // indirect
github.com/satori/go.uuid v1.2.0 github.com/satori/go.uuid v1.2.0
github.com/sevlyar/go-daemon v0.1.5 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/simplereach/timeutils v1.2.0 // indirect
github.com/sirupsen/logrus v1.4.2 github.com/sirupsen/logrus v1.4.2
github.com/soheilhy/cmux v0.1.4 // indirect github.com/soheilhy/cmux v0.1.4 // indirect

4
go.sum
View file

@ -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/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 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 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 h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 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= 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/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 h1:Zy/6jLbM8CfqJ4x4RPr7MJlSKt90f00kNM1D401C+Qk=
github.com/sevlyar/go-daemon v0.1.5/go.mod h1:6dJpPatBT9eUwM5VCw9Bt6CdX9Tk6UWvhW3MebLDRKE= 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 h1:btgOAlu9RW6de2r2qQiONhjgxdAG7BL6je0G6J/yPnA=
github.com/simplereach/timeutils v1.2.0/go.mod h1:VVbQDfN/FHRZa1LSqcwo4kNZ62OOyqLLGQKYB3pB0Q8= 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= github.com/sirupsen/logrus v0.0.0-20170323161349-3bcb09397d6d/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=

14
internal/osm/address.go Normal file
View 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
View 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
View 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
}

View 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
View 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