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 / 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.')