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 / opencage.py @ 564
History | View | Annotate | Download (6.98 KB)
1 |
"""
|
---|---|
2 |
:class:`.OpenCage` is the Opencagedata geocoder.
|
3 |
"""
|
4 |
|
5 |
from geopy.compat import urlencode |
6 |
from geopy.geocoders.base import Geocoder, DEFAULT_TIMEOUT, DEFAULT_SCHEME |
7 |
from geopy.exc import ( |
8 |
GeocoderQueryError, |
9 |
GeocoderQuotaExceeded, |
10 |
) |
11 |
from geopy.location import Location |
12 |
from geopy.util import logger |
13 |
|
14 |
|
15 |
__all__ = ("OpenCage", )
|
16 |
|
17 |
|
18 |
class OpenCage(Geocoder): |
19 |
"""
|
20 |
Geocoder using the Open Cage Data API. Documentation at:
|
21 |
http://geocoder.opencagedata.com/api.html
|
22 |
|
23 |
..versionadded:: 1.1.0
|
24 |
"""
|
25 |
|
26 |
def __init__( |
27 |
self,
|
28 |
api_key, |
29 |
domain='api.opencagedata.com',
|
30 |
scheme=DEFAULT_SCHEME, |
31 |
timeout=DEFAULT_TIMEOUT, |
32 |
proxies=None,
|
33 |
user_agent=None,
|
34 |
): # pylint: disable=R0913
|
35 |
"""
|
36 |
Initialize a customized Open Cage Data geocoder.
|
37 |
|
38 |
:param string api_key: The API key required by Open Cage Data
|
39 |
to perform geocoding requests. You can get your key here:
|
40 |
https://developer.opencagedata.com/
|
41 |
|
42 |
:param string domain: Currently it is 'api.opencagedata.com', can
|
43 |
be changed for testing purposes.
|
44 |
|
45 |
:param string scheme: Use 'https' or 'http' as the API URL's scheme.
|
46 |
Default is https. Note that SSL connections' certificates are not
|
47 |
verified.
|
48 |
|
49 |
:param dict proxies: If specified, routes this geocoder's requests
|
50 |
through the specified proxy. E.g., {"https": "192.0.2.0"}. For
|
51 |
more information, see documentation on
|
52 |
:class:`urllib2.ProxyHandler`.
|
53 |
|
54 |
"""
|
55 |
super(OpenCage, self).__init__( |
56 |
scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent |
57 |
) |
58 |
|
59 |
self.api_key = api_key
|
60 |
self.domain = domain.strip('/') |
61 |
self.scheme = scheme
|
62 |
self.api = '%s://%s/geocode/v1/json' % (self.scheme, self.domain) |
63 |
|
64 |
def geocode( |
65 |
self,
|
66 |
query, |
67 |
bounds=None,
|
68 |
country=None,
|
69 |
language=None,
|
70 |
exactly_one=True,
|
71 |
timeout=None,
|
72 |
): # pylint: disable=W0221,R0913
|
73 |
"""
|
74 |
Geocode a location query.
|
75 |
|
76 |
:param string query: The query string to be geocoded; this must
|
77 |
be URL encoded.
|
78 |
|
79 |
:param string language: an IETF format language code (such as `es`
|
80 |
for Spanish or pt-BR for Brazilian Portuguese); if this is
|
81 |
omitted a code of `en` (English) will be assumed by the remote
|
82 |
service.
|
83 |
|
84 |
:param string bounds: Provides the geocoder with a hint to the region
|
85 |
that the query resides in. This value will help the geocoder
|
86 |
but will not restrict the possible results to the supplied
|
87 |
region. The bounds parameter should be specified as 4
|
88 |
coordinate points forming the south-west and north-east
|
89 |
corners of a bounding box. For example,
|
90 |
`bounds=-0.563160,51.280430,0.278970,51.683979`.
|
91 |
|
92 |
:param string country: Provides the geocoder with a hint to the
|
93 |
country that the query resides in. This value will help the
|
94 |
geocoder but will not restrict the possible results to the
|
95 |
supplied country. The country code is a 3 character code as
|
96 |
defined by the ISO 3166-1 Alpha 3 standard.
|
97 |
|
98 |
:param bool exactly_one: Return one result or a list of results, if
|
99 |
available.
|
100 |
|
101 |
:param int timeout: Time, in seconds, to wait for the geocoding service
|
102 |
to respond before raising a :class:`geopy.exc.GeocoderTimedOut`
|
103 |
exception. Set this only if you wish to override, on this call
|
104 |
only, the value set during the geocoder's initialization.
|
105 |
|
106 |
"""
|
107 |
params = { |
108 |
'key': self.api_key, |
109 |
'q': self.format_string % query, |
110 |
} |
111 |
if bounds:
|
112 |
params['bounds'] = bounds
|
113 |
if language:
|
114 |
params['language'] = language
|
115 |
if country:
|
116 |
params['country'] = country
|
117 |
|
118 |
url = "?".join((self.api, urlencode(params))) |
119 |
|
120 |
logger.debug("%s.geocode: %s", self.__class__.__name__, url) |
121 |
return self._parse_json( |
122 |
self._call_geocoder(url, timeout=timeout), exactly_one
|
123 |
) |
124 |
|
125 |
def reverse( |
126 |
self,
|
127 |
query, |
128 |
language=None,
|
129 |
exactly_one=False,
|
130 |
timeout=None,
|
131 |
): # pylint: disable=W0221,R0913
|
132 |
"""
|
133 |
Given a point, find an address.
|
134 |
|
135 |
:param query: The coordinates for which you wish to obtain the
|
136 |
closest human-readable addresses.
|
137 |
:type query: :class:`geopy.point.Point`, list or tuple of (latitude,
|
138 |
longitude), or string as "%(latitude)s, %(longitude)s"
|
139 |
|
140 |
:param string language: The language in which to return results.
|
141 |
|
142 |
:param boolean exactly_one: Return one result or a list of results, if
|
143 |
available.
|
144 |
|
145 |
:param int timeout: Time, in seconds, to wait for the geocoding service
|
146 |
to respond before raising a :class:`geopy.exc.GeocoderTimedOut`
|
147 |
exception. Set this only if you wish to override, on this call
|
148 |
only, the value set during the geocoder's initialization.
|
149 |
|
150 |
"""
|
151 |
params = { |
152 |
'key': self.api_key, |
153 |
'q': self._coerce_point_to_string(query), |
154 |
} |
155 |
if language:
|
156 |
params['language'] = language
|
157 |
|
158 |
url = "?".join((self.api, urlencode(params))) |
159 |
logger.debug("%s.reverse: %s", self.__class__.__name__, url) |
160 |
return self._parse_json( |
161 |
self._call_geocoder(url, timeout=timeout), exactly_one
|
162 |
) |
163 |
|
164 |
def _parse_json(self, page, exactly_one=True): |
165 |
'''Returns location, (latitude, longitude) from json feed.'''
|
166 |
|
167 |
places = page.get('results', [])
|
168 |
if not len(places): |
169 |
self._check_status(page.get('status')) |
170 |
return None |
171 |
|
172 |
def parse_place(place): |
173 |
'''Get the location, lat, lng from a single json place.'''
|
174 |
location = place.get('formatted')
|
175 |
latitude = place['geometry']['lat'] |
176 |
longitude = place['geometry']['lng'] |
177 |
return Location(location, (latitude, longitude), place)
|
178 |
|
179 |
if exactly_one:
|
180 |
return parse_place(places[0]) |
181 |
else:
|
182 |
return [parse_place(place) for place in places] |
183 |
|
184 |
@staticmethod
|
185 |
def _check_status(status): |
186 |
"""
|
187 |
Validates error statuses.
|
188 |
"""
|
189 |
status_code = status['code']
|
190 |
if status_code == 429: |
191 |
# Rate limit exceeded
|
192 |
raise GeocoderQuotaExceeded(
|
193 |
'The given key has gone over the requests limit in the 24'
|
194 |
' hour period or has submitted too many requests in too'
|
195 |
' short a period of time.'
|
196 |
) |
197 |
if status_code == 200: |
198 |
# When there are no results, just return.
|
199 |
return
|
200 |
|
201 |
if status_code == 403: |
202 |
raise GeocoderQueryError(
|
203 |
'Your request was denied.'
|
204 |
) |
205 |
else:
|
206 |
raise GeocoderQueryError('Unknown error.') |