gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / geopy / geocoders / what3words.py @ 564
History | View | Annotate | Download (8.35 KB)
1 |
"""
|
---|---|
2 |
:class:`.What3Words` geocoder.
|
3 |
"""
|
4 |
|
5 |
import re |
6 |
from geopy.compat import urlencode |
7 |
from geopy.geocoders.base import ( |
8 |
Geocoder, |
9 |
DEFAULT_FORMAT_STRING, |
10 |
DEFAULT_TIMEOUT, |
11 |
DEFAULT_SCHEME |
12 |
) |
13 |
from geopy.location import Location |
14 |
from geopy.util import logger, join_filter |
15 |
from geopy import exc |
16 |
|
17 |
|
18 |
__all__ = ("What3Words", )
|
19 |
|
20 |
|
21 |
class What3Words(Geocoder): |
22 |
"""
|
23 |
What3Words geocoder, documentation at:
|
24 |
http://what3words.com/api/reference
|
25 |
"""
|
26 |
|
27 |
word_re = re.compile(r"^\*{1,1}[^\W\d\_]+$", re.U)
|
28 |
multiple_word_re = re.compile( |
29 |
r"[^\W\d\_]+\.{1,1}[^\W\d\_]+\.{1,1}[^\W\d\_]+$", re.U
|
30 |
) |
31 |
|
32 |
def __init__( |
33 |
self,
|
34 |
api_key, |
35 |
format_string=DEFAULT_FORMAT_STRING, |
36 |
scheme=DEFAULT_SCHEME, |
37 |
timeout=DEFAULT_TIMEOUT, |
38 |
proxies=None,
|
39 |
user_agent=None,
|
40 |
): |
41 |
"""
|
42 |
Initialize a What3Words geocoder with 3-word or OneWord-address and
|
43 |
What3Words API key.
|
44 |
|
45 |
.. versionadded:: 1.5.0
|
46 |
|
47 |
:param string api_key: Key provided by What3Words.
|
48 |
|
49 |
:param string format_string: String containing '%s' where the
|
50 |
string to geocode should be interpolated before querying the
|
51 |
geocoder. For example: '%s, piped.gains.jungle'. The default
|
52 |
is just '%s'.
|
53 |
|
54 |
:param string scheme: Use 'https' or 'http' as the API URL's scheme.
|
55 |
Default is https. Note that SSL connections' certificates are not
|
56 |
verified.
|
57 |
|
58 |
|
59 |
:param int timeout: Time, in seconds, to wait for the geocoding service
|
60 |
to respond before raising a :class:`geopy.exc.GeocoderTimedOut`
|
61 |
exception.
|
62 |
|
63 |
|
64 |
:param dict proxies: If specified, routes this geocoder's requests
|
65 |
through the specified proxy. E.g., {"https": "192.0.2.0"}. For
|
66 |
more information, see documentation on
|
67 |
:class:`urllib2.ProxyHandler`.
|
68 |
"""
|
69 |
super(What3Words, self).__init__( |
70 |
format_string, |
71 |
scheme, |
72 |
timeout, |
73 |
proxies, |
74 |
user_agent=user_agent, |
75 |
) |
76 |
self.api_key = api_key
|
77 |
self.api = (
|
78 |
"%s://api.what3words.com/" % self.scheme |
79 |
) |
80 |
|
81 |
def _check_query(self, query): |
82 |
"""
|
83 |
Check query validity with regex
|
84 |
"""
|
85 |
if not (self.word_re.match(query) or |
86 |
self.multiple_word_re.match(query)):
|
87 |
return False |
88 |
else:
|
89 |
return True |
90 |
|
91 |
def geocode(self, |
92 |
query, |
93 |
lang='en',
|
94 |
exactly_one=True,
|
95 |
timeout=None):
|
96 |
|
97 |
"""
|
98 |
Geocode a "3 words" or "OneWord" query.
|
99 |
|
100 |
:param string query: The 3-word or OneWord-address you wish to geocode.
|
101 |
|
102 |
:param string lang: two character language codes as supported by
|
103 |
the API (http://what3words.com/api/reference/languages).
|
104 |
|
105 |
:param bool exactly_one: Parameter has no effect for this geocoder.
|
106 |
Due to the address scheme there is always exactly one result.
|
107 |
|
108 |
:param int timeout: Time, in seconds, to wait for the geocoding service
|
109 |
to respond before raising a :class:`geopy.exc.GeocoderTimedOut`
|
110 |
exception. Set this only if you wish to override, on this call
|
111 |
only, the value set during the geocoder's initialization.
|
112 |
.. versionadded:: 0.97
|
113 |
"""
|
114 |
|
115 |
if not self._check_query(query): |
116 |
raise exc.GeocoderQueryError(
|
117 |
"Search string must be either like "
|
118 |
"'word.word.word' or '*word' "
|
119 |
) |
120 |
|
121 |
params = { |
122 |
'string': self.format_string % query, |
123 |
'lang': self.format_string % lang.lower() |
124 |
|
125 |
} |
126 |
|
127 |
url = "?".join((
|
128 |
(self.api + "w3w"), |
129 |
"&".join(("=".join(('key', self.api_key)), urlencode(params))) |
130 |
)) |
131 |
logger.debug("%s.geocode: %s", self.__class__.__name__, url) |
132 |
return self._parse_json( |
133 |
self._call_geocoder(url, timeout=timeout),
|
134 |
exactly_one |
135 |
) |
136 |
|
137 |
def _parse_json(self, resources, exactly_one=True): |
138 |
"""
|
139 |
Parse type, words, latitude, and longitude and language from a
|
140 |
JSON response.
|
141 |
"""
|
142 |
if resources.get('error') == "X1": |
143 |
raise exc.GeocoderAuthenticationFailure()
|
144 |
|
145 |
if resources.get('error') == "11": |
146 |
raise exc.GeocoderQueryError(
|
147 |
"Address (Word(s)) not recognised by What3Words."
|
148 |
) |
149 |
|
150 |
def parse_resource(resource): |
151 |
"""
|
152 |
Parse record.
|
153 |
"""
|
154 |
|
155 |
if resource['type'] == '3 words': |
156 |
words = resource['words']
|
157 |
words = join_filter(".", [words[0], words[1], words[2]]) |
158 |
position = resource['position']
|
159 |
latitude, longitude = position[0], position[1] |
160 |
|
161 |
if latitude and longitude: |
162 |
latitude = float(latitude)
|
163 |
longitude = float(longitude)
|
164 |
|
165 |
return Location(words, (latitude, longitude), resource)
|
166 |
elif resource['type'] == 'OneWord': |
167 |
words = resource['words']
|
168 |
words = join_filter(".", [words[0], words[1], words[2]]) |
169 |
oneword = resource['oneword']
|
170 |
info = resource['info']
|
171 |
|
172 |
address = join_filter(", ", [
|
173 |
oneword, |
174 |
words, |
175 |
info['name'],
|
176 |
info['address1'],
|
177 |
info['address2'],
|
178 |
info['address3'],
|
179 |
info['city'],
|
180 |
info['county'],
|
181 |
info['postcode'],
|
182 |
info['country_id']
|
183 |
]) |
184 |
|
185 |
position = resource['position']
|
186 |
latitude, longitude = position[0], position[1] |
187 |
|
188 |
if latitude and longitude: |
189 |
latitude = float(latitude)
|
190 |
longitude = float(longitude)
|
191 |
|
192 |
return Location(address, (latitude, longitude), resource)
|
193 |
else:
|
194 |
raise exc.GeocoderParseError('Error parsing result.') |
195 |
|
196 |
|
197 |
return parse_resource(resources)
|
198 |
|
199 |
|
200 |
def reverse(self, query, lang='en', exactly_one=True, timeout=None): |
201 |
"""
|
202 |
Given a point, find the 3 word address.
|
203 |
|
204 |
:param query: The coordinates for which you wish to obtain the 3 word
|
205 |
address.
|
206 |
|
207 |
:type query: :class:`geopy.point.Point`, list or tuple of (latitude,
|
208 |
longitude), or string as "%(latitude)s, %(longitude)s"
|
209 |
|
210 |
:param string lang: two character language codes as supported by the
|
211 |
API (http://what3words.com/api/reference/languages).
|
212 |
|
213 |
:param bool exactly_one: Parameter has no effect for this geocoder.
|
214 |
Due to the address scheme there is always exactly one result.
|
215 |
|
216 |
:param int timeout: Time, in seconds, to wait for the geocoding service
|
217 |
to respond before raising a :class:`geopy.exc.GeocoderTimedOut`
|
218 |
exception. Set this only if you wish to override, on this call
|
219 |
only, the value set during the geocoder's initialization.
|
220 |
|
221 |
"""
|
222 |
lang = lang.lower() |
223 |
|
224 |
params = { |
225 |
'position': self._coerce_point_to_string(query), |
226 |
'lang': self.format_string % lang |
227 |
|
228 |
} |
229 |
|
230 |
url = "?".join((
|
231 |
(self.api + "position"), |
232 |
"&".join(("=".join(('key', self.api_key)), urlencode(params))) |
233 |
)) |
234 |
|
235 |
logger.debug("%s.reverse: %s", self.__class__.__name__, url) |
236 |
return self._parse_reverse_json( |
237 |
self._call_geocoder(url, timeout=timeout),
|
238 |
) |
239 |
|
240 |
|
241 |
@staticmethod
|
242 |
def _parse_reverse_json(resources): |
243 |
"""
|
244 |
Parses a location from a single-result reverse API call.
|
245 |
"""
|
246 |
|
247 |
if resources.get('error') == "21": |
248 |
raise exc.GeocoderQueryError("Invalid coordinates") |
249 |
|
250 |
def parse_resource(resource): |
251 |
"""
|
252 |
Parse resource to return Geopy Location object
|
253 |
"""
|
254 |
words = resource['words']
|
255 |
words = join_filter(".", [words[0], words[1], words[2]]) |
256 |
position = resource['position']
|
257 |
latitude, longitude = position[0], position[1] |
258 |
|
259 |
if latitude and longitude: |
260 |
latitude = float(latitude)
|
261 |
longitude = float(longitude)
|
262 |
|
263 |
return Location(words, (latitude, longitude), resource)
|
264 |
|
265 |
return parse_resource(resources)
|
266 |
|
267 |
|
268 |
|
269 |
|
270 |
|
271 |
|
272 |
|