2019-12-20 11:30:58 +01:00
|
|
|
package osm
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/melihmucuk/geocache"
|
|
|
|
"github.com/photoprism/photoprism/internal/util"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Location struct {
|
2019-12-20 23:05:44 +01:00
|
|
|
PlaceID int `json:"place_id"`
|
|
|
|
LocLat string `json:"lat"`
|
|
|
|
LocLng string `json:"lon"`
|
2019-12-27 05:18:52 +01:00
|
|
|
LocName string `json:"name"`
|
2019-12-20 23:05:44 +01:00
|
|
|
LocCategory string `json:"category"`
|
|
|
|
LocType string `json:"type"`
|
|
|
|
LocDisplayName string `json:"display_name"`
|
|
|
|
Address Address `json:"address"`
|
|
|
|
Cached bool
|
2019-12-20 11:30:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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, lng float64) (result Location, err error) {
|
|
|
|
if lat == 0.0 || lng == 0.0 {
|
2019-12-20 12:04:26 +01:00
|
|
|
return result, fmt.Errorf("osm: skipping lat %f, lng %f", lat, lng)
|
2019-12-20 11:30:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
point := geocache.GeoPoint{Latitude: lat, Longitude: lng}
|
|
|
|
|
|
|
|
if hit, ok := geoCache.Get(point); ok {
|
2019-12-20 12:04:26 +01:00
|
|
|
log.Debugf("osm: cache hit for lat %f, lng %f", lat, lng)
|
2019-12-20 11:30:58 +01:00
|
|
|
result = hit.(Location)
|
|
|
|
result.Cached = true
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
url := fmt.Sprintf(ReverseLookupURL, lat, lng)
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-12-20 12:20:35 +01:00
|
|
|
func (o Location) State() (result string) {
|
2019-12-20 11:30:58 +01:00
|
|
|
result = o.Address.State
|
|
|
|
|
|
|
|
return strings.TrimSpace(result)
|
|
|
|
}
|
|
|
|
|
2019-12-20 12:20:35 +01:00
|
|
|
func (o Location) City() (result string) {
|
2019-12-20 11:30:58 +01:00
|
|
|
if o.Address.City != "" {
|
|
|
|
result = o.Address.City
|
|
|
|
} else if o.Address.Town != "" {
|
|
|
|
result = o.Address.Town
|
2019-12-21 16:22:24 +01:00
|
|
|
} else if o.Address.Village != "" {
|
|
|
|
result = o.Address.Village
|
2019-12-20 11:30:58 +01:00
|
|
|
} else if o.Address.County != "" {
|
|
|
|
result = o.Address.County
|
|
|
|
} else if o.Address.State != "" {
|
|
|
|
result = o.Address.State
|
|
|
|
}
|
|
|
|
|
2019-12-28 20:24:20 +01:00
|
|
|
if len([]rune(result)) > 19 {
|
|
|
|
result = ""
|
|
|
|
}
|
|
|
|
|
2019-12-20 11:30:58 +01:00
|
|
|
return strings.TrimSpace(result)
|
|
|
|
}
|
|
|
|
|
2019-12-20 12:20:35 +01:00
|
|
|
func (o Location) Suburb() (result string) {
|
2019-12-20 11:30:58 +01:00
|
|
|
result = o.Address.Suburb
|
|
|
|
|
|
|
|
return strings.TrimSpace(result)
|
|
|
|
}
|
|
|
|
|
2019-12-20 20:23:16 +01:00
|
|
|
func (o Location) CountryCode() (result string) {
|
2019-12-20 11:30:58 +01:00
|
|
|
result = o.Address.CountryCode
|
|
|
|
|
|
|
|
return strings.ToLower(strings.TrimSpace(result))
|
|
|
|
}
|
|
|
|
|
2019-12-20 12:20:35 +01:00
|
|
|
func (o Location) Latitude() (result float64) {
|
2019-12-27 05:18:52 +01:00
|
|
|
if o.LocLat == "" {
|
|
|
|
log.Warn("osm: no latitude")
|
|
|
|
return 0.0
|
|
|
|
}
|
|
|
|
|
2019-12-20 23:05:44 +01:00
|
|
|
result, err := strconv.ParseFloat(o.LocLat, 64)
|
2019-12-20 11:30:58 +01:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("osm: %s", err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2019-12-20 12:20:35 +01:00
|
|
|
func (o Location) Longitude() (result float64) {
|
2019-12-27 05:18:52 +01:00
|
|
|
if o.LocLng == "" {
|
|
|
|
log.Warn("osm: no longitude")
|
|
|
|
return 0.0
|
|
|
|
}
|
|
|
|
|
2019-12-20 23:05:44 +01:00
|
|
|
result, err := strconv.ParseFloat(o.LocLng, 64)
|
2019-12-20 11:30:58 +01:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("osm: %s", err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2019-12-20 12:20:35 +01:00
|
|
|
func (o Location) Keywords() (result []string) {
|
2019-12-20 23:05:44 +01:00
|
|
|
return util.Keywords(o.LocDisplayName)
|
2019-12-20 11:30:58 +01:00
|
|
|
}
|
2019-12-20 12:20:35 +01:00
|
|
|
|
|
|
|
func (o Location) Source() string {
|
|
|
|
return "osm"
|
|
|
|
}
|