/
geocoder.go
95 lines (78 loc) · 2.18 KB
/
geocoder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// Package locationiq is a geo-golang based LocationIQ geocode/reverse geocode client
package locationiq
import (
"fmt"
"strings"
"github.com/codingsince1985/geo-golang"
"github.com/codingsince1985/geo-golang/osm"
)
type baseURL string
type geocodeResponse struct {
DisplayName string `json:"display_name"`
Lat, Lon, Error string
Addr osm.Address `json:"address"`
}
const (
defaultURL = "http://locationiq.org/v1/"
minZoom = 0 // Min zoom level for locationiq - country level
maxZoom = 18 // Max zoom level for locationiq - house level
defaultZoom = 18
)
var (
key string
zoom int
)
// Geocoder constructs LocationIQ geocoder
func Geocoder(k string, z int, baseURLs ...string) geo.Geocoder {
key = k
var url string
if len(baseURLs) > 0 {
url = baseURLs[0]
} else {
url = defaultURL
}
if z > minZoom && z <= maxZoom {
zoom = z
} else {
zoom = defaultZoom
}
return geo.HTTPGeocoder{
EndpointBuilder: baseURL(url),
ResponseParserFactory: func() geo.ResponseParser { return &geocodeResponse{} },
}
}
func (b baseURL) GeocodeURL(address string) string {
return string(b) + "search.php?key=" + key + "&format=json&limit=1&q=" + address
}
func (b baseURL) ReverseGeocodeURL(l geo.Location) string {
return string(b) + "reverse.php?key=" + key + fmt.Sprintf("&format=json&lat=%f&lon=%f&zoom=%d", l.Lat, l.Lng, zoom)
}
func (r *geocodeResponse) Location() (*geo.Location, error) {
if r.Error != "" {
return nil, fmt.Errorf("geocoding error: %s", r.Error)
}
// no result
if r.Lat == "" || r.Lon == "" {
return nil, nil
}
return &geo.Location{
Lat: geo.ParseFloat(r.Lat),
Lng: geo.ParseFloat(r.Lon),
}, nil
}
func (r *geocodeResponse) Address() (*geo.Address, error) {
if r.Error != "" {
return nil, fmt.Errorf("reverse geocoding error: %s", r.Error)
}
return &geo.Address{
FormattedAddress: r.DisplayName,
Street: r.Addr.Street(),
HouseNumber: r.Addr.HouseNumber,
City: r.Addr.Locality(),
Postcode: r.Addr.Postcode,
Suburb: r.Addr.Suburb,
State: r.Addr.State,
Country: r.Addr.Country,
CountryCode: strings.ToUpper(r.Addr.CountryCode),
}, nil
}