Statistics
| Revision:

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)