forked from jasonwinn/geocoder
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgeocoding.go
146 lines (121 loc) · 3.29 KB
/
geocoding.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/* Exposes (partially) the mapquest geocoding api.
Reference: http://open.mapquestapi.com/geocoding/
Example:
lat, lng := Geocode("Seattle WA")
address := ReverseGeocode(47.603561, -122.329437)
*/
package geocoder
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/url"
)
const (
geocodeURL = "https://open.mapquestapi.com/geocoding/v1/address?inFormat=kvp&outFormat=json&location="
reverseGeocodeURL = "https://open.mapquestapi.com/geocoding/v1/reverse?location="
batchGeocodeURL = "https://open.mapquestapi.com/geocoding/v1/batch?key="
)
// Geocode returns the latitude and longitude for a certain address
func Geocode(address string) (lat float64, lng float64) {
// Query Provider
resp, err := http.Get(geocodeURL + url.QueryEscape(address) + "&key=" + apiKey)
if err != nil {
panic(err)
}
defer resp.Body.Close()
// Decode our JSON results
var result geocodingResults
err = decoder(resp).Decode(&result)
if err != nil {
panic(err)
}
if len(result.Results[0].Locations) > 0 {
lat = result.Results[0].Locations[0].LatLng.Lat
lng = result.Results[0].Locations[0].LatLng.Lng
}
return
}
// ReverseGeocode returns the address for a certain latitude and longitude
func ReverseGeocode(lat float64, lng float64) *Location {
// Query Provider
resp, err := http.Get(reverseGeocodeURL +
fmt.Sprintf("%f,%f&key=%s", lat, lng, apiKey))
if err != nil {
panic(err)
}
defer resp.Body.Close()
// Decode our JSON results
var result geocodingResults
err = decoder(resp).Decode(&result)
if err != nil {
panic(err)
}
var location Location
// Assign the results to the Location struct
if len(result.Results[0].Locations) > 0 {
location = result.Results[0].Locations[0]
}
return &location
}
// BatchGeocode allows multiple locations to be geocoded at the same time.
// A limit of 100 locations exists for one call. Therefore the json is
// embedded as the body of an http post.
func BatchGeocode(addresses []string) (latLngs []LatLng) {
var next, start, end int
n := len(addresses)
latLngs = make([]LatLng, n)
batches := n/100 + 1
next = 0
for batch := 0; batch < batches; batch++ {
start = next
next = (batch + 1) * 100
if n < next {
end = n
} else {
end = next
}
bgb := batchGeocodeBody{
Locations: addresses[start:end],
MaxResults: 1,
ThumbMaps: false,
}
b, err := json.Marshal(bgb)
if err != nil {
panic(err)
}
body := bytes.NewBuffer(b)
resp, err := http.Post(batchGeocodeURL+apiKey, "application/json", body)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var result geocodingResults
err = decoder(resp).Decode(&result)
if err != nil {
panic(err)
}
for i, r := range result.Results {
if len(r.Locations) == 0 {
latLngs[start+i] = LatLng{Lat: 0, Lng: 0}
} else {
latLngs[start+i] = r.Locations[0].LatLng
}
}
}
return
}
// geocodingResults contains the locations of a geocoding request
// MapQuest providers more JSON fields than this but this is all we are interested in.
type geocodingResults struct {
Results []struct {
Locations []Location `json:"locations"`
} `json:"results"`
}
// batchGeocodeBody will be marshalled as json to send in body with http post
type batchGeocodeBody struct {
Locations []string `json:"locations"`
MaxResults int
ThumbMaps bool `json:"thumbMaps"`
}