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 / osm.py @ 564
History | View | Annotate | Download (8.83 KB)
1 |
"""
|
---|---|
2 |
OpenStreetMaps geocoder, contributed by Alessandro Pasotti of ItOpen.
|
3 |
"""
|
4 |
|
5 |
from geopy.geocoders.base import ( |
6 |
Geocoder, |
7 |
DEFAULT_FORMAT_STRING, |
8 |
DEFAULT_TIMEOUT, |
9 |
DEFAULT_SCHEME |
10 |
) |
11 |
from geopy.compat import urlencode |
12 |
from geopy.location import Location |
13 |
from geopy.util import logger |
14 |
from geopy.exc import GeocoderQueryError |
15 |
|
16 |
|
17 |
__all__ = ("Nominatim", )
|
18 |
|
19 |
|
20 |
class Nominatim(Geocoder): |
21 |
"""
|
22 |
Nominatim geocoder for OpenStreetMap servers. Documentation at:
|
23 |
https://wiki.openstreetmap.org/wiki/Nominatim
|
24 |
|
25 |
Note that Nominatim does not support SSL.
|
26 |
"""
|
27 |
|
28 |
structured_query_params = { |
29 |
'street',
|
30 |
'city',
|
31 |
'county',
|
32 |
'state',
|
33 |
'country',
|
34 |
'postalcode',
|
35 |
} |
36 |
|
37 |
def __init__( |
38 |
self,
|
39 |
format_string=DEFAULT_FORMAT_STRING, |
40 |
view_box=None,
|
41 |
country_bias=None,
|
42 |
timeout=DEFAULT_TIMEOUT, |
43 |
proxies=None,
|
44 |
domain='nominatim.openstreetmap.org',
|
45 |
scheme=DEFAULT_SCHEME, |
46 |
user_agent=None
|
47 |
): # pylint: disable=R0913
|
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, Mountain View, CA'. The default
|
52 |
is just '%s'.
|
53 |
|
54 |
:param tuple view_box: Coordinates to restrict search within.
|
55 |
|
56 |
:param string country_bias: Bias results to this country.
|
57 |
|
58 |
:param dict proxies: If specified, routes this geocoder's requests
|
59 |
through the specified proxy. E.g., {"https": "192.0.2.0"}. For
|
60 |
more information, see documentation on
|
61 |
:class:`urllib2.ProxyHandler`.
|
62 |
|
63 |
.. versionadded:: 0.96
|
64 |
|
65 |
:param string domain: Should be the localized Openstreetmap domain to
|
66 |
connect to. The default is 'nominatim.openstreetmap.org', but you
|
67 |
can change it to a domain of your own.
|
68 |
|
69 |
.. versionadded:: 1.8.2
|
70 |
|
71 |
:param string scheme: Use 'https' or 'http' as the API URL's scheme.
|
72 |
Default is https. Note that SSL connections' certificates are not
|
73 |
verified.
|
74 |
|
75 |
.. versionadded:: 1.8.2
|
76 |
"""
|
77 |
super(Nominatim, self).__init__( |
78 |
format_string, scheme, timeout, proxies, user_agent=user_agent |
79 |
) |
80 |
self.country_bias = country_bias
|
81 |
self.format_string = format_string
|
82 |
self.view_box = view_box
|
83 |
self.domain = domain.strip('/') |
84 |
|
85 |
self.api = "%s://%s/search" % (self.scheme, self.domain) |
86 |
self.reverse_api = "%s://%s/reverse" % (self.scheme, self.domain) |
87 |
|
88 |
def geocode( |
89 |
self,
|
90 |
query, |
91 |
exactly_one=True,
|
92 |
timeout=None,
|
93 |
addressdetails=False,
|
94 |
language=False,
|
95 |
geometry=None
|
96 |
): # pylint: disable=R0913,W0221
|
97 |
"""
|
98 |
Geocode a location query.
|
99 |
|
100 |
:param query: The address, query or structured query to geocode
|
101 |
you wish to geocode.
|
102 |
|
103 |
For a structured query, provide a dictionary whose keys
|
104 |
are one of: `street`, `city`, `county`, `state`, `country`, or
|
105 |
`postalcode`. For more information, see Nominatim's
|
106 |
documentation for "structured requests":
|
107 |
|
108 |
https://wiki.openstreetmap.org/wiki/Nominatim
|
109 |
|
110 |
:type query: dict or string
|
111 |
|
112 |
.. versionchanged:: 1.0.0
|
113 |
|
114 |
:param bool exactly_one: Return one result or a list of results, if
|
115 |
available.
|
116 |
|
117 |
:param int timeout: Time, in seconds, to wait for the geocoding service
|
118 |
to respond before raising a :class:`geopy.exc.GeocoderTimedOut`
|
119 |
exception. Set this only if you wish to override, on this call
|
120 |
only, the value set during the geocoder's initialization.
|
121 |
|
122 |
.. versionadded:: 0.97
|
123 |
|
124 |
:param addressdetails: If you want in *Location.raw* to include
|
125 |
addressdetails such as city_district, etc set it to True
|
126 |
:type addressdetails: bool
|
127 |
|
128 |
:param string language: Preferred language in which to return results.
|
129 |
Either uses standard
|
130 |
`RFC2616 <http://www.ietf.org/rfc/rfc2616.txt>`_
|
131 |
accept-language string or a simple comma-separated
|
132 |
list of language codes.
|
133 |
:type addressdetails: string
|
134 |
|
135 |
.. versionadded:: 1.0.0
|
136 |
|
137 |
:param string geometry: If present, specifies whether the geocoding
|
138 |
service should return the result's geometry in `wkt`, `svg`,
|
139 |
`kml`, or `geojson` formats. This is available via the
|
140 |
`raw` attribute on the returned :class:`geopy.location.Location`
|
141 |
object.
|
142 |
|
143 |
.. versionadded:: 1.3.0
|
144 |
|
145 |
"""
|
146 |
|
147 |
if isinstance(query, dict): |
148 |
params = { |
149 |
key: val |
150 |
for key, val
|
151 |
in query.items()
|
152 |
if key in self.structured_query_params |
153 |
} |
154 |
else:
|
155 |
params = {'q': self.format_string % query} |
156 |
|
157 |
params.update({ |
158 |
'format': 'json' |
159 |
}) |
160 |
|
161 |
# `viewbox` apparently replaces `view_box`
|
162 |
if self.view_box: |
163 |
params['viewbox'] = ','.join(self.view_box) |
164 |
|
165 |
if self.country_bias: |
166 |
params['countrycodes'] = self.country_bias |
167 |
|
168 |
if addressdetails:
|
169 |
params['addressdetails'] = 1 |
170 |
|
171 |
if language:
|
172 |
params['accept-language'] = language
|
173 |
|
174 |
if geometry is not None: |
175 |
geometry = geometry.lower() |
176 |
if geometry == 'wkt': |
177 |
params['polygon_text'] = 1 |
178 |
elif geometry == 'svg': |
179 |
params['polygon_svg'] = 1 |
180 |
elif geometry == 'kml': |
181 |
params['polygon_kml'] = 1 |
182 |
elif geometry == 'geojson': |
183 |
params['polygon_geojson'] = 1 |
184 |
else:
|
185 |
raise GeocoderQueryError(
|
186 |
"Invalid geometry format. Must be one of: "
|
187 |
"wkt, svg, kml, geojson."
|
188 |
) |
189 |
|
190 |
url = "?".join((self.api, urlencode(params))) |
191 |
logger.debug("%s.geocode: %s", self.__class__.__name__, url) |
192 |
return self._parse_json( |
193 |
self._call_geocoder(url, timeout=timeout), exactly_one
|
194 |
) |
195 |
|
196 |
def reverse( |
197 |
self,
|
198 |
query, |
199 |
exactly_one=True,
|
200 |
timeout=None,
|
201 |
language=False,
|
202 |
): # pylint: disable=W0221
|
203 |
"""
|
204 |
Returns a reverse geocoded location.
|
205 |
|
206 |
:param query: The coordinates for which you wish to obtain the
|
207 |
closest human-readable addresses.
|
208 |
:type query: :class:`geopy.point.Point`, list or tuple of (latitude,
|
209 |
longitude), or string as "%(latitude)s, %(longitude)s"
|
210 |
|
211 |
:param bool exactly_one: Return one result or a list of results, if
|
212 |
available.
|
213 |
|
214 |
:param int timeout: Time, in seconds, to wait for the geocoding service
|
215 |
to respond before raising a :class:`geopy.exc.GeocoderTimedOut`
|
216 |
exception. Set this only if you wish to override, on this call
|
217 |
only, the value set during the geocoder's initialization.
|
218 |
|
219 |
.. versionadded:: 0.97
|
220 |
|
221 |
:param string language: Preferred language in which to return results.
|
222 |
Either uses standard
|
223 |
`RFC2616 <http://www.ietf.org/rfc/rfc2616.txt>`_
|
224 |
accept-language string or a simple comma-separated
|
225 |
list of language codes.
|
226 |
:type addressdetails: string
|
227 |
|
228 |
.. versionadded:: 1.0.0
|
229 |
|
230 |
"""
|
231 |
try:
|
232 |
lat, lon = [ |
233 |
x.strip() for x in |
234 |
self._coerce_point_to_string(query).split(',') |
235 |
] # doh
|
236 |
except ValueError: |
237 |
raise ValueError("Must be a coordinate pair or Point") |
238 |
params = { |
239 |
'lat': lat,
|
240 |
'lon': lon,
|
241 |
'format': 'json', |
242 |
} |
243 |
if language:
|
244 |
params['accept-language'] = language
|
245 |
url = "?".join((self.reverse_api, urlencode(params))) |
246 |
logger.debug("%s.reverse: %s", self.__class__.__name__, url) |
247 |
return self._parse_json( |
248 |
self._call_geocoder(url, timeout=timeout), exactly_one
|
249 |
) |
250 |
|
251 |
@staticmethod
|
252 |
def parse_code(place): |
253 |
"""
|
254 |
Parse each resource.
|
255 |
"""
|
256 |
latitude = place.get('lat', None) |
257 |
longitude = place.get('lon', None) |
258 |
placename = place.get('display_name', None) |
259 |
if latitude and longitude: |
260 |
latitude = float(latitude)
|
261 |
longitude = float(longitude)
|
262 |
return Location(placename, (latitude, longitude), place)
|
263 |
|
264 |
def _parse_json(self, places, exactly_one): |
265 |
if places is None: |
266 |
return None |
267 |
if not isinstance(places, list): |
268 |
places = [places] |
269 |
if not len(places): |
270 |
return None |
271 |
if exactly_one is True: |
272 |
return self.parse_code(places[0]) |
273 |
else:
|
274 |
return [self.parse_code(place) for place in places] |