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 / geocodefarm.py @ 564

History | View | Annotate | Download (6.1 KB)

1
"""
2
:class:`.GeocodeFarm` geocoder.
3
"""
4

    
5
from geopy.geocoders.base import Geocoder, DEFAULT_FORMAT_STRING, \
6
    DEFAULT_TIMEOUT
7
from geopy.location import Location
8
from geopy.util import logger
9
from geopy.exc import GeocoderAuthenticationFailure, GeocoderQuotaExceeded, \
10
    GeocoderServiceError
11
from geopy.compat import urlencode
12

    
13

    
14
__all__ = ("GeocodeFarm", )
15

    
16

    
17
class GeocodeFarm(Geocoder):
18
    """
19
    Geocoder using the GeocodeFarm API. Documentation at:
20
        https://www.geocode.farm/geocoding/free-api-documentation/
21
    """
22

    
23
    def __init__(
24
            self,
25
            api_key=None,
26
            format_string=DEFAULT_FORMAT_STRING,
27
            timeout=DEFAULT_TIMEOUT,
28
            proxies=None,
29
            user_agent=None,
30
        ):  # pylint: disable=R0913
31
        """
32
        Create a geocoder for GeocodeFarm.
33

34
            .. versionadded:: 0.99
35

36
        :param string api_key: The API key required by GeocodeFarm to perform
37
            geocoding requests.
38

39
        :param string format_string: String containing '%s' where the
40
            string to geocode should be interpolated before querying the
41
            geocoder. For example: '%s, Mountain View, CA'. The default
42
            is just '%s'.
43

44
        :param dict proxies: If specified, routes this geocoder's requests
45
            through the specified proxy. E.g., {"https": "192.0.2.0"}. For
46
            more information, see documentation on
47
            :class:`urllib2.ProxyHandler`.
48
        """
49
        super(GeocodeFarm, self).__init__(
50
            format_string, 'https', timeout, proxies, user_agent=user_agent
51
        )
52
        self.api_key = api_key
53
        self.format_string = format_string
54
        self.api = (
55
            "%s://www.geocode.farm/v3/json/forward/" % self.scheme
56
        )
57
        self.reverse_api = (
58
            "%s://www.geocode.farm/v3/json/reverse/" % self.scheme
59
        )
60

    
61
    def geocode(self, query, exactly_one=True, timeout=None):
62
        """
63
        Geocode a location query.
64

65
        :param string query: The address or query you wish to geocode.
66

67
        :param bool exactly_one: Return one result or a list of results, if
68
            available.
69

70
        :param int timeout: Time, in seconds, to wait for the geocoding service
71
            to respond before raising a :class:`geopy.exc.GeocoderTimedOut`
72
            exception. Set this only if you wish to override, on this call
73
            only, the value set during the geocoder's initialization.
74
        """
75
        params = {
76
            'addr': self.format_string % query,
77
        }
78
        if self.api_key:
79
            params['key'] = self.api_key
80
        url = "?".join((self.api, urlencode(params)))
81
        logger.debug("%s.geocode: %s", self.__class__.__name__, url)
82
        return self._parse_json(
83
            self._call_geocoder(url, timeout=timeout), exactly_one
84
        )
85

    
86
    def reverse(self, query, exactly_one=True, timeout=None):
87
        """
88
        Returns a reverse geocoded location.
89

90
        :param query: The coordinates for which you wish to obtain the
91
            closest human-readable addresses.
92
        :type query: :class:`geopy.point.Point`, list or tuple of (latitude,
93
            longitude), or string as "%(latitude)s, %(longitude)s"
94

95
        :param bool exactly_one: Return one result or a list of results, if
96
            available. GeocodeFarm's API will always return at most one
97
            result.
98

99
        :param int timeout: Time, in seconds, to wait for the geocoding service
100
            to respond before raising a :class:`geopy.exc.GeocoderTimedOut`
101
            exception. Set this only if you wish to override, on this call
102
            only, the value set during the geocoder's initialization.
103
        """
104
        try:
105
            lat, lon = [
106
                x.strip() for x in
107
                self._coerce_point_to_string(query).split(',')
108
            ]
109
        except ValueError:
110
            raise ValueError("Must be a coordinate pair or Point")
111
        params = {
112
            'lat': lat,
113
            'lon': lon
114
        }
115
        if self.api_key:
116
            params['key'] = self.api_key
117
        url = "?".join((self.reverse_api, urlencode(params)))
118
        logger.debug("%s.reverse: %s", self.__class__.__name__, url)
119
        return self._parse_json(
120
            self._call_geocoder(url, timeout=timeout), exactly_one
121
        )
122

    
123
    @staticmethod
124
    def parse_code(results):
125
        """
126
        Parse each resource.
127
        """
128
        places = []
129
        for result in results.get('RESULTS'):
130
            coordinates = result.get('COORDINATES', {})
131
            address = result.get('ADDRESS', {})
132
            latitude = coordinates.get('latitude', None)
133
            longitude = coordinates.get('longitude', None)
134
            placename = address.get('address_returned', None)
135
            if placename is None:
136
                placename = address.get('address', None)
137
            if latitude and longitude:
138
                latitude = float(latitude)
139
                longitude = float(longitude)
140
            places.append(Location(placename, (latitude, longitude), result))
141
        return places
142

    
143
    def _parse_json(self, api_result, exactly_one):
144
        if api_result is None:
145
            return None
146
        geocoding_results = api_result["geocoding_results"]
147
        self._check_for_api_errors(geocoding_results)
148

    
149
        places = self.parse_code(geocoding_results)
150
        if exactly_one is True:
151
            return places[0]
152
        else:
153
            return places
154

    
155
    @staticmethod
156
    def _check_for_api_errors(geocoding_results):
157
        """
158
        Raise any exceptions if there were problems reported
159
        in the api response.
160
        """
161
        status_result = geocoding_results.get("STATUS", {})
162
        api_call_success = status_result.get("status", "") == "SUCCESS"
163
        if not api_call_success:
164
            access_error = status_result.get("access")
165
            access_error_to_exception = {
166
                'API_KEY_INVALID': GeocoderAuthenticationFailure,
167
                'OVER_QUERY_LIMIT': GeocoderQuotaExceeded,
168
            }
169
            exception_cls = access_error_to_exception.get(
170
                access_error, GeocoderServiceError
171
            )
172
            raise exception_cls(access_error)