-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathw3w.go
218 lines (166 loc) · 5.55 KB
/
w3w.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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
package w3w
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
)
const (
endpoint string = "https://api.what3words.com"
)
// Default error codes
var (
ErrNoAPIKey = errors.New("No API Key specified")
)
var (
defs = Options{"en", false}
)
// ----------------------------------------------------------------------------
// What3Words type
// ----------------------------------------------------------------------------
// What3Words holds the 3 word position returned or passed to the server
type What3Words [3]string
// ----------------------------------------------------------------------------
// LatLng type
// ----------------------------------------------------------------------------
// LatLng type
type LatLng [2]float64
// Lat extracts the latitude from the latLng type
func (ll *LatLng) Lat() float64 {
return ll[0]
}
// Lng extracts the longitude from the latLng type
func (ll *LatLng) Lng() float64 {
return ll[1]
}
// ----------------------------------------------------------------------------
// BBox type
// ----------------------------------------------------------------------------
// BBox represents the corners of the W3W square
type BBox [2]*LatLng
// SW returns the southwest coordinates of the square
func (b *BBox) SW() *LatLng {
return b[0]
}
// NE returns the northeast coordinates of the square
func (b *BBox) NE() *LatLng {
return b[1]
}
// ----------------------------------------------------------------------------
// Position struct
// ----------------------------------------------------------------------------
// Position holds the server response from a query
type Position struct {
Type string `json:"type"`
Words What3Words `json:"words"`
Position *LatLng `json:"position"`
Corners *BBox `json:"corners"`
Language string `json:"language"`
}
// ----------------------------------------------------------------------------
// Language struct
// ----------------------------------------------------------------------------
// Language holds a single language type from the languages call
type Language struct {
Code string `json:"code"`
Name string `json:"name_display"`
}
// ----------------------------------------------------------------------------
// Languages struct
// ----------------------------------------------------------------------------
// Languages holds the full languages response from the server
type Languages struct {
Languages []Language `json:"languages"`
}
// ----------------------------------------------------------------------------
// Options struct
// ----------------------------------------------------------------------------
// Options holds the various optional query params for all W3W calls.
type Options struct {
Lang string
Corners bool
}
func (o *Options) add(v *url.Values) {
if o.Lang == "" {
v.Set("lang", "en")
} else {
v.Set("lang", o.Lang)
}
if o.Corners {
v.Set("corners", "true")
}
}
// ----------------------------------------------------------------------------
// W3W struct
// ----------------------------------------------------------------------------
// W3W holds details about the connection to the W3W service
type W3W struct {
apikey string
client *http.Client
defaults *Options
}
// New returns a W3W with the given API key. The options defaults allows for sensible defaults to be
// associated with each W3W call.
//
// if the key is missing or empty, the returned error is `ErrNoAPIKey`
func New(apikey string, defaults *Options) (*W3W, error) {
if apikey == "" || strings.TrimSpace(apikey) == "" {
return nil, ErrNoAPIKey
}
if defaults == nil {
return &W3W{apikey, &http.Client{}, &defs}, nil
}
return &W3W{apikey, &http.Client{}, defaults}, nil
}
// Words converts a 3 word string to LatLng position
func (w *W3W) Words(words What3Words, opts *Options) (*Position, error) {
vals := url.Values{}
vals.Set("key", w.apikey)
vals.Set("string", strings.Join(words[:], "."))
pos, err := w.exec(endpoint+"/w3w", &vals, opts, &Position{})
return pos.(*Position), err
}
// Position converts a 3 word string to LatLng position
func (w *W3W) Position(ll LatLng, opts *Options) (*Position, error) {
vals := url.Values{}
vals.Set("key", w.apikey)
vals.Set("position", fmt.Sprintf("%.15f,%.15f", ll[0], ll[1]))
pos, err := w.exec(endpoint+"/position", &vals, opts, &Position{})
return pos.(*Position), err
}
// LangsW3W obtains the list of available 3 word lanagues for a given W3W position
func (w *W3W) LangsW3W(words What3Words, opts *Options) (*Languages, error) {
vals := url.Values{}
vals.Set("key", w.apikey)
vals.Set("string", strings.Join(words[:], "."))
langs, err := w.exec(endpoint+"/get-languages", &vals, opts, &Languages{[]Language{}})
return langs.(*Languages), err
}
// LangsPos obtains the list of available 3 word lanagues for a given LatLng position
func (w *W3W) LangsPos(ll LatLng, opts *Options) (*Languages, error) {
vals := url.Values{}
vals.Set("key", w.apikey)
vals.Set("position", fmt.Sprintf("%.15f,%.15f", ll[0], ll[1]))
langs, err := w.exec(endpoint+"/get-languages", &vals, opts, &Languages{[]Language{}})
return langs.(*Languages), err
}
func (w *W3W) exec(url string, vals *url.Values, opts *Options, in interface{}) (interface{}, error) {
if opts != nil {
opts.add(vals)
} else {
w.defaults.add(vals)
}
req, err := http.NewRequest("GET", url+"?"+vals.Encode(), nil)
req.Header.Add("Accept", "application/json")
resp, err := w.client.Do(req)
if err != nil {
return nil, err
}
err = json.NewDecoder(resp.Body).Decode(in)
if err != nil {
return nil, err
}
return in, nil
}