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 / photon.py @ 564
History | View | Annotate | Download (8.19 KB)
1 |
"""
|
---|---|
2 |
:class:`.Photon` geocoder.
|
3 |
"""
|
4 |
|
5 |
from geopy.compat import urlencode, string_compare |
6 |
from geopy.geocoders.base import ( |
7 |
Geocoder, |
8 |
DEFAULT_FORMAT_STRING, |
9 |
DEFAULT_TIMEOUT, |
10 |
DEFAULT_SCHEME |
11 |
) |
12 |
from geopy.location import Location |
13 |
from geopy.util import logger |
14 |
|
15 |
|
16 |
__all__ = ("Photon", )
|
17 |
|
18 |
|
19 |
class Photon(Geocoder): # pylint: disable=W0223 |
20 |
"""
|
21 |
Geocoder using Photon geocoding service (data based on OpenStreetMap and
|
22 |
service provided by Komoot on https://photon.komoot.de).
|
23 |
Documentation at https://github.com/komoot/photon
|
24 |
"""
|
25 |
|
26 |
def __init__( |
27 |
self,
|
28 |
format_string=DEFAULT_FORMAT_STRING, |
29 |
scheme=DEFAULT_SCHEME, |
30 |
timeout=DEFAULT_TIMEOUT, |
31 |
proxies=None,
|
32 |
domain='photon.komoot.de'
|
33 |
): # pylint: disable=R0913
|
34 |
"""
|
35 |
Initialize a Photon/Komoot geocoder which aims to let you "search as
|
36 |
you type with OpenStreetMap". No API Key is needed by this platform.
|
37 |
|
38 |
:param string format_string: String containing '%s' where
|
39 |
the string to geocode should be interpolated before querying
|
40 |
the geocoder. For example: '%s, Mountain View, CA'. The default
|
41 |
is just '%s'.
|
42 |
|
43 |
:param string scheme: Use 'https' or 'http' as the API URL's scheme.
|
44 |
Default is https. Note that SSL connections' certificates are not
|
45 |
verified.
|
46 |
|
47 |
:param int timeout: Time, in seconds, to wait for the geocoding service
|
48 |
to respond before raising a :class:`geopy.exc.GeocoderTimedOut`
|
49 |
exception.
|
50 |
|
51 |
:param dict proxies: If specified, routes this geocoder's requests
|
52 |
through the specified proxy. E.g., {"https": "192.0.2.0"}. For
|
53 |
more information, see documentation on
|
54 |
:class:`urllib2.ProxyHandler`.
|
55 |
|
56 |
:param string domain: Should be the localized Photon domain to
|
57 |
connect to. The default is 'photon.komoot.de', but you
|
58 |
can change it to a domain of your own.
|
59 |
"""
|
60 |
super(Photon, self).__init__( |
61 |
format_string, scheme, timeout, proxies |
62 |
) |
63 |
self.domain = domain.strip('/') |
64 |
self.api = "%s://%s/api" % (self.scheme, self.domain) |
65 |
self.reverse_api = "%s://%s/reverse" % (self.scheme, self.domain) |
66 |
|
67 |
def geocode( |
68 |
self,
|
69 |
query, |
70 |
exactly_one=True,
|
71 |
timeout=None,
|
72 |
location_bias=None,
|
73 |
language=False,
|
74 |
osm_tag=None
|
75 |
): # pylint: disable=W0221
|
76 |
"""
|
77 |
Geocode a location query.
|
78 |
|
79 |
:param string query: The address or query you wish to geocode.
|
80 |
|
81 |
:param bool exactly_one: Return one result or a list of results, if
|
82 |
available.
|
83 |
|
84 |
:param int timeout: Time, in seconds, to wait for the geocoding service
|
85 |
to respond before raising a :class:`geopy.exc.GeocoderTimedOut`
|
86 |
exception. Set this only if you wish to override, on this call
|
87 |
only, the value set during the geocoder's initialization.
|
88 |
|
89 |
:param location_bias: The coordinates to used as location bias.
|
90 |
:type query: :class:`geopy.point.Point`, list or tuple of (latitude,
|
91 |
longitude), or string as "%(latitude)s, %(longitude)s"
|
92 |
|
93 |
:param string language: Preferred language in which to return results.
|
94 |
|
95 |
:param osm_tag: The expression to filter (include/exclude) by key and/
|
96 |
or value, str as 'key:value' or list/set of str if multiple filters
|
97 |
are requiered as ['key:!val', '!key', ':!value']
|
98 |
|
99 |
"""
|
100 |
params = { |
101 |
'q': self.format_string % query |
102 |
} |
103 |
if exactly_one:
|
104 |
params['limit'] = 1 |
105 |
if language:
|
106 |
params['lang'] = language
|
107 |
if location_bias:
|
108 |
try:
|
109 |
lat, lon = [x.strip() for x
|
110 |
in self._coerce_point_to_string(location_bias) |
111 |
.split(',')]
|
112 |
params['lon'] = lon
|
113 |
params['lat'] = lat
|
114 |
except ValueError: |
115 |
raise ValueError(("Location bias must be a" |
116 |
" coordinate pair or Point"))
|
117 |
if osm_tag:
|
118 |
if isinstance(osm_tag, string_compare): |
119 |
params['osm_tag'] = osm_tag
|
120 |
else:
|
121 |
try:
|
122 |
params['osm_tag'] = '&osm_tag='.join(osm_tag) |
123 |
except ValueError: |
124 |
raise ValueError( |
125 |
"osm_tag must be a string expression or "
|
126 |
"a set/list of string expressions"
|
127 |
) |
128 |
url = "?".join((self.api, urlencode(params))) |
129 |
|
130 |
logger.debug("%s.geocode: %s", self.__class__.__name__, url) |
131 |
return self._parse_json( |
132 |
self._call_geocoder(url, timeout=timeout),
|
133 |
exactly_one |
134 |
) |
135 |
|
136 |
def reverse( |
137 |
self,
|
138 |
query, |
139 |
exactly_one=True,
|
140 |
timeout=None,
|
141 |
language=False,
|
142 |
osm_tag=None
|
143 |
): # pylint: disable=W0221
|
144 |
"""
|
145 |
Returns a reverse geocoded location.
|
146 |
|
147 |
:param query: The coordinates for which you wish to obtain the
|
148 |
closest human-readable addresses.
|
149 |
:type query: :class:`geopy.point.Point`, list or tuple of (latitude,
|
150 |
longitude), or string as "%(latitude)s, %(longitude)s"
|
151 |
|
152 |
:param bool exactly_one: Return one result or a list of results, if
|
153 |
available.
|
154 |
|
155 |
:param int timeout: Time, in seconds, to wait for the geocoding service
|
156 |
to respond before raising a :class:`geopy.exc.GeocoderTimedOut`
|
157 |
exception. Set this only if you wish to override, on this call
|
158 |
only, the value set during the geocoder's initialization.
|
159 |
|
160 |
:param string language: Preferred language in which to return results.
|
161 |
|
162 |
:param osm_tag: The expression to filter (include/exclude) by key and/
|
163 |
or value, str as 'key:value' or list/set of str if multiple filters
|
164 |
are requiered as ['key:!val', '!key', ':!value']
|
165 |
"""
|
166 |
try:
|
167 |
lat, lon = [x.strip() for x in |
168 |
self._coerce_point_to_string(query).split(',')] |
169 |
except ValueError: |
170 |
raise ValueError("Must be a coordinate pair or Point") |
171 |
params = { |
172 |
'lat': lat,
|
173 |
'lon': lon,
|
174 |
} |
175 |
if exactly_one:
|
176 |
params['limit'] = 1 |
177 |
if language:
|
178 |
params['lang'] = language
|
179 |
if osm_tag:
|
180 |
if isinstance(osm_tag, string_compare): |
181 |
params['osm_tag'] = osm_tag
|
182 |
else:
|
183 |
try:
|
184 |
params['osm_tag'] = '&osm_tag='.join(osm_tag) |
185 |
except ValueError: |
186 |
raise ValueError(("osm_tag must be a string expression or " |
187 |
"a set/list of string expressions"))
|
188 |
url = "?".join((self.reverse_api, urlencode(params))) |
189 |
logger.debug("%s.reverse: %s", self.__class__.__name__, url) |
190 |
return self._parse_json( |
191 |
self._call_geocoder(url, timeout=timeout), exactly_one
|
192 |
) |
193 |
|
194 |
@classmethod
|
195 |
def _parse_json(cls, resources, exactly_one=True): |
196 |
"""
|
197 |
Parse display name, latitude, and longitude from a JSON response.
|
198 |
"""
|
199 |
if not len(resources): # pragma: no cover |
200 |
return None |
201 |
if exactly_one:
|
202 |
return cls.parse_resource(resources['features'][0]) |
203 |
else:
|
204 |
return [cls.parse_resource(resource) for resource |
205 |
in resources['features']] |
206 |
|
207 |
@classmethod
|
208 |
def parse_resource(cls, resource): |
209 |
"""
|
210 |
Return location and coordinates tuple from dict.
|
211 |
"""
|
212 |
name_elements = ['name', 'housenumber', 'street', |
213 |
'postcode', 'street', 'city', |
214 |
'state', 'country'] |
215 |
name = [resource.get(k) for k
|
216 |
in name_elements if resource.get(k)] |
217 |
location = ', '.join(name)
|
218 |
|
219 |
latitude = resource['geometry']['coordinates'][1] or None |
220 |
longitude = resource['geometry']['coordinates'][0] or None |
221 |
if latitude and longitude: |
222 |
latitude = float(latitude)
|
223 |
longitude = float(longitude)
|
224 |
|
225 |
return Location(location, (latitude, longitude), resource)
|