forked from juliendsv/geocode
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathgeocode.go
162 lines (140 loc) · 3.76 KB
/
geocode.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Package geocode is an interface to The Google Geocoding API.
//
// See http://code.google.com/apis/maps/documentation/geocoding/ for details.
package geocode
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
)
const api = "http://maps.googleapis.com/maps/api/geocode/json"
const uri = "/maps/api/geocode/json"
type Request struct {
// One (and only one) of these must be set.
Address string
Location *Point
// Optional fields.
Bounds *Bounds // Lookup within this viewport.
Region string
Language string
Components string
Channel string
Sensor bool
// Google credential
Googleclient string
Googlesignature string
values url.Values
}
func (r *Request) Values() url.Values {
if r.values == nil {
r.values = make(url.Values)
}
var v = r.values
if r.Address != "" {
v.Set("address", r.Address)
} else if r.Location != nil {
v.Set("latlng", r.Location.String())
} else {
// well, the request will probably fail
// let's return an empty string?
return v
}
if r.Channel != "" {
v.Set("channel", r.Channel)
}
if r.Bounds != nil {
v.Set("bounds", r.Bounds.String())
}
if r.Region != "" {
v.Set("region", r.Region)
}
if r.Language != "" {
v.Set("language", r.Language)
}
if r.Components != "" {
v.Set("components", r.Components)
}
if r.Googleclient != "" {
v.Set("client", r.Googleclient)
}
if r.Googlesignature != "" && r.Googleclient != "" {
v.Set("signature", r.Googlesignature)
}
v.Set("sensor", strconv.FormatBool(r.Sensor))
return v
}
// Lookup makes the Request to the Google Geocoding API servers using
// the provided transport (or http.DefaultTransport if nil).
func (r *Request) Lookup(transport http.RoundTripper) (*Response, error) {
c := http.Client{Transport: transport}
params := r.Values().Encode()
if len(params) == 0 {
return nil, fmt.Errorf("Missing address or latlng argument")
}
u := fmt.Sprintf("%s?%s", api, params)
getResp, err := c.Get(u)
if err != nil {
return nil, err
}
defer getResp.Body.Close()
if getResp.StatusCode < 200 || getResp.StatusCode >= 300 {
body, _ := ioutil.ReadAll(getResp.Body)
return nil, fmt.Errorf("Failed to lookup address (code %d): %s", getResp.StatusCode, body)
}
resp := new(Response)
err = json.NewDecoder(getResp.Body).Decode(resp)
if err != nil {
return nil, err
}
switch resp.Status {
case "OVER_QUERY_LIMIT", "REQUEST_DENIED", "INVALID_REQUEST", "UNKNOWN_ERROR":
return nil, fmt.Errorf("Lookup failed (%s): %s", resp.Status, resp.ErrorMessage)
default:
return resp, nil
}
}
func (r *Request) GetUri() string {
u := fmt.Sprintf("%s?%s", uri, r.Values().Encode())
return u
}
type Response struct {
Status string `json:"status"`
ErrorMessage string `json:"error_message"`
Results []*Result `json:"results"`
}
type Result struct {
Address string `json:"formatted_address"`
AddressParts []*AddressPart `json:"address_components"`
Geometry *Geometry `json:"geometry"`
Types []string `json:"types"`
PartialMatch bool `json:"partial_match,omitempty"`
PlaceId string `json:"place_id,omitempty"`
}
type AddressPart struct {
Name string `json:"long_name"`
ShortName string `json:"short_name"`
Types []string `json:"types"`
}
type Geometry struct {
Bounds *Bounds `json:"bounds,omitempty"`
Location Point `json:"location"`
Type string `json:"location_type"`
Viewport Bounds `json:"viewport"`
}
type Bounds struct {
NorthEast Point `json:"northeast"`
SouthWest Point `json:"southwest"`
}
func (b Bounds) String() string {
return fmt.Sprintf("%v|%v", b.NorthEast, b.SouthWest)
}
type Point struct {
Lat float64 `json:"lat"`
Lng float64 `json:"lng"`
}
func (p Point) String() string {
return fmt.Sprintf("%g,%g", p.Lat, p.Lng)
}