geopy-1.11.0/0000775000175000017500000000000012571061653012303 5ustar ijlijl00000000000000geopy-1.11.0/geopy/0000775000175000017500000000000012571061653013426 5ustar ijlijl00000000000000geopy-1.11.0/geopy/geocoders/0000775000175000017500000000000012571061653015400 5ustar ijlijl00000000000000geopy-1.11.0/geopy/geocoders/__init__.py0000664000175000017500000001267212571054217017517 0ustar ijlijl00000000000000""" Each geolocation service you might use, such as Google Maps, Bing Maps, or Yahoo BOSS, has its own class in ``geopy.geocoders`` abstracting the service's API. Geocoders each define at least a ``geocode`` method, for resolving a location from a string, and may define a ``reverse`` method, which resolves a pair of coordinates to an address. Each Geocoder accepts any credentials or settings needed to interact with its service, e.g., an API key or locale, during its initialization. To geolocate a query to an address and coordinates: >>> from geopy.geocoders import Nominatim >>> geolocator = Nominatim() >>> location = geolocator.geocode("175 5th Avenue NYC") >>> print(location.address) Flatiron Building, 175, 5th Avenue, Flatiron, New York, NYC, New York, ... >>> print((location.latitude, location.longitude)) (40.7410861, -73.9896297241625) >>> print(location.raw) {'place_id': '9167009604', 'type': 'attraction', ...} To find the address corresponding to a set of coordinates: >>> from geopy.geocoders import Nominatim >>> geolocator = Nominatim() >>> location = geolocator.reverse("52.509669, 13.376294") >>> print(location.address) Potsdamer Platz, Mitte, Berlin, 10117, Deutschland, European Union >>> print((location.latitude, location.longitude)) (52.5094982, 13.3765983) >>> print(location.raw) {'place_id': '654513', 'osm_type': 'node', ...} Locators' ``geolocate`` and ``reverse`` methods require the argument ``query``, and also accept at least the argument ``exactly_one``, which is ``True``. Geocoders may have additional attributes, e.g., Bing accepts ``user_location``, the effect of which is to bias results near that location. ``geolocate`` and ``reverse`` methods may return three types of values: - When there are no results found, returns ``None``. - When the method's ``exactly_one`` argument is ``True`` and at least one result is found, returns a :class:`geopy.location.Location` object, which can be iterated over as: (address, (latitude, longitude)) Or can be accessed as `Location.address`, `Location.latitude`, `Location.longitude`, `Location.altitude`, and `Location.raw`. The last contains the geocoder's unparsed response for this result. - When ``exactly_one`` is False, and there is at least one result, returns a list of :class:`geopy.location.Location` objects, as above: [Location, [...]] If a service is unavailable or otherwise returns a non-OK response, or doesn't receive a response in the allotted timeout, you will receive one of the `Exceptions`_ detailed below. Every geocoder accepts an argument ``format_string`` that defaults to '%s' where the input string to geocode is interpolated. For example, if you only need to geocode locations in Cleveland, Ohio, you could do:: >>> from geopy.geocoders import GeocoderDotUS >>> geolocator = GeocoderDotUS(format_string="%s, Cleveland OH") >>> address, (latitude, longitude) = geolocator.geocode("11111 Euclid Ave") >>> print(address, latitude, longitude) 11111 Euclid Ave, Cleveland, OH 44106 41.506784 -81.608148 """ __all__ = ( "get_geocoder_for_service", "ArcGIS", "Baidu", "Bing", "DataBC", "GeocoderDotUS", "GeocodeFarm", "GeoNames", "GoogleV3", "IGNFrance", "OpenCage", "OpenMapQuest", "NaviData", "Nominatim", "YahooPlaceFinder", "LiveAddress", 'Yandex', "What3Words", "Photon", ) from geopy.geocoders.arcgis import ArcGIS from geopy.geocoders.baidu import Baidu from geopy.geocoders.bing import Bing from geopy.geocoders.databc import DataBC from geopy.geocoders.dot_us import GeocoderDotUS from geopy.geocoders.geocodefarm import GeocodeFarm from geopy.geocoders.geonames import GeoNames from geopy.geocoders.googlev3 import GoogleV3 from geopy.geocoders.opencage import OpenCage from geopy.geocoders.openmapquest import OpenMapQuest from geopy.geocoders.navidata import NaviData from geopy.geocoders.osm import Nominatim from geopy.geocoders.placefinder import YahooPlaceFinder from geopy.geocoders.smartystreets import LiveAddress from geopy.geocoders.what3words import What3Words from geopy.geocoders.yandex import Yandex from geopy.geocoders.ignfrance import IGNFrance from geopy.geocoders.photon import Photon from geopy.exc import GeocoderNotFound SERVICE_TO_GEOCODER = { "arcgis": ArcGIS, "baidu": Baidu, "bing": Bing, "databc": DataBC, "google": GoogleV3, "googlev3": GoogleV3, "geocoderdotus": GeocoderDotUS, "geonames": GeoNames, "yahoo": YahooPlaceFinder, "placefinder": YahooPlaceFinder, "opencage": OpenCage, "openmapquest": OpenMapQuest, "liveaddress": LiveAddress, "navidata": NaviData, "nominatim": Nominatim, "geocodefarm": GeocodeFarm, "what3words": What3Words, "yandex": Yandex, "ignfrance": IGNFrance, "photon": Photon } def get_geocoder_for_service(service): """ For the service provided, try to return a geocoder class. >>> from geopy.geocoders import get_geocoder_for_service >>> get_geocoder_for_service("nominatim") geopy.geocoders.osm.Nominatim If the string given is not recognized, a :class:`geopy.exc.GeocoderNotFound` exception is raised. """ try: return SERVICE_TO_GEOCODER[service.lower()] except KeyError: raise GeocoderNotFound( "Unknown geocoder '%s'; options are: %s" % (service, SERVICE_TO_GEOCODER.keys()) ) geopy-1.11.0/geopy/geocoders/arcgis.py0000664000175000017500000002275712571061561017235 0ustar ijlijl00000000000000""" :class:`.ArcGIS` geocoder. """ import json from time import time from geopy.compat import urlencode, Request from geopy.geocoders.base import Geocoder, DEFAULT_SCHEME, DEFAULT_TIMEOUT, \ DEFAULT_WKID from geopy.exc import GeocoderServiceError, GeocoderAuthenticationFailure from geopy.exc import ConfigurationError from geopy.location import Location from geopy.util import logger __all__ = ("ArcGIS", ) class ArcGIS(Geocoder): # pylint: disable=R0921,R0902,W0223 """ Geocoder using the ERSI ArcGIS API. Documentation at: https://developers.arcgis.com/rest/geocode/api-reference/overview-world-geocoding-service.htm """ _TOKEN_EXPIRED = 498 _MAX_RETRIES = 3 auth_api = 'https://www.arcgis.com/sharing/generateToken' def __init__(self, username=None, password=None, referer=None, # pylint: disable=R0913 token_lifetime=60, scheme=DEFAULT_SCHEME, timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None): """ Create a ArcGIS-based geocoder. .. versionadded:: 0.97 :param string username: ArcGIS username. Required if authenticated mode is desired. :param string password: ArcGIS password. Required if authenticated mode is desired. :param string referer: Required if authenticated mode is desired. 'Referer' HTTP header to send with each request, e.g., 'http://www.example.com'. This is tied to an issued token, so fielding queries for multiple referrers should be handled by having multiple ArcGIS geocoder instances. :param int token_lifetime: Desired lifetime, in minutes, of an ArcGIS-issued token. :param string scheme: Desired scheme. If authenticated mode is in use, it must be 'https'. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. """ super(ArcGIS, self).__init__( scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent ) if username or password or referer: if not (username and password and referer): raise ConfigurationError( "Authenticated mode requires username," " password, and referer" ) if self.scheme != 'https': raise ConfigurationError( "Authenticated mode requires scheme of 'https'" ) self._base_call_geocoder = self._call_geocoder self._call_geocoder = self._authenticated_call_geocoder self.username = username self.password = password self.referer = referer self.token = None self.token_lifetime = token_lifetime * 60 # store in seconds self.token_expiry = None self.retry = 1 self.api = ( '%s://geocode.arcgis.com/arcgis/rest/services/' 'World/GeocodeServer/find' % self.scheme ) self.reverse_api = ( '%s://geocode.arcgis.com/arcgis/rest/services/' 'World/GeocodeServer/reverseGeocode' % self.scheme ) def _authenticated_call_geocoder(self, url, timeout=None): """ Wrap self._call_geocoder, handling tokens. """ if self.token is None or int(time()) > self.token_expiry: self._refresh_authentication_token() request = Request( "&token=".join((url, self.token)), # no urlencoding headers={"Referer": self.referer} ) return self._base_call_geocoder(request, timeout=timeout) def geocode(self, query, exactly_one=True, timeout=None): """ Geocode a location query. :param string query: The address or query you wish to geocode. :param bool exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. """ params = {'text': query, 'f': 'json'} if exactly_one is True: params['maxLocations'] = 1 url = "?".join((self.api, urlencode(params))) logger.debug("%s.geocode: %s", self.__class__.__name__, url) response = self._call_geocoder(url, timeout=timeout) # Handle any errors; recursing in the case of an expired token. if 'error' in response: if response['error']['code'] == self._TOKEN_EXPIRED: self.retry += 1 self._refresh_authentication_token() return self.geocode( query, exactly_one=exactly_one, timeout=timeout ) raise GeocoderServiceError(str(response['error'])) # Success; convert from the ArcGIS JSON format. if not len(response['locations']): return None geocoded = [] for resource in response['locations']: geometry = resource['feature']['geometry'] geocoded.append( Location( resource['name'], (geometry['y'], geometry['x']), resource ) ) if exactly_one is True: return geocoded[0] return geocoded def reverse(self, query, exactly_one=True, timeout=None, # pylint: disable=R0913,W0221 distance=None, wkid=DEFAULT_WKID): """ Given a point, find an address. :param query: The coordinates for which you wish to obtain the closest human-readable addresses. :type query: :class:`geopy.point.Point`, list or tuple of (latitude, longitude), or string as "%(latitude)s, %(longitude)s". :param bool exactly_one: Return one result, or a list? :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. :param int distance: Distance from the query location, in meters, within which to search. ArcGIS has a default of 100 meters, if not specified. :param string wkid: WKID to use for both input and output coordinates. """ # ArcGIS is lon,lat; maintain lat,lon convention of geopy point = self._coerce_point_to_string(query).split(",") if wkid != DEFAULT_WKID: location = {"x": point[1], "y": point[0], "spatialReference": wkid} else: location = ",".join((point[1], point[0])) params = {'location': location, 'f': 'json', 'outSR': wkid} if distance is not None: params['distance'] = distance url = "?".join((self.reverse_api, urlencode(params))) logger.debug("%s.reverse: %s", self.__class__.__name__, url) response = self._call_geocoder(url, timeout=timeout) if not len(response): return None if 'error' in response: if response['error']['code'] == self._TOKEN_EXPIRED: self.retry += 1 self._refresh_authentication_token() return self.reverse(query, exactly_one=exactly_one, timeout=timeout, distance=distance, wkid=wkid) raise GeocoderServiceError(str(response['error'])) address = ( "%(Address)s, %(City)s, %(Region)s %(Postal)s," " %(CountryCode)s" % response['address'] ) return Location( address, (response['location']['y'], response['location']['x']), response['address'] ) def _refresh_authentication_token(self): """ POST to ArcGIS requesting a new token. """ if self.retry == self._MAX_RETRIES: raise GeocoderAuthenticationFailure( 'Too many retries for auth: %s' % self.retry ) token_request_arguments = { 'username': self.username, 'password': self.password, 'expiration': self.token_lifetime, 'f': 'json' } token_request_arguments = "&".join([ "%s=%s" % (key, val) for key, val in token_request_arguments.items() ]) url = "&".join(( "?".join((self.auth_api, token_request_arguments)), urlencode({'referer': self.referer}) )) logger.debug( "%s._refresh_authentication_token: %s", self.__class__.__name__, url ) self.token_expiry = int(time()) + self.token_lifetime response = self._base_call_geocoder(url) if not 'token' in response: raise GeocoderAuthenticationFailure( 'Missing token in auth request.' 'Request URL: %s; response JSON: %s' % (url, json.dumps(response)) ) self.retry = 0 self.token = response['token'] geopy-1.11.0/geopy/geocoders/baidu.py0000664000175000017500000001473012571061561017041 0ustar ijlijl00000000000000""" :class:`.Baidu` is the Baidu Maps geocoder. """ from geopy.compat import urlencode from geopy.geocoders.base import Geocoder, DEFAULT_TIMEOUT from geopy.exc import ( GeocoderQueryError, GeocoderQuotaExceeded, GeocoderAuthenticationFailure, ) from geopy.location import Location from geopy.util import logger __all__ = ("Baidu", ) class Baidu(Geocoder): """ Geocoder using the Baidu Maps v2 API. Documentation at: http://developer.baidu.com/map/webservice-geocoding.htm """ def __init__( self, api_key, scheme='http', timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None ): """ Initialize a customized Baidu geocoder using the v2 API. .. versionadded:: 1.0.0 :param string api_key: The API key required by Baidu Map to perform geocoding requests. API keys are managed through the Baidu APIs console (http://lbsyun.baidu.com/apiconsole/key). :param string scheme: Use 'https' or 'http' as the API URL's scheme. Default is http and only http support. :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. """ super(Baidu, self).__init__( scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent ) self.api_key = api_key self.scheme = scheme self.doc = {} self.api = 'http://api.map.baidu.com/geocoder/v2/' @staticmethod def _format_components_param(components): """ Format the components dict to something Baidu understands. """ return "|".join( (":".join(item) for item in components.items() ) ) def geocode( self, query, exactly_one=True, timeout=None ): """ Geocode a location query. :param string query: The address or query you wish to geocode. :param bool exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. """ params = { 'ak': self.api_key, 'output': 'json', 'address': self.format_string % query, } url = "?".join((self.api, urlencode(params))) logger.debug("%s.geocode: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one=exactly_one ) def reverse(self, query, timeout=None): # pylint: disable=W0221 """ Given a point, find an address. :param query: The coordinates for which you wish to obtain the closest human-readable addresses. :type query: :class:`geopy.point.Point`, list or tuple of (latitude, longitude), or string as "%(latitude)s, %(longitude)s" :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. """ params = { 'ak': self.api_key, 'output': 'json', 'location': self._coerce_point_to_string(query), } url = "?".join((self.api, urlencode(params))) logger.debug("%s.reverse: %s", self.__class__.__name__, url) return self._parse_reverse_json( self._call_geocoder(url, timeout=timeout) ) @staticmethod def _parse_reverse_json(page): """ Parses a location from a single-result reverse API call. """ place = page.get('result') location = place.get('formatted_address').encode('utf-8') latitude = place['location']['lat'] longitude = place['location']['lng'] return Location(location, (latitude, longitude), place) def _parse_json(self, page, exactly_one=True): """ Returns location, (latitude, longitude) from JSON feed. """ place = page.get('result', None) if not place: self._check_status(page.get('status')) return None def parse_place(place): """ Get the location, lat, lng from a single JSON place. """ location = place.get('level') latitude = place['location']['lat'] longitude = place['location']['lng'] return Location(location, (latitude, longitude), place) if exactly_one: return parse_place(place) else: return [parse_place(item) for item in place] @staticmethod def _check_status(status): """ Validates error statuses. """ if status == '0': # When there are no results, just return. return if status == '1': raise GeocoderQueryError( 'Internal server error.' ) elif status == '2': raise GeocoderQueryError( 'Invalid request.' ) elif status == '3': raise GeocoderAuthenticationFailure( 'Authentication failure.' ) elif status == '4': raise GeocoderQuotaExceeded( 'Quota validate failure.' ) elif status == '5': raise GeocoderQueryError( 'AK Illegal or Not Exist.' ) elif status == '101': raise GeocoderQueryError( 'Your request was denied.' ) elif status == '102': raise GeocoderQueryError( 'IP/SN/SCODE/REFERER Illegal:' ) elif status == '2xx': raise GeocoderQueryError( 'Has No Privilleges.' ) elif status == '3xx': raise GeocoderQuotaExceeded( 'Quota Error.' ) else: raise GeocoderQueryError('Unknown error') geopy-1.11.0/geopy/geocoders/base.py0000664000175000017500000001356112571061565016674 0ustar ijlijl00000000000000""" :class:`.GeoCoder` base object from which other geocoders are templated. """ from ssl import SSLError from socket import timeout as SocketTimeout import json from geopy.compat import ( string_compare, HTTPError, py3k, urlopen as urllib_urlopen, build_opener, ProxyHandler, URLError, install_opener, Request, ) from geopy.point import Point from geopy.exc import ( GeocoderServiceError, ConfigurationError, GeocoderTimedOut, GeocoderAuthenticationFailure, GeocoderQuotaExceeded, GeocoderQueryError, GeocoderInsufficientPrivileges, GeocoderUnavailable, GeocoderParseError, ) from geopy.util import decode_page, __version__ __all__ = ( "Geocoder", "DEFAULT_FORMAT_STRING", "DEFAULT_SCHEME", "DEFAULT_TIMEOUT", "DEFAULT_WKID", ) DEFAULT_FORMAT_STRING = '%s' DEFAULT_SCHEME = 'https' DEFAULT_TIMEOUT = 1 DEFAULT_WKID = 4326 DEFAULT_USER_AGENT = "geopy/%s" % __version__ ERROR_CODE_MAP = { 400: GeocoderQueryError, 401: GeocoderAuthenticationFailure, 402: GeocoderQuotaExceeded, 403: GeocoderInsufficientPrivileges, 407: GeocoderAuthenticationFailure, 412: GeocoderQueryError, 413: GeocoderQueryError, 414: GeocoderQueryError, 502: GeocoderServiceError, 503: GeocoderTimedOut, 504: GeocoderTimedOut } class Geocoder(object): # pylint: disable=R0921 """ Template object for geocoders. """ def __init__( self, format_string=DEFAULT_FORMAT_STRING, scheme=DEFAULT_SCHEME, timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None ): """ Mostly-common geocoder validation, proxies, &c. Not all geocoders specify format_string and such. """ self.format_string = format_string self.scheme = scheme if self.scheme not in ('http', 'https'): # pragma: no cover raise ConfigurationError( 'Supported schemes are `http` and `https`.' ) self.proxies = proxies self.timeout = timeout self.headers = {'User-Agent': user_agent or DEFAULT_USER_AGENT} if self.proxies: install_opener( build_opener( ProxyHandler(self.proxies) ) ) self.urlopen = urllib_urlopen @staticmethod def _coerce_point_to_string(point): """ Do the right thing on "point" input. For geocoders with reverse methods. """ if isinstance(point, Point): return ",".join((str(point.latitude), str(point.longitude))) elif isinstance(point, (list, tuple)): return ",".join((str(point[0]), str(point[1]))) # -altitude elif isinstance(point, string_compare): return point else: # pragma: no cover raise ValueError("Invalid point") def _parse_json(self, page, exactly_one): # pragma: no cover """ Template for subclasses """ raise NotImplementedError() def _call_geocoder( self, url, timeout=None, raw=False, requester=None, deserializer=json.loads, **kwargs ): """ For a generated query URL, get the results. """ requester = requester or self.urlopen if not requester: req = Request(url=url, headers=self.headers) else: # work around for placefinder's use of requests req = url try: page = requester(req, timeout=(timeout or self.timeout), **kwargs) except Exception as error: # pylint: disable=W0703 message = ( str(error) if not py3k else ( str(error.args[0]) if len(error.args) else str(error) ) ) if hasattr(self, '_geocoder_exception_handler'): self._geocoder_exception_handler(error, message) # pylint: disable=E1101 if isinstance(error, HTTPError): code = error.getcode() try: raise ERROR_CODE_MAP[code](message) except KeyError: raise GeocoderServiceError(message) elif isinstance(error, URLError): if "timed out" in message: raise GeocoderTimedOut('Service timed out') elif "unreachable" in message: raise GeocoderUnavailable('Service not available') elif isinstance(error, SocketTimeout): raise GeocoderTimedOut('Service timed out') elif isinstance(error, SSLError): if "timed out" in message: raise GeocoderTimedOut('Service timed out') raise GeocoderServiceError(message) if hasattr(page, 'getcode'): status_code = page.getcode() elif hasattr(page, 'status_code'): status_code = page.status_code else: status_code = None if status_code in ERROR_CODE_MAP: raise ERROR_CODE_MAP[page.status_code]("\n%s" % decode_page(page)) if raw: return page page = decode_page(page) if deserializer is not None: try: return deserializer(page) except ValueError: raise GeocoderParseError( "Could not deserialize using deserializer:\n%s" % page ) else: return page def geocode(self, query, exactly_one=True, timeout=None): """ Implemented in subclasses. """ raise NotImplementedError() def reverse(self, query, exactly_one=True, timeout=None): """ Implemented in subclasses. """ raise NotImplementedError() geopy-1.11.0/geopy/geocoders/bing.py0000664000175000017500000002054512571061561016675 0ustar ijlijl00000000000000""" :class:`.Bing` geocoder. """ from geopy.compat import urlencode from geopy.geocoders.base import Geocoder, DEFAULT_FORMAT_STRING, \ DEFAULT_TIMEOUT, DEFAULT_SCHEME from geopy.location import Location from geopy.exc import ( GeocoderAuthenticationFailure, GeocoderQuotaExceeded, GeocoderInsufficientPrivileges, GeocoderUnavailable, GeocoderServiceError, ) from geopy.util import logger, join_filter __all__ = ("Bing", ) class Bing(Geocoder): """ Geocoder using the Bing Maps Locations API. Documentation at: https://msdn.microsoft.com/en-us/library/ff701715.aspx """ structured_query_params = { 'addressLine', 'locality', 'adminDistrict', 'countryRegion', 'postalCode', } def __init__( self, api_key, format_string=DEFAULT_FORMAT_STRING, scheme=DEFAULT_SCHEME, timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None, ): # pylint: disable=R0913 """Initialize a customized Bing geocoder with location-specific address information and your Bing Maps API key. :param string api_key: Should be a valid Bing Maps API key. :param string format_string: String containing '%s' where the string to geocode should be interpolated before querying the geocoder. For example: '%s, Mountain View, CA'. The default is just '%s'. :param string scheme: Use 'https' or 'http' as the API URL's scheme. Default is https. Note that SSL connections' certificates are not verified. .. versionadded:: 0.97 :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. .. versionadded:: 0.97 :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. .. versionadded:: 0.96 """ super(Bing, self).__init__(format_string, scheme, timeout, proxies, user_agent=user_agent) self.api_key = api_key self.api = "%s://dev.virtualearth.net/REST/v1/Locations" % self.scheme def geocode( self, query, exactly_one=True, user_location=None, timeout=None, culture=None, include_neighborhood=None, include_country_code=False ): # pylint: disable=W0221 """ Geocode an address. :param string query: The address or query you wish to geocode. For a structured query, provide a dictionary whose keys are one of: `addressLine`, `locality` (city), `adminDistrict` (state), `countryRegion`, or `postalcode`. :param bool exactly_one: Return one result or a list of results, if available. :param user_location: Prioritize results closer to this location. .. versionadded:: 0.96 :type user_location: :class:`geopy.point.Point` :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. .. versionadded:: 0.97 :param string culture: Affects the language of the response, must be a two-letter country code. .. versionadded:: 1.4.0 :param boolean include_neighborhood: Sets whether to include the neighborhood field in the response. .. versionadded:: 1.4.0 :param boolean include_country_code: Sets whether to include the two-letter ISO code of the country in the response (field name 'countryRegionIso2'). .. versionadded:: 1.4.0 """ if isinstance(query, dict): params = { key: val for key, val in query.items() if key in self.structured_query_params } params['key'] = self.api_key else: params = { 'query': self.format_string % query, 'key': self.api_key } if user_location: params['userLocation'] = ",".join( (str(user_location.latitude), str(user_location.longitude)) ) if exactly_one is True: params['maxResults'] = 1 if culture: params['culture'] = culture if include_neighborhood is not None: params['includeNeighborhood'] = include_neighborhood if include_country_code: params['include'] = 'ciso2' # the only acceptable value url = "?".join((self.api, urlencode(params))) logger.debug("%s.geocode: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) def reverse(self, query, exactly_one=True, timeout=None): """ Reverse geocode a point. :param query: The coordinates for which you wish to obtain the closest human-readable addresses. :type query: :class:`geopy.point.Point`, list or tuple of (latitude, longitude), or string as "%(latitude)s, %(longitude)s". :param bool exactly_one: Return one result, or a list? :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. .. versionadded:: 0.97 """ point = self._coerce_point_to_string(query) params = {'key': self.api_key} url = "%s/%s?%s" % ( self.api, point, urlencode(params)) logger.debug("%s.reverse: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) @staticmethod def _parse_json(doc, exactly_one=True): # pylint: disable=W0221 """ Parse a location name, latitude, and longitude from an JSON response. """ status_code = doc.get("statusCode", 200) if status_code != 200: err = doc.get("errorDetails", "") if status_code == 401: raise GeocoderAuthenticationFailure(err) elif status_code == 403: raise GeocoderInsufficientPrivileges(err) elif status_code == 429: raise GeocoderQuotaExceeded(err) elif status_code == 503: raise GeocoderUnavailable(err) else: raise GeocoderServiceError(err) resources = doc['resourceSets'][0]['resources'] if resources is None or not len(resources): # pragma: no cover return None def parse_resource(resource): """ Parse each return object. """ stripchars = ", \n" addr = resource['address'] address = addr.get('addressLine', '').strip(stripchars) city = addr.get('locality', '').strip(stripchars) state = addr.get('adminDistrict', '').strip(stripchars) zipcode = addr.get('postalCode', '').strip(stripchars) country = addr.get('countryRegion', '').strip(stripchars) city_state = join_filter(", ", [city, state]) place = join_filter(" ", [city_state, zipcode]) location = join_filter(", ", [address, place, country]) latitude = resource['point']['coordinates'][0] or None longitude = resource['point']['coordinates'][1] or None if latitude and longitude: latitude = float(latitude) longitude = float(longitude) return Location(location, (latitude, longitude), resource) if exactly_one: return parse_resource(resources[0]) else: return [parse_resource(resource) for resource in resources] geopy-1.11.0/geopy/geocoders/databc.py0000664000175000017500000001021312571061561017163 0ustar ijlijl00000000000000""" :class:`.DataBC` geocoder. """ from geopy.compat import urlencode from geopy.geocoders.base import Geocoder, DEFAULT_SCHEME, DEFAULT_TIMEOUT from geopy.exc import GeocoderQueryError from geopy.location import Location from geopy.util import logger __all__ = ("DataBC", ) class DataBC(Geocoder): """ Geocoder using the Physical Address Geocoder from DataBC. Documentation at: http://www.data.gov.bc.ca/dbc/geographic/locate/geocoding.page """ def __init__(self, scheme=DEFAULT_SCHEME, timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None): """ Create a DataBC-based geocoder. :param string scheme: Desired scheme. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. """ super(DataBC, self).__init__( scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent ) self.api = '%s://apps.gov.bc.ca/pub/geocoder/addresses.geojson' % self.scheme def geocode( self, query, max_results=25, set_back=0, location_descriptor='any', exactly_one=True, timeout=None, ): """ Geocode a location query. :param string query: The address or query you wish to geocode. :param int max_results: The maximum number of resutls to request. :param float set_back: The distance to move the accessPoint away from the curb (in meters) and towards the interior of the parcel. location_descriptor must be set to accessPoint for set_back to take effect. :param string location_descriptor: The type of point requested. It can be any, accessPoint, frontDoorPoint, parcelPoint, rooftopPoint and routingPoint. :param bool exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. """ params = {'addressString': query} if set_back != 0: params['setBack'] = set_back if location_descriptor not in ['any', 'accessPoint', 'frontDoorPoint', 'parcelPoint', 'rooftopPoint', 'routingPoint']: raise GeocoderQueryError( "You did not provided a location_descriptor " "the webservice can consume. It should be any, accessPoint, " "frontDoorPoint, parcelPoint, rooftopPoint or routingPoint." ) params['locationDescriptor'] = location_descriptor if exactly_one is True: max_results = 1 params['maxResults'] = max_results url = "?".join((self.api, urlencode(params))) logger.debug("%s.geocode: %s", self.__class__.__name__, url) response = self._call_geocoder(url, timeout=timeout) # Success; convert from GeoJSON if not len(response['features']): return None geocoded = [] for feature in response['features']: geocoded.append(self._parse_feature(feature)) if exactly_one is True: return geocoded[0] return geocoded @staticmethod def _parse_feature(feature): properties = feature['properties'] coordinates = feature['geometry']['coordinates'] return Location( properties['fullAddress'], (coordinates[1], coordinates[0]), properties ) geopy-1.11.0/geopy/geocoders/dot_us.py0000664000175000017500000001225012571061561017245 0ustar ijlijl00000000000000""" :class:`GeocoderDotUS` geocoder. """ import csv from base64 import encodestring from geopy.compat import urlencode, py3k, Request from geopy.geocoders.base import ( Geocoder, DEFAULT_FORMAT_STRING, DEFAULT_TIMEOUT, ) from geopy.location import Location from geopy.exc import ConfigurationError from geopy.util import logger, join_filter __all__ = ("GeocoderDotUS", ) class GeocoderDotUS(Geocoder): # pylint: disable=W0223 """ GeocoderDotUS geocoder, documentation at: http://geocoder.us/ Note that GeocoderDotUS does not support SSL. """ def __init__( self, username=None, password=None, format_string=DEFAULT_FORMAT_STRING, timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None, ): # pylint: disable=R0913 """ :param string username: :param string password: :param string format_string: String containing '%s' where the string to geocode should be interpolated before querying the geocoder. For example: '%s, Mountain View, CA'. The default is just '%s'. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising an :class:`geopy.exc.GeocoderTimedOut` exception. .. versionadded:: 0.97 :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. .. versionadded:: 0.96 """ super(GeocoderDotUS, self).__init__( format_string=format_string, timeout=timeout, proxies=proxies, user_agent=user_agent ) if username or password: if not (username and password): raise ConfigurationError( "Username and password must both specified" ) self.authenticated = True self.api = "http://geocoder.us/member/service/namedcsv" else: self.authenticated = False self.api = "http://geocoder.us/service/namedcsv" self.username = username self.password = password def geocode(self, query, exactly_one=True, timeout=None): """ Geocode a location query. :param string query: The address or query you wish to geocode. :param bool exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. .. versionadded:: 0.97 """ query_str = self.format_string % query url = "?".join((self.api, urlencode({'address':query_str}))) logger.debug("%s.geocode: %s", self.__class__.__name__, url) if self.authenticated is True: auth = " ".join(( "Basic", encodestring(":".join((self.username, self.password))\ .encode('utf-8')).strip().decode('utf-8') )) url = Request(url, headers={"Authorization": auth}) page = self._call_geocoder(url, timeout=timeout, raw=True) content = page.read().decode("utf-8") if py3k else page.read() # pylint: disable=E1101,E1103 places = [ r for r in csv.reader( [content, ] if not isinstance(content, list) else content ) ] if not len(places): return None if exactly_one is True: return self._parse_result(places[0]) else: result = [self._parse_result(res) for res in places] if None in result: # todo return None return result @staticmethod def _parse_result(result): """ Parse individual results. Different, but lazy actually, so... ok. """ # turn x=y pairs ("lat=47.6", "long=-117.426") # into dict key/value pairs: place = dict( [x.split('=') for x in result if len(x.split('=')) > 1] ) if 'error' in place: if "couldn't find" in place['error']: return None address = [ place.get('number', None), place.get('prefix', None), place.get('street', None), place.get('type', None), place.get('suffix', None) ] city = place.get('city', None) state = place.get('state', None) zip_code = place.get('zip', None) name = join_filter(", ", [ join_filter(" ", address), city, join_filter(" ", [state, zip_code]) ]) latitude = place.get('lat', None) longitude = place.get('long', None) if latitude and longitude: latlon = float(latitude), float(longitude) else: return None return Location(name, latlon, place) geopy-1.11.0/geopy/geocoders/geocodefarm.py0000664000175000017500000001414312571061561020226 0ustar ijlijl00000000000000""" :class:`.GeocodeFarm` geocoder. """ from geopy.geocoders.base import Geocoder, DEFAULT_FORMAT_STRING, \ DEFAULT_TIMEOUT from geopy.location import Location from geopy.util import logger from geopy.exc import GeocoderAuthenticationFailure, GeocoderQuotaExceeded, \ GeocoderServiceError from geopy.compat import urlencode __all__ = ("GeocodeFarm", ) class GeocodeFarm(Geocoder): """ Geocoder using the GeocodeFarm API. Documentation at: https://www.geocode.farm/geocoding/free-api-documentation/ """ def __init__( self, api_key=None, format_string=DEFAULT_FORMAT_STRING, timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None, ): # pylint: disable=R0913 """ Create a geocoder for GeocodeFarm. .. versionadded:: 0.99 :param string api_key: The API key required by GeocodeFarm to perform geocoding requests. :param string format_string: String containing '%s' where the string to geocode should be interpolated before querying the geocoder. For example: '%s, Mountain View, CA'. The default is just '%s'. :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. """ super(GeocodeFarm, self).__init__( format_string, 'https', timeout, proxies, user_agent=user_agent ) self.api_key = api_key self.format_string = format_string self.api = ( "%s://www.geocode.farm/v3/json/forward/" % self.scheme ) self.reverse_api = ( "%s://www.geocode.farm/v3/json/reverse/" % self.scheme ) def geocode(self, query, exactly_one=True, timeout=None): """ Geocode a location query. :param string query: The address or query you wish to geocode. :param bool exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. """ params = { 'addr': self.format_string % query, } if self.api_key: params['key'] = self.api_key url = "?".join((self.api, urlencode(params))) logger.debug("%s.geocode: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) def reverse(self, query, exactly_one=True, timeout=None): """ Returns a reverse geocoded location. :param query: The coordinates for which you wish to obtain the closest human-readable addresses. :type query: :class:`geopy.point.Point`, list or tuple of (latitude, longitude), or string as "%(latitude)s, %(longitude)s" :param bool exactly_one: Return one result or a list of results, if available. GeocodeFarm's API will always return at most one result. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. """ try: lat, lon = [ x.strip() for x in self._coerce_point_to_string(query).split(',') ] except ValueError: raise ValueError("Must be a coordinate pair or Point") params = { 'lat': lat, 'lon': lon } if self.api_key: params['key'] = self.api_key url = "?".join((self.reverse_api, urlencode(params))) logger.debug("%s.reverse: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) @staticmethod def parse_code(results): """ Parse each resource. """ places = [] for result in results.get('RESULTS'): coordinates = result.get('COORDINATES', {}) address = result.get('ADDRESS', {}) latitude = coordinates.get('latitude', None) longitude = coordinates.get('longitude', None) placename = address.get('address_returned', None) if placename is None: placename = address.get('address', None) if latitude and longitude: latitude = float(latitude) longitude = float(longitude) places.append(Location(placename, (latitude, longitude), result)) return places def _parse_json(self, api_result, exactly_one): if api_result is None: return None geocoding_results = api_result["geocoding_results"] self._check_for_api_errors(geocoding_results) places = self.parse_code(geocoding_results) if exactly_one is True: return places[0] else: return places @staticmethod def _check_for_api_errors(geocoding_results): """ Raise any exceptions if there were problems reported in the api response. """ status_result = geocoding_results.get("STATUS", {}) api_call_success = status_result.get("status", "") == "SUCCESS" if not api_call_success: access_error = status_result.get("access") access_error_to_exception = { 'API_KEY_INVALID': GeocoderAuthenticationFailure, 'OVER_QUERY_LIMIT': GeocoderQuotaExceeded, } exception_cls = access_error_to_exception.get( access_error, GeocoderServiceError ) raise exception_cls(access_error) geopy-1.11.0/geopy/geocoders/geonames.py0000664000175000017500000001341612571061561017553 0ustar ijlijl00000000000000""" :class:`GeoNames` geocoder. """ from geopy.compat import urlencode from geopy.geocoders.base import Geocoder, DEFAULT_TIMEOUT from geopy.location import Location from geopy.exc import ( GeocoderInsufficientPrivileges, GeocoderServiceError, ConfigurationError ) from geopy.util import logger __all__ = ("GeoNames", ) class GeoNames(Geocoder): # pylint: disable=W0223 """ GeoNames geocoder, documentation at: http://www.geonames.org/export/geonames-search.html Reverse geocoding documentation at: http://www.geonames.org/maps/us-reverse-geocoder.html """ def __init__( self, country_bias=None, username=None, timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None, ): """ :param string country_bias: :param string username: :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. .. versionadded:: 0.97 :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. .. versionadded:: 0.96 """ super(GeoNames, self).__init__( scheme='http', timeout=timeout, proxies=proxies, user_agent=user_agent ) if username == None: raise ConfigurationError( 'No username given, required for api access. If you do not ' 'have a GeoNames username, sign up here: ' 'http://www.geonames.org/login' ) self.username = username self.country_bias = country_bias self.api = "%s://api.geonames.org/searchJSON" % self.scheme self.api_reverse = ( "%s://api.geonames.org/findNearbyPlaceNameJSON" % self.scheme ) def geocode(self, query, exactly_one=True, timeout=None): # pylint: disable=W0221 """ Geocode a location query. :param string query: The address or query you wish to geocode. :param bool exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. .. versionadded:: 0.97 """ params = { 'q': query, 'username': self.username } if self.country_bias: params['countryBias'] = self.country_bias if exactly_one is True: params['maxRows'] = 1 url = "?".join((self.api, urlencode(params))) logger.debug("%s.geocode: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one, ) def reverse( self, query, exactly_one=False, timeout=None, ): """ Given a point, find an address. .. versionadded:: 1.2.0 :param string query: The coordinates for which you wish to obtain the closest human-readable addresses. :type query: :class:`geopy.point.Point`, list or tuple of (latitude, longitude), or string as "%(latitude)s, %(longitude)s" :param boolean exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. """ try: lat, lng = [ x.strip() for x in self._coerce_point_to_string(query).split(',') ] except ValueError: raise ValueError("Must be a coordinate pair or Point") params = { 'lat': lat, 'lng': lng, 'username': self.username } url = "?".join((self.api_reverse, urlencode(params))) logger.debug("%s.reverse: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) def _parse_json(self, doc, exactly_one): """ Parse JSON response body. """ places = doc.get('geonames', []) err = doc.get('status', None) if err and 'message' in err: if err['message'].startswith("user account not enabled to use"): raise GeocoderInsufficientPrivileges(err['message']) else: raise GeocoderServiceError(err['message']) if not len(places): return None def parse_code(place): """ Parse each record. """ latitude = place.get('lat', None) longitude = place.get('lng', None) if latitude and longitude: latitude = float(latitude) longitude = float(longitude) else: return None placename = place.get('name') state = place.get('adminCode1', None) country = place.get('countryCode', None) location = ', '.join( [x for x in [placename, state, country] if x] ) return Location(location, (latitude, longitude), place) if exactly_one: return parse_code(places[0]) else: return [parse_code(place) for place in places] geopy-1.11.0/geopy/geocoders/googlev3.py0000664000175000017500000003072312571061561017502 0ustar ijlijl00000000000000""" :class:`.GoogleV3` is the Google Maps V3 geocoder. """ import base64 import hashlib import hmac from geopy.compat import urlencode from geopy.geocoders.base import Geocoder, DEFAULT_TIMEOUT, DEFAULT_SCHEME from geopy.exc import ( GeocoderQueryError, GeocoderQuotaExceeded, ConfigurationError, GeocoderParseError, GeocoderQueryError, ) from geopy.location import Location from geopy.util import logger try: from pytz import timezone, UnknownTimeZoneError from calendar import timegm from datetime import datetime from numbers import Number pytz_available = True except ImportError: pytz_available = False __all__ = ("GoogleV3", ) class GoogleV3(Geocoder): # pylint: disable=R0902 """ Geocoder using the Google Maps v3 API. Documentation at: https://developers.google.com/maps/documentation/geocoding/ """ def __init__( self, api_key=None, domain='maps.googleapis.com', scheme=DEFAULT_SCHEME, client_id=None, secret_key=None, timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None, ): # pylint: disable=R0913 """ Initialize a customized Google geocoder. API authentication is only required for Google Maps Premier customers. :param string api_key: The API key required by Google to perform geocoding requests. API keys are managed through the Google APIs console (https://code.google.com/apis/console). .. versionadded:: 0.98.2 :param string domain: Should be the localized Google Maps domain to connect to. The default is 'maps.googleapis.com', but if you're geocoding address in the UK (for example), you may want to set it to 'maps.google.co.uk' to properly bias results. :param string scheme: Use 'https' or 'http' as the API URL's scheme. Default is https. Note that SSL connections' certificates are not verified. .. versionadded:: 0.97 :param string client_id: If using premier, the account client id. :param string secret_key: If using premier, the account secret key. :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. .. versionadded:: 0.96 """ super(GoogleV3, self).__init__( scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent ) if client_id and not secret_key: raise ConfigurationError('Must provide secret_key with client_id.') if secret_key and not client_id: raise ConfigurationError('Must provide client_id with secret_key.') self.api_key = api_key self.domain = domain.strip('/') self.scheme = scheme self.doc = {} if client_id and secret_key: self.premier = True self.client_id = client_id self.secret_key = secret_key else: self.premier = False self.client_id = None self.secret_key = None self.api = '%s://%s/maps/api/geocode/json' % (self.scheme, self.domain) self.tz_api = '%s://%s/maps/api/timezone/json' % ( self.scheme, self.domain ) def _get_signed_url(self, params): """ Returns a Premier account signed url. Docs on signature: https://developers.google.com/maps/documentation/business/webservices/auth#digital_signatures """ params['client'] = self.client_id path = "?".join(('/maps/api/geocode/json', urlencode(params))) signature = hmac.new( base64.urlsafe_b64decode(self.secret_key), path.encode('utf-8'), hashlib.sha1 ) signature = base64.urlsafe_b64encode( signature.digest() ).decode('utf-8') return '%s://%s%s&signature=%s' % ( self.scheme, self.domain, path, signature ) @staticmethod def _format_components_param(components): """ Format the components dict to something Google understands. """ return "|".join( (":".join(item) for item in components.items() ) ) @staticmethod def _format_bounds_param(bounds): """ Format the bounds to something Google understands. """ return '%f,%f|%f,%f' % (bounds[0], bounds[1], bounds[2], bounds[3]) def geocode( self, query, exactly_one=True, timeout=None, bounds=None, region=None, components=None, language=None, sensor=False, ): # pylint: disable=W0221,R0913 """ Geocode a location query. :param string query: The address or query you wish to geocode. :param bool exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. .. versionadded:: 0.97 :param bounds: The bounding box of the viewport within which to bias geocode results more prominently. :type bounds: list or tuple :param string region: The region code, specified as a ccTLD ("top-level domain") two-character value. :param dict components: Restricts to an area. Can use any combination of: route, locality, administrative_area, postal_code, country. .. versionadded:: 0.97.1 :param string language: The language in which to return results. :param bool sensor: Whether the geocoding request comes from a device with a location sensor. """ params = { 'address': self.format_string % query, 'sensor': str(sensor).lower() } if self.api_key: params['key'] = self.api_key if bounds: if len(bounds) != 4: raise GeocoderQueryError( "bounds must be a four-item iterable of lat,lon,lat,lon" ) params['bounds'] = self._format_bounds_param(bounds) if region: params['region'] = region if components: params['components'] = self._format_components_param(components) if language: params['language'] = language if self.premier is False: url = "?".join((self.api, urlencode(params))) else: url = self._get_signed_url(params) logger.debug("%s.geocode: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) def reverse( self, query, exactly_one=False, timeout=None, language=None, sensor=False, ): # pylint: disable=W0221,R0913 """ Given a point, find an address. :param query: The coordinates for which you wish to obtain the closest human-readable addresses. :type query: :class:`geopy.point.Point`, list or tuple of (latitude, longitude), or string as "%(latitude)s, %(longitude)s" :param boolean exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. .. versionadded:: 0.97 :param string language: The language in which to return results. :param boolean sensor: Whether the geocoding request comes from a device with a location sensor. """ params = { 'latlng': self._coerce_point_to_string(query), 'sensor': str(sensor).lower() } if language: params['language'] = language if self.api_key: params['key'] = self.api_key if not self.premier: url = "?".join((self.api, urlencode(params))) else: url = self._get_signed_url(params) logger.debug("%s.reverse: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) def timezone(self, location, at_time=None, timeout=None): """ **This is an unstable API.** Finds the timezone a `location` was in for a specified `at_time`, and returns a pytz timezone object. .. versionadded:: 1.2.0 :param location: The coordinates for which you want a timezone. :type location: :class:`geopy.point.Point`, list or tuple of (latitude, longitude), or string as "%(latitude)s, %(longitude)s" :param at_time: The time at which you want the timezone of this location. This is optional, and defaults to the time that the function is called in UTC. :type at_time integer, long, float, datetime: :rtype: pytz timezone """ if not pytz_available: raise ImportError( 'pytz must be installed in order to locate timezones. ' ' Install with `pip install geopy -e ".[timezone]"`.' ) location = self._coerce_point_to_string(location) if isinstance(at_time, Number): timestamp = at_time elif isinstance(at_time, datetime): timestamp = timegm(at_time.utctimetuple()) elif at_time is None: timestamp = timegm(datetime.utcnow().utctimetuple()) else: raise GeocoderQueryError( "`at_time` must be an epoch integer or " "datetime.datetime object" ) params = { "location": location, "timestamp": timestamp, } if self.api_key: params['key'] = self.api_key url = "?".join((self.tz_api, urlencode(params))) logger.debug("%s.timezone: %s", self.__class__.__name__, url) response = self._call_geocoder(url, timeout=timeout) try: tz = timezone(response["timeZoneId"]) except UnknownTimeZoneError: raise GeocoderParseError( "pytz could not parse the timezone identifier (%s) " "returned by the service." % response["timeZoneId"] ) except KeyError: raise GeocoderParseError( "geopy could not find a timezone in this response: %s" % response ) return tz def _parse_json(self, page, exactly_one=True): '''Returns location, (latitude, longitude) from json feed.''' places = page.get('results', []) if not len(places): self._check_status(page.get('status')) return None def parse_place(place): '''Get the location, lat, lng from a single json place.''' location = place.get('formatted_address') latitude = place['geometry']['location']['lat'] longitude = place['geometry']['location']['lng'] return Location(location, (latitude, longitude), place) if exactly_one: return parse_place(places[0]) else: return [parse_place(place) for place in places] @staticmethod def _check_status(status): """ Validates error statuses. """ if status == 'ZERO_RESULTS': # When there are no results, just return. return if status == 'OVER_QUERY_LIMIT': raise GeocoderQuotaExceeded( 'The given key has gone over the requests limit in the 24' ' hour period or has submitted too many requests in too' ' short a period of time.' ) elif status == 'REQUEST_DENIED': raise GeocoderQueryError( 'Your request was denied.' ) elif status == 'INVALID_REQUEST': raise GeocoderQueryError('Probably missing address or latlng.') else: raise GeocoderQueryError('Unknown error.') geopy-1.11.0/geopy/geocoders/ignfrance.py0000664000175000017500000004552312571061561017715 0ustar ijlijl00000000000000""" :class:`.IGNFrance` is the IGN France Geocoder. """ import xml.etree.ElementTree as ET from geopy.compat import (urlencode, HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener, u, install_opener, iteritems, Request) from geopy.geocoders.base import Geocoder, DEFAULT_TIMEOUT, DEFAULT_SCHEME from geopy.exc import ( GeocoderQueryError, ConfigurationError, ) from geopy.location import Location from geopy.util import logger __all__ = ("IGNFrance", ) class IGNFrance(Geocoder): # pylint: disable=W0223 """ Geocoder using the IGN France GeoCoder OpenLS API. Documentation at: http://api.ign.fr/tech-docs-js/fr/developpeur/search.html """ xml_request = """ {sub_request} """ def __init__( self, api_key, username=None, password=None, referer=None, domain='wxs.ign.fr', scheme=DEFAULT_SCHEME, timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None, ): # pylint: disable=R0913 """ Initialize a customized IGN France geocoder. :param string api_key: The API key required by IGN France API to perform geocoding requests. You can get your key here: http://api.ign.fr. Mandatory. For authentication with referer and with username/password, the api key always differ. :param string username: When making a call need HTTP simple authentication username. Mandatory if no referer set :param string password: When making a call need HTTP simple authentication password. Mandatory if no referer set :param string referer: When making a call need HTTP referer. Mandatory if no password and username :param string domain: Currently it is 'wxs.ign.fr', can be changed for testing purposes for developer API e.g gpp3-wxs.ign.fr at the moment. :param string scheme: Use 'https' or 'http' as the API URL's scheme. Default is https. Note that SSL connections' certificates are not verified. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. """ super(IGNFrance, self).__init__( scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent ) # Catch if no api key with username and password # or no api key with referer if not (api_key and username and password) \ and not (api_key and referer): raise ConfigurationError('You should provide an api key and a ' 'username with a password or an api ' 'key with a referer depending on ' 'created api key') if (username and password) and referer: raise ConfigurationError('You can\'t set username/password and ' 'referer together. The API key always ' 'differs depending on both scenarios') if username and not password: raise ConfigurationError( 'username and password must be set together' ) self.api_key = api_key self.username = username self.password = password self.referer = referer self.domain = domain.strip('/') self.api = "{scheme}://{domain}/{api_key}/geoportail/ols".format( scheme=self.scheme, api_key=self.api_key, domain=self.domain ) if username and password and referer is None: self.addSimpleHTTPAuthHeader() def geocode( self, query, query_type='StreetAddress', maximum_responses=25, is_freeform=False, filtering=None, exactly_one=True, timeout=None ): # pylint: disable=W0221,R0913 """ Geocode a location query. :param string query: The query string to be geocoded. :param string query_type: The type to provide for geocoding. It can be PositionOfInterest, StreetAddress or CadastralParcel. StreetAddress is the default choice if none provided. :param int maximum_responses: The maximum number of responses to ask to the API in the query body. :param string is_freeform: Set if return is structured with freeform structure or a more structured returned. By default, value is False. :param string filtering: Provide string that help setting geocoder filter. It contains an XML string. See examples in documentation and ignfrance.py file in directory tests. :param bool exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. """ # Check if acceptable query type if query_type not in ['PositionOfInterest', 'StreetAddress', 'CadastralParcel']: raise GeocoderQueryError("""You did not provided a query_type the webservice can consume. It should be PositionOfInterest, 'StreetAddress or CadastralParcel""") # Check query validity for CadastralParcel if query_type == 'CadastralParcel' and len(query.strip()) != 14: raise GeocoderQueryError("""You must send a string of fourteen characters long to match the cadastre required code""") sub_request = """
{query} {filtering}
""" xml_request = self.xml_request.format( method_name='LocationUtilityService', sub_request=sub_request, maximum_responses=maximum_responses ) # Manage type change for xml case sensitive if is_freeform: is_freeform = 'true' else: is_freeform = 'false' # Manage filtering value if filtering is None: filtering = '' # Create query using parameters request_string = xml_request.format( is_freeform=is_freeform, query=query, query_type=query_type, filtering=filtering ) params = { 'xls': request_string } url = "?".join((self.api, urlencode(params))) logger.debug("%s.geocode: %s", self.__class__.__name__, url) raw_xml = self._request_raw_content(url, timeout) return self._parse_xml( raw_xml, is_freeform=is_freeform, exactly_one=exactly_one ) def reverse( self, query, reverse_geocode_preference=('StreetAddress', ), maximum_responses=25, filtering='', exactly_one=False, timeout=None ): # pylint: disable=W0221,R0913 """ Given a point, find an address. :param query: The coordinates for which you wish to obtain the closest human-readable addresses. :type query: :class:`geopy.point.Point`, list or tuple of (latitude, longitude), or string as "%(latitude)s, %(longitude)s" :param list reverse_geocode_preference: Enable to set expected results type. It can be StreetAddress or PositionOfInterest. Default is set to StreetAddress :param int maximum_responses: The maximum number of responses to ask to the API in the query body. :param string filtering: Provide string that help setting geocoder filter. It contains an XML string. See examples in documentation and ignfrance.py file in directory tests. :param boolean exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. """ sub_request = """ {reverse_geocode_preference} {query} {filtering} """ xml_request = self.xml_request.format( method_name='ReverseGeocodeRequest', sub_request=sub_request, maximum_responses=maximum_responses ) for pref in reverse_geocode_preference: if pref not in ('StreetAddress', 'PositionOfInterest'): raise GeocoderQueryError( '`reverse_geocode_preference` must contain ' 'one or more of: StreetAddress, PositionOfInterest' ) point = self._coerce_point_to_string(query).replace(',', ' ') reverse_geocode_preference = '\n'.join(( '%s' % pref for pref in reverse_geocode_preference )) request_string = xml_request.format( maximum_responses=maximum_responses, query=point, reverse_geocode_preference=reverse_geocode_preference, filtering=filtering ) url = "?".join((self.api, urlencode({'xls': request_string}))) logger.debug("%s.reverse: %s", self.__class__.__name__, url) raw_xml = self._request_raw_content(url, timeout) return self._parse_xml( raw_xml, exactly_one=exactly_one, is_reverse=True, is_freeform='false' ) def addSimpleHTTPAuthHeader(self): """ Create Urllib request object embedding HTTP simple authentication """ sub_request = """
{query}
""" xml_request = self.xml_request.format( method_name='LocationUtilityService', sub_request=sub_request, maximum_responses=1 ) # Create query using parameters request_string = xml_request.format( is_freeform='false', query='rennes', query_type='PositionOfInterest' ) params = { 'xls': request_string } top_level_url = "?".join((self.api, urlencode(params))) password_mgr = HTTPPasswordMgrWithDefaultRealm() # Add the username and password. # If we knew the realm, we could use it instead of None. password_mgr.add_password( None, top_level_url, self.username, self.password ) handler = HTTPBasicAuthHandler(password_mgr) # create "opener" (OpenerDirector instance) opener = build_opener(handler) # Install the opener. # Now all calls to urllib.request.urlopen use our opener. install_opener(opener) def _parse_xml(self, page, is_reverse=False, is_freeform=False, exactly_one=True): """ Returns location, (latitude, longitude) from XML feed and transform to json """ # Parse the page tree = ET.fromstring(page.encode('utf-8')) # Clean tree from namespace to facilitate XML manipulation def remove_namespace(doc, namespace): """Remove namespace in the document in place.""" ns = '{%s}' % namespace ns = u(ns) nsl = len(ns) for elem in doc.getiterator(): if elem.tag.startswith(ns): elem.tag = elem.tag[nsl:] remove_namespace(tree, 'http://www.opengis.net/gml') remove_namespace(tree, 'http://www.opengis.net/xls') remove_namespace(tree, 'http://www.opengis.net/xlsext') # Return places as json instead of XML places = self._xml_to_json_places(tree, is_reverse=is_reverse) if exactly_one: return self._parse_place(places[0], is_freeform=is_freeform) else: return [ self._parse_place( place, is_freeform=is_freeform ) for place in places ] @staticmethod def _xml_to_json_places(tree, is_reverse=False): """ Transform the xml ElementTree due to XML webservice return to json """ select_multi = ( 'GeocodedAddress' if not is_reverse else 'ReverseGeocodedLocation' ) adresses = tree.findall('.//' + select_multi) places = [] sel_pl = './/Address/Place[@type="{}"]' for adr in adresses: el = {} el['pos'] = adr.find('./Point/pos') el['street'] = adr.find('.//Address/StreetAddress/Street') el['freeformaddress'] = adr.find('.//Address/freeFormAddress') el['municipality'] = adr.find(sel_pl.format('Municipality')) el['numero'] = adr.find(sel_pl.format('Numero')) el['feuille'] = adr.find(sel_pl.format('Feuille')) el['section'] = adr.find(sel_pl.format('Section')) el['departement'] = adr.find(sel_pl.format('Departement')) el['commune_absorbee'] = adr.find(sel_pl.format('CommuneAbsorbee')) el['commune'] = adr.find(sel_pl.format('Commune')) el['insee'] = adr.find(sel_pl.format('INSEE')) el['qualite'] = adr.find(sel_pl.format('Qualite')) el['territoire'] = adr.find(sel_pl.format('Territoire')) el['id'] = adr.find(sel_pl.format('ID')) el['id_tr'] = adr.find(sel_pl.format('ID_TR')) el['bbox'] = adr.find(sel_pl.format('Bbox')) el['nature'] = adr.find(sel_pl.format('Nature')) el['postal_code'] = adr.find('.//Address/PostalCode') el['extended_geocode_match_code'] = adr.find( './/ExtendedGeocodeMatchCode' ) place = {} def testContentAttrib(selector, key): """ Helper to select by attribute and if not attribute, value set to empty string """ return selector.attrib.get( key, None ) if selector is not None else None place['accuracy'] = testContentAttrib( adr.find('.//GeocodeMatchCode'), 'accuracy') place['match_type'] = testContentAttrib( adr.find('.//GeocodeMatchCode'), 'matchType') place['building'] = testContentAttrib( adr.find('.//Address/StreetAddress/Building'), 'number') place['search_centre_distance'] = testContentAttrib( adr.find('.//SearchCentreDistance'), 'value') for key, value in iteritems(el): if value is not None: place[key] = value.text if value.text == None: place[key] = None else: place[key] = None # We check if lat lng is not empty and unpack accordingly if place['pos']: lat, lng = place['pos'].split(' ') place['lat'] = lat.strip() place['lng'] = lng.strip() else: place['lat'] = place['lng'] = None # We removed the unused key place.pop("pos", None) places.append(place) return places def _request_raw_content(self, url, timeout): """ Send the request to get raw content. """ request = Request(url) if self.referer is not None: request.add_header('Referer', self.referer) raw_xml = self._call_geocoder( request, timeout=timeout, deserializer=None ) return raw_xml @staticmethod def _parse_place(place, is_freeform=None): """ Get the location, lat, lng and place from a single json place. """ # When freeform already so full adress if is_freeform == 'true': location = place.get('freeformaddress') else: # For parcelle if place.get('numero'): location = place.get('street') else: # When classic geocoding # or when reverse geocoding location = "%s %s" % ( place.get('postal_code', ''), place.get('commune', ''), ) if place.get('street'): location = "%s, %s" % ( place.get('street', ''), location, ) if place.get('building'): location = "%s %s" % ( place.get('building', ''), location, ) return Location(location, (place.get('lat'), place.get('lng')), place) geopy-1.11.0/geopy/geocoders/navidata.py0000664000175000017500000001371712571061561017550 0ustar ijlijl00000000000000""" :class:`.NaviData` is the NaviData.pl geocoder. """ from geopy.compat import urlencode from geopy.location import Location from geopy.util import logger from geopy.geocoders.base import Geocoder, DEFAULT_TIMEOUT from geopy.exc import ( GeocoderQueryError, GeocoderQuotaExceeded, ) __all__ = ("NaviData", ) class NaviData(Geocoder): # pylint: disable=W0223 """ Geocoder using the NaviData API. Documentation at: http://www.navidata.pl """ def __init__( self, api_key=None, domain='api.navidata.pl', timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None, ): """ .. versionadded:: 1.8.0 Initialize NaviData geocoder. Please note that 'scheme' parameter is not supported: at present state, all NaviData traffic use plain http. :param string api_key: The commercial API key for service. None required if you use the API for non-commercial purposes. :param string domain: Currently it is 'api.navidata.pl', can be changed for testing purposes. :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. """ super(NaviData, self).__init__( scheme="http", timeout=timeout, proxies=proxies, user_agent=user_agent ) self.api_key = api_key self.domain = domain.strip('/') self.geocode_api = 'http://%s/geocode' % (self.domain) self.reverse_geocode_api = 'http://%s/revGeo' % (self.domain) def geocode( self, query, exactly_one=True, timeout=None, ): """ Geocode a location query. :param string query: The query string to be geocoded; this must be URL encoded. :param bool exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. """ params = { 'q': self.format_string % query, } if self.api_key is not None: params["api_key"] = self.api_key url = "?".join((self.geocode_api, urlencode(params))) logger.debug("%s.geocode: %s", self.__class__.__name__, url) return self._parse_json_geocode( self._call_geocoder(url, timeout=timeout), exactly_one ) def reverse( self, query, exactly_one=True, timeout=None, ): """ Given a point, find an address. :param query: The coordinates for which you wish to obtain the closest human-readable addresses. :type query: :class:`geopy.point.Point`, list or tuple of (latitude, longitude), or string as "%(latitude)s, %(longitude)s" :param boolean exactly_one: Return one result or a list of results, if available. Currently this has no effect (only one address is returned by API). :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. """ (lat, lon) = self._coerce_point_to_string(query).split(',') params = { 'lat': lat, 'lon': lon } if self.api_key is not None: params["api_key"] = self.api_key url = "?".join((self.reverse_geocode_api, urlencode(params))) logger.debug("%s.reverse: %s", self.__class__.__name__, url) return self._parse_json_revgeocode( self._call_geocoder(url, timeout=timeout) ) @staticmethod def _parse_json_geocode(page, exactly_one=True): '''Returns location, (latitude, longitude) from json feed.''' places = page if not len(places): return None def parse_place(place): '''Get the location, lat, lon from a single json result.''' location = place.get('description') latitude = place.get('lat') longitude = place.get('lon') return Location(location, (latitude, longitude), place) if exactly_one: return parse_place(places[0]) else: return [parse_place(place) for place in places] @staticmethod def _parse_json_revgeocode(page): '''Returns location, (latitude, longitude) from json feed.''' result = page if result.get('description', None) is None: return None location = result.get('description') latitude = result.get('lat') longitude = result.get('lon') return Location(location, (latitude, longitude), result) @staticmethod def _check_status(status): """ Validates error statuses. """ status_code = status['code'] if status_code == 200: # When there are no results, just return. return elif status_code == 429: # Rate limit exceeded raise GeocoderQuotaExceeded( 'The given key has gone over the requests limit in the 24' ' hour period or has submitted too many requests in too' ' short a period of time.' ) elif status_code == 403: raise GeocoderQueryError( 'Your request was denied.' ) else: raise GeocoderQueryError('Unknown error: ' + str(status_code)) geopy-1.11.0/geopy/geocoders/opencage.py0000664000175000017500000001575212571061561017543 0ustar ijlijl00000000000000""" :class:`.OpenCage` is the Opencagedata geocoder. """ from geopy.compat import urlencode from geopy.geocoders.base import Geocoder, DEFAULT_TIMEOUT, DEFAULT_SCHEME from geopy.exc import ( GeocoderQueryError, GeocoderQuotaExceeded, ) from geopy.location import Location from geopy.util import logger __all__ = ("OpenCage", ) class OpenCage(Geocoder): """ Geocoder using the Open Cage Data API. Documentation at: http://geocoder.opencagedata.com/api.html ..versionadded:: 1.1.0 """ def __init__( self, api_key, domain='api.opencagedata.com', scheme=DEFAULT_SCHEME, timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None, ): # pylint: disable=R0913 """ Initialize a customized Open Cage Data geocoder. :param string api_key: The API key required by Open Cage Data to perform geocoding requests. You can get your key here: https://developer.opencagedata.com/ :param string domain: Currently it is 'api.opencagedata.com', can be changed for testing purposes. :param string scheme: Use 'https' or 'http' as the API URL's scheme. Default is https. Note that SSL connections' certificates are not verified. :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. """ super(OpenCage, self).__init__( scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent ) self.api_key = api_key self.domain = domain.strip('/') self.scheme = scheme self.api = '%s://%s/geocode/v1/json' % (self.scheme, self.domain) def geocode( self, query, bounds=None, country=None, language=None, exactly_one=True, timeout=None, ): # pylint: disable=W0221,R0913 """ Geocode a location query. :param string query: The query string to be geocoded; this must be URL encoded. :param string language: an IETF format language code (such as `es` for Spanish or pt-BR for Brazilian Portuguese); if this is omitted a code of `en` (English) will be assumed by the remote service. :param string bounds: Provides the geocoder with a hint to the region that the query resides in. This value will help the geocoder but will not restrict the possible results to the supplied region. The bounds parameter should be specified as 4 coordinate points forming the south-west and north-east corners of a bounding box. For example, `bounds=-0.563160,51.280430,0.278970,51.683979`. :param string country: Provides the geocoder with a hint to the country that the query resides in. This value will help the geocoder but will not restrict the possible results to the supplied country. The country code is a 3 character code as defined by the ISO 3166-1 Alpha 3 standard. :param bool exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. """ params = { 'key': self.api_key, 'q': self.format_string % query, } if bounds: params['bounds'] = bounds if language: params['language'] = language if country: params['country'] = country url = "?".join((self.api, urlencode(params))) logger.debug("%s.geocode: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) def reverse( self, query, language=None, exactly_one=False, timeout=None, ): # pylint: disable=W0221,R0913 """ Given a point, find an address. :param query: The coordinates for which you wish to obtain the closest human-readable addresses. :type query: :class:`geopy.point.Point`, list or tuple of (latitude, longitude), or string as "%(latitude)s, %(longitude)s" :param string language: The language in which to return results. :param boolean exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. """ params = { 'key': self.api_key, 'q': self._coerce_point_to_string(query), } if language: params['language'] = language url = "?".join((self.api, urlencode(params))) logger.debug("%s.reverse: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) def _parse_json(self, page, exactly_one=True): '''Returns location, (latitude, longitude) from json feed.''' places = page.get('results', []) if not len(places): self._check_status(page.get('status')) return None def parse_place(place): '''Get the location, lat, lng from a single json place.''' location = place.get('formatted') latitude = place['geometry']['lat'] longitude = place['geometry']['lng'] return Location(location, (latitude, longitude), place) if exactly_one: return parse_place(places[0]) else: return [parse_place(place) for place in places] @staticmethod def _check_status(status): """ Validates error statuses. """ status_code = status['code'] if status_code == 429: # Rate limit exceeded raise GeocoderQuotaExceeded( 'The given key has gone over the requests limit in the 24' ' hour period or has submitted too many requests in too' ' short a period of time.' ) if status_code == 200: # When there are no results, just return. return if status_code == 403: raise GeocoderQueryError( 'Your request was denied.' ) else: raise GeocoderQueryError('Unknown error.') geopy-1.11.0/geopy/geocoders/openmapquest.py0000664000175000017500000001001312571061561020464 0ustar ijlijl00000000000000""" :class:`.OpenMapQuest` geocoder. """ from geopy.compat import urlencode from geopy.geocoders.base import ( Geocoder, DEFAULT_FORMAT_STRING, DEFAULT_TIMEOUT, DEFAULT_SCHEME ) from geopy.location import Location from geopy.util import logger __all__ = ("OpenMapQuest", ) class OpenMapQuest(Geocoder): # pylint: disable=W0223 """ Geocoder using MapQuest Open Platform Web Services. Documentation at: http://developer.mapquest.com/web/products/open/geocoding-service """ def __init__( self, api_key=None, format_string=DEFAULT_FORMAT_STRING, scheme=DEFAULT_SCHEME, timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None, ): # pylint: disable=R0913 """ Initialize an Open MapQuest geocoder with location-specific address information. No API Key is needed by the Nominatim based platform. :param string format_string: String containing '%s' where the string to geocode should be interpolated before querying the geocoder. For example: '%s, Mountain View, CA'. The default is just '%s'. :param string scheme: Use 'https' or 'http' as the API URL's scheme. Default is https. Note that SSL connections' certificates are not verified. .. versionadded:: 0.97 :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. .. versionadded:: 0.97 :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. .. versionadded:: 0.96 """ super(OpenMapQuest, self).__init__( format_string, scheme, timeout, proxies, user_agent=user_agent ) self.api_key = api_key or '' self.api = "%s://open.mapquestapi.com/nominatim/v1/search" \ "?format=json" % self.scheme def geocode(self, query, exactly_one=True, timeout=None): # pylint: disable=W0221 """ Geocode a location query. :param string query: The address or query you wish to geocode. :param bool exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. .. versionadded:: 0.97 """ params = { 'q': self.format_string % query } if exactly_one: params['maxResults'] = 1 url = "&".join((self.api, urlencode(params))) logger.debug("%s.geocode: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) @classmethod def _parse_json(cls, resources, exactly_one=True): """ Parse display name, latitude, and longitude from an JSON response. """ if not len(resources): # pragma: no cover return None if exactly_one: return cls.parse_resource(resources[0]) else: return [cls.parse_resource(resource) for resource in resources] @classmethod def parse_resource(cls, resource): """ Return location and coordinates tuple from dict. """ location = resource['display_name'] latitude = resource['lat'] or None longitude = resource['lon'] or None if latitude and longitude: latitude = float(latitude) longitude = float(longitude) return Location(location, (latitude, longitude), resource) geopy-1.11.0/geopy/geocoders/osm.py0000664000175000017500000002152112571061561016547 0ustar ijlijl00000000000000""" OpenStreetMaps geocoder, contributed by Alessandro Pasotti of ItOpen. """ from geopy.geocoders.base import ( Geocoder, DEFAULT_FORMAT_STRING, DEFAULT_TIMEOUT, DEFAULT_SCHEME ) from geopy.compat import urlencode from geopy.location import Location from geopy.util import logger from geopy.exc import GeocoderQueryError __all__ = ("Nominatim", ) class Nominatim(Geocoder): """ Nominatim geocoder for OpenStreetMap servers. Documentation at: https://wiki.openstreetmap.org/wiki/Nominatim Note that Nominatim does not support SSL. """ structured_query_params = { 'street', 'city', 'county', 'state', 'country', 'postalcode', } def __init__( self, format_string=DEFAULT_FORMAT_STRING, view_box=None, country_bias=None, timeout=DEFAULT_TIMEOUT, proxies=None, domain='nominatim.openstreetmap.org', scheme=DEFAULT_SCHEME, user_agent=None ): # pylint: disable=R0913 """ :param string format_string: String containing '%s' where the string to geocode should be interpolated before querying the geocoder. For example: '%s, Mountain View, CA'. The default is just '%s'. :param tuple view_box: Coordinates to restrict search within. :param string country_bias: Bias results to this country. :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. .. versionadded:: 0.96 :param string domain: Should be the localized Openstreetmap domain to connect to. The default is 'nominatim.openstreetmap.org', but you can change it to a domain of your own. .. versionadded:: 1.8.2 :param string scheme: Use 'https' or 'http' as the API URL's scheme. Default is https. Note that SSL connections' certificates are not verified. .. versionadded:: 1.8.2 """ super(Nominatim, self).__init__( format_string, scheme, timeout, proxies, user_agent=user_agent ) self.country_bias = country_bias self.format_string = format_string self.view_box = view_box self.domain = domain.strip('/') self.api = "%s://%s/search" % (self.scheme, self.domain) self.reverse_api = "%s://%s/reverse" % (self.scheme, self.domain) def geocode( self, query, exactly_one=True, timeout=None, addressdetails=False, language=False, geometry=None ): # pylint: disable=R0913,W0221 """ Geocode a location query. :param query: The address, query or structured query to geocode you wish to geocode. For a structured query, provide a dictionary whose keys are one of: `street`, `city`, `county`, `state`, `country`, or `postalcode`. For more information, see Nominatim's documentation for "structured requests": https://wiki.openstreetmap.org/wiki/Nominatim :type query: dict or string .. versionchanged:: 1.0.0 :param bool exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. .. versionadded:: 0.97 :param addressdetails: If you want in *Location.raw* to include addressdetails such as city_district, etc set it to True :type addressdetails: bool :param string language: Preferred language in which to return results. Either uses standard `RFC2616 `_ accept-language string or a simple comma-separated list of language codes. :type addressdetails: string .. versionadded:: 1.0.0 :param string geometry: If present, specifies whether the geocoding service should return the result's geometry in `wkt`, `svg`, `kml`, or `geojson` formats. This is available via the `raw` attribute on the returned :class:`geopy.location.Location` object. .. versionadded:: 1.3.0 """ if isinstance(query, dict): params = { key: val for key, val in query.items() if key in self.structured_query_params } else: params = {'q': self.format_string % query} params.update({ 'format': 'json' }) # `viewbox` apparently replaces `view_box` if self.view_box: params['viewbox'] = ','.join(self.view_box) if self.country_bias: params['countrycodes'] = self.country_bias if addressdetails: params['addressdetails'] = 1 if language: params['accept-language'] = language if geometry is not None: geometry = geometry.lower() if geometry == 'wkt': params['polygon_text'] = 1 elif geometry == 'svg': params['polygon_svg'] = 1 elif geometry == 'kml': params['polygon_kml'] = 1 elif geometry == 'geojson': params['polygon_geojson'] = 1 else: raise GeocoderQueryError( "Invalid geometry format. Must be one of: " "wkt, svg, kml, geojson." ) url = "?".join((self.api, urlencode(params))) logger.debug("%s.geocode: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) def reverse( self, query, exactly_one=True, timeout=None, language=False, ): # pylint: disable=W0221 """ Returns a reverse geocoded location. :param query: The coordinates for which you wish to obtain the closest human-readable addresses. :type query: :class:`geopy.point.Point`, list or tuple of (latitude, longitude), or string as "%(latitude)s, %(longitude)s" :param bool exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. .. versionadded:: 0.97 :param string language: Preferred language in which to return results. Either uses standard `RFC2616 `_ accept-language string or a simple comma-separated list of language codes. :type addressdetails: string .. versionadded:: 1.0.0 """ try: lat, lon = [ x.strip() for x in self._coerce_point_to_string(query).split(',') ] # doh except ValueError: raise ValueError("Must be a coordinate pair or Point") params = { 'lat': lat, 'lon': lon, 'format': 'json', } if language: params['accept-language'] = language url = "?".join((self.reverse_api, urlencode(params))) logger.debug("%s.reverse: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) @staticmethod def parse_code(place): """ Parse each resource. """ latitude = place.get('lat', None) longitude = place.get('lon', None) placename = place.get('display_name', None) if latitude and longitude: latitude = float(latitude) longitude = float(longitude) return Location(placename, (latitude, longitude), place) def _parse_json(self, places, exactly_one): if places is None: return None if not isinstance(places, list): places = [places] if not len(places): return None if exactly_one is True: return self.parse_code(places[0]) else: return [self.parse_code(place) for place in places] geopy-1.11.0/geopy/geocoders/photon.py0000664000175000017500000002027612571061551017265 0ustar ijlijl00000000000000""" :class:`.Photon` geocoder. """ from geopy.compat import urlencode, string_compare from geopy.geocoders.base import ( Geocoder, DEFAULT_FORMAT_STRING, DEFAULT_TIMEOUT, DEFAULT_SCHEME ) from geopy.location import Location from geopy.util import logger __all__ = ("Photon", ) class Photon(Geocoder): # pylint: disable=W0223 """ Geocoder using Photon geocoding service (data based on OpenStreetMap and service provided by Komoot on https://photon.komoot.de). Documentation at https://github.com/komoot/photon """ def __init__( self, format_string=DEFAULT_FORMAT_STRING, scheme=DEFAULT_SCHEME, timeout=DEFAULT_TIMEOUT, proxies=None, domain='photon.komoot.de' ): # pylint: disable=R0913 """ Initialize a Photon/Komoot geocoder which aims to let you "search as you type with OpenStreetMap". No API Key is needed by this platform. :param string format_string: String containing '%s' where the string to geocode should be interpolated before querying the geocoder. For example: '%s, Mountain View, CA'. The default is just '%s'. :param string scheme: Use 'https' or 'http' as the API URL's scheme. Default is https. Note that SSL connections' certificates are not verified. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. :param string domain: Should be the localized Photon domain to connect to. The default is 'photon.komoot.de', but you can change it to a domain of your own. """ super(Photon, self).__init__( format_string, scheme, timeout, proxies ) self.domain = domain.strip('/') self.api = "%s://%s/api" % (self.scheme, self.domain) self.reverse_api = "%s://%s/reverse" % (self.scheme, self.domain) def geocode( self, query, exactly_one=True, timeout=None, location_bias=None, language=False, osm_tag=None ): # pylint: disable=W0221 """ Geocode a location query. :param string query: The address or query you wish to geocode. :param bool exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. :param location_bias: The coordinates to used as location bias. :type query: :class:`geopy.point.Point`, list or tuple of (latitude, longitude), or string as "%(latitude)s, %(longitude)s" :param string language: Preferred language in which to return results. :param osm_tag: The expression to filter (include/exclude) by key and/ or value, str as 'key:value' or list/set of str if multiple filters are requiered as ['key:!val', '!key', ':!value'] """ params = { 'q': self.format_string % query } if exactly_one: params['limit'] = 1 if language: params['lang'] = language if location_bias: try: lat, lon = [x.strip() for x in self._coerce_point_to_string(location_bias) .split(',')] params['lon'] = lon params['lat'] = lat except ValueError: raise ValueError(("Location bias must be a" " coordinate pair or Point")) if osm_tag: if isinstance(osm_tag, string_compare): params['osm_tag'] = osm_tag else: try: params['osm_tag'] = '&osm_tag='.join(osm_tag) except ValueError: raise ValueError( "osm_tag must be a string expression or " "a set/list of string expressions" ) url = "?".join((self.api, urlencode(params))) logger.debug("%s.geocode: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) def reverse( self, query, exactly_one=True, timeout=None, language=False, osm_tag=None ): # pylint: disable=W0221 """ Returns a reverse geocoded location. :param query: The coordinates for which you wish to obtain the closest human-readable addresses. :type query: :class:`geopy.point.Point`, list or tuple of (latitude, longitude), or string as "%(latitude)s, %(longitude)s" :param bool exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. :param string language: Preferred language in which to return results. :param osm_tag: The expression to filter (include/exclude) by key and/ or value, str as 'key:value' or list/set of str if multiple filters are requiered as ['key:!val', '!key', ':!value'] """ try: lat, lon = [x.strip() for x in self._coerce_point_to_string(query).split(',')] except ValueError: raise ValueError("Must be a coordinate pair or Point") params = { 'lat': lat, 'lon': lon, } if exactly_one: params['limit'] = 1 if language: params['lang'] = language if osm_tag: if isinstance(osm_tag, string_compare): params['osm_tag'] = osm_tag else: try: params['osm_tag'] = '&osm_tag='.join(osm_tag) except ValueError: raise ValueError(("osm_tag must be a string expression or " "a set/list of string expressions")) url = "?".join((self.reverse_api, urlencode(params))) logger.debug("%s.reverse: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) @classmethod def _parse_json(cls, resources, exactly_one=True): """ Parse display name, latitude, and longitude from a JSON response. """ if not len(resources): # pragma: no cover return None if exactly_one: return cls.parse_resource(resources['features'][0]) else: return [cls.parse_resource(resource) for resource in resources['features']] @classmethod def parse_resource(cls, resource): """ Return location and coordinates tuple from dict. """ name_elements = ['name', 'housenumber', 'street', 'postcode', 'street', 'city', 'state', 'country'] name = [resource.get(k) for k in name_elements if resource.get(k)] location = ', '.join(name) latitude = resource['geometry']['coordinates'][1] or None longitude = resource['geometry']['coordinates'][0] or None if latitude and longitude: latitude = float(latitude) longitude = float(longitude) return Location(location, (latitude, longitude), resource) geopy-1.11.0/geopy/geocoders/placefinder.py0000664000175000017500000001463312571061565020237 0ustar ijlijl00000000000000""" :class:`.YahooPlaceFinder` geocoder. """ from functools import partial try: from requests import get, Request from requests_oauthlib import OAuth1 requests_missing = False except ImportError: requests_missing = True from geopy.geocoders.base import Geocoder, DEFAULT_TIMEOUT from geopy.exc import GeocoderParseError from geopy.location import Location from geopy.compat import string_compare, py3k __all__ = ("YahooPlaceFinder", ) class YahooPlaceFinder(Geocoder): # pylint: disable=W0223 """ Geocoder that utilizes the Yahoo! BOSS PlaceFinder API. Documentation at: https://developer.yahoo.com/boss/geo/docs/ """ def __init__( self, consumer_key, consumer_secret, timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None, ): # pylint: disable=R0913 """ :param string consumer_key: Key provided by Yahoo. :param string consumer_secret: Secret corresponding to the key provided by Yahoo. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. :param dict proxies: If specified, routes this geocoder"s requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. .. versionadded:: 0.96 """ if requests_missing: raise ImportError( 'requests-oauthlib is needed for YahooPlaceFinder.' ' Install with `pip install geopy -e ".[placefinder]"`.' ) super(YahooPlaceFinder, self).__init__( timeout=timeout, proxies=proxies, user_agent=user_agent ) self.consumer_key = ( unicode(consumer_key) if not py3k else str(consumer_key) ) self.consumer_secret = ( unicode(consumer_secret) if not py3k else str(consumer_secret) ) self.auth = OAuth1( client_key=self.consumer_key, client_secret=self.consumer_secret, signature_method="HMAC-SHA1", signature_type="AUTH_HEADER", ) self.api = "https://yboss.yahooapis.com/geo/placefinder" @staticmethod def _filtered_results(results, min_quality, valid_country_codes): """ Returns only the results that meet the minimum quality threshold and are located in expected countries. """ if min_quality: results = [ loc for loc in results if int(loc.raw["quality"]) > min_quality ] if valid_country_codes: results = [ loc for loc in results if loc.raw["countrycode"] in valid_country_codes ] return results def _parse_response(self, content): """ Returns the parsed result of a PlaceFinder API call. """ try: placefinder = ( content["bossresponse"]["placefinder"] ) if not len(placefinder) or not len(placefinder.get("results", [])): return None results = [ Location( self.humanize(place), (float(place["latitude"]), float(place["longitude"])), raw=place ) for place in placefinder["results"] ] except (KeyError, ValueError): raise GeocoderParseError("Error parsing PlaceFinder result") return results @staticmethod def humanize(location): """ Returns a human readable representation of a raw PlaceFinder location """ return ", ".join([ location[line] for line in ["line1", "line2", "line3", "line4"] if location[line] ]) def geocode( self, query, exactly_one=True, timeout=None, min_quality=0, reverse=False, valid_country_codes=None, with_timezone=False, ): # pylint: disable=W0221,R0913 """ Geocode a location query. :param string query: The address or query you wish to geocode. :param bool exactly_one: Return one result or a list of results, if available. :param int min_quality: :param bool reverse: :param valid_country_codes: :type valid_country_codes: list or tuple :param bool with_timezone: Include the timezone in the response's `raw` dictionary (as `timezone`). """ params = { "location": query, "flags": "J", # JSON } if reverse is True: params["gflags"] = "R" if exactly_one is True: params["count"] = "1" if with_timezone is True: params['flags'] += 'T' #Return timezone response = self._call_geocoder( self.api, timeout=timeout, requester=get, params=params, auth=self.auth, ) results = self._parse_response(response) if results is None: return None results = self._filtered_results( results, min_quality, valid_country_codes, ) if exactly_one: return results[0] else: return results def reverse(self, query, exactly_one=True, timeout=None): """ Returns a reverse geocoded location using Yahoo"s PlaceFinder API. :param query: The coordinates for which you wish to obtain the closest human-readable addresses. :type query: :class:`geopy.point.Point`, list or tuple of (latitude, longitude), or string as "%(latitude)s, %(longitude)s" :param bool exactly_one: Return one result or a list of results, if available. """ query = self._coerce_point_to_string(query) if isinstance(query, string_compare): query = query.replace(" ", "") # oauth signature failure; todo return self.geocode( query, exactly_one=exactly_one, timeout=timeout, reverse=True ) geopy-1.11.0/geopy/geocoders/smartystreets.py0000664000175000017500000001121112571061561020675 0ustar ijlijl00000000000000""" :class:`.LiveAddress` geocoder. """ from geopy.geocoders.base import Geocoder, DEFAULT_TIMEOUT, DEFAULT_SCHEME from geopy.compat import urlencode from geopy.location import Location from geopy.exc import ConfigurationError, GeocoderQuotaExceeded from geopy.util import logger __all__ = ("LiveAddress", ) class LiveAddress(Geocoder): # pylint: disable=W0223 """ Initialize a customized LiveAddress geocoder provided by SmartyStreets. More information regarding the LiveAddress API can be found here: https://smartystreets.com/products/liveaddress-api """ def __init__( self, auth_id, auth_token, candidates=1, scheme=DEFAULT_SCHEME, timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None, ): # pylint: disable=R0913 """ Initialize a customized SmartyStreets LiveAddress geocoder. :param string auth_id: Valid `Auth ID` from SmartyStreets. .. versionadded:: 1.5.0 :param string auth_token: Valid `Auth Token` from SmartyStreets. :param int candidates: An integer between 1 and 10 indicating the max number of candidate addresses to return if a valid address could be found. :param string scheme: Use 'https' or 'http' as the API URL's scheme. Default is https. Note that SSL connections' certificates are not verified. .. versionadded:: 0.97 .. versionchanged:: 1.8.0 LiveAddress now requires `https`. Specifying `scheme=http` will result in a :class:`geopy.exc.ConfigurationError`. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising an :class:`geopy.exc.GeocoderTimedOut` exception. .. versionadded:: 0.97 :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. .. versionadded:: 0.96 """ super(LiveAddress, self).__init__( timeout=timeout, proxies=proxies, user_agent=user_agent ) if scheme == "http": raise ConfigurationError("LiveAddress now requires `https`.") self.scheme = scheme self.auth_id = auth_id self.auth_token = auth_token if candidates: if not 1 <= candidates <= 10: raise ValueError('candidates must be between 1 and 10') self.candidates = candidates self.api = '%s://api.smartystreets.com/street-address' % self.scheme def geocode(self, query, exactly_one=True, timeout=None): # pylint: disable=W0221 """ Geocode a location query. :param string query: The address or query you wish to geocode. :param bool exactly_one: Return one result or a list of results, if available. """ url = self._compose_url(query) logger.debug("%s.geocode: %s", self.__class__.__name__, url) return self._parse_json(self._call_geocoder(url, timeout=timeout), exactly_one) def _geocoder_exception_handler(self, error, message): # pylint: disable=R0201,W0613 """ LiveStreets-specific exceptions. """ if "no active subscriptions found" in message.lower(): raise GeocoderQuotaExceeded(message) def _compose_url(self, location): """ Generate API URL. """ query = { 'auth-id': self.auth_id, 'auth-token': self.auth_token, 'street': location, 'candidates': self.candidates } return '{url}?{query}'.format(url=self.api, query=urlencode(query)) def _parse_json(self, response, exactly_one=True): """ Parse responses as JSON objects. """ if not len(response): return None if exactly_one is True: return self._format_structured_address(response[0]) else: return [self._format_structured_address(c) for c in response] @staticmethod def _format_structured_address(address): """ Pretty-print address and return lat, lon tuple. """ latitude = address['metadata'].get('latitude') longitude = address['metadata'].get('longitude') return Location( ", ".join((address['delivery_line_1'], address['last_line'])), (latitude, longitude) if latitude and longitude else None, address ) geopy-1.11.0/geopy/geocoders/what3words.py0000664000175000017500000002055312571061561020062 0ustar ijlijl00000000000000""" :class:`.What3Words` geocoder. """ import re from geopy.compat import urlencode from geopy.geocoders.base import ( Geocoder, DEFAULT_FORMAT_STRING, DEFAULT_TIMEOUT, DEFAULT_SCHEME ) from geopy.location import Location from geopy.util import logger, join_filter from geopy import exc __all__ = ("What3Words", ) class What3Words(Geocoder): """ What3Words geocoder, documentation at: http://what3words.com/api/reference """ word_re = re.compile(r"^\*{1,1}[^\W\d\_]+$", re.U) multiple_word_re = re.compile( r"[^\W\d\_]+\.{1,1}[^\W\d\_]+\.{1,1}[^\W\d\_]+$", re.U ) def __init__( self, api_key, format_string=DEFAULT_FORMAT_STRING, scheme=DEFAULT_SCHEME, timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None, ): """ Initialize a What3Words geocoder with 3-word or OneWord-address and What3Words API key. .. versionadded:: 1.5.0 :param string api_key: Key provided by What3Words. :param string format_string: String containing '%s' where the string to geocode should be interpolated before querying the geocoder. For example: '%s, piped.gains.jungle'. The default is just '%s'. :param string scheme: Use 'https' or 'http' as the API URL's scheme. Default is https. Note that SSL connections' certificates are not verified. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. """ super(What3Words, self).__init__( format_string, scheme, timeout, proxies, user_agent=user_agent, ) self.api_key = api_key self.api = ( "%s://api.what3words.com/" % self.scheme ) def _check_query(self, query): """ Check query validity with regex """ if not (self.word_re.match(query) or self.multiple_word_re.match(query)): return False else: return True def geocode(self, query, lang='en', exactly_one=True, timeout=None): """ Geocode a "3 words" or "OneWord" query. :param string query: The 3-word or OneWord-address you wish to geocode. :param string lang: two character language codes as supported by the API (http://what3words.com/api/reference/languages). :param bool exactly_one: Parameter has no effect for this geocoder. Due to the address scheme there is always exactly one result. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. .. versionadded:: 0.97 """ if not self._check_query(query): raise exc.GeocoderQueryError( "Search string must be either like " "'word.word.word' or '*word' " ) params = { 'string': self.format_string % query, 'lang': self.format_string % lang.lower() } url = "?".join(( (self.api + "w3w"), "&".join(("=".join(('key', self.api_key)), urlencode(params))) )) logger.debug("%s.geocode: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) def _parse_json(self, resources, exactly_one=True): """ Parse type, words, latitude, and longitude and language from a JSON response. """ if resources.get('error') == "X1": raise exc.GeocoderAuthenticationFailure() if resources.get('error') == "11": raise exc.GeocoderQueryError( "Address (Word(s)) not recognised by What3Words." ) def parse_resource(resource): """ Parse record. """ if resource['type'] == '3 words': words = resource['words'] words = join_filter(".", [words[0], words[1], words[2]]) position = resource['position'] latitude, longitude = position[0], position[1] if latitude and longitude: latitude = float(latitude) longitude = float(longitude) return Location(words, (latitude, longitude), resource) elif resource['type'] == 'OneWord': words = resource['words'] words = join_filter(".", [words[0], words[1], words[2]]) oneword = resource['oneword'] info = resource['info'] address = join_filter(", ", [ oneword, words, info['name'], info['address1'], info['address2'], info['address3'], info['city'], info['county'], info['postcode'], info['country_id'] ]) position = resource['position'] latitude, longitude = position[0], position[1] if latitude and longitude: latitude = float(latitude) longitude = float(longitude) return Location(address, (latitude, longitude), resource) else: raise exc.GeocoderParseError('Error parsing result.') return parse_resource(resources) def reverse(self, query, lang='en', exactly_one=True, timeout=None): """ Given a point, find the 3 word address. :param query: The coordinates for which you wish to obtain the 3 word address. :type query: :class:`geopy.point.Point`, list or tuple of (latitude, longitude), or string as "%(latitude)s, %(longitude)s" :param string lang: two character language codes as supported by the API (http://what3words.com/api/reference/languages). :param bool exactly_one: Parameter has no effect for this geocoder. Due to the address scheme there is always exactly one result. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. """ lang = lang.lower() params = { 'position': self._coerce_point_to_string(query), 'lang': self.format_string % lang } url = "?".join(( (self.api + "position"), "&".join(("=".join(('key', self.api_key)), urlencode(params))) )) logger.debug("%s.reverse: %s", self.__class__.__name__, url) return self._parse_reverse_json( self._call_geocoder(url, timeout=timeout), ) @staticmethod def _parse_reverse_json(resources): """ Parses a location from a single-result reverse API call. """ if resources.get('error') == "21": raise exc.GeocoderQueryError("Invalid coordinates") def parse_resource(resource): """ Parse resource to return Geopy Location object """ words = resource['words'] words = join_filter(".", [words[0], words[1], words[2]]) position = resource['position'] latitude, longitude = position[0], position[1] if latitude and longitude: latitude = float(latitude) longitude = float(longitude) return Location(words, (latitude, longitude), resource) return parse_resource(resources) geopy-1.11.0/geopy/geocoders/yandex.py0000664000175000017500000001252112571061561017241 0ustar ijlijl00000000000000""" :class:`Yandex` geocoder. """ from geopy.compat import urlencode from geopy.geocoders.base import Geocoder, DEFAULT_TIMEOUT from geopy.location import Location from geopy.exc import ( GeocoderServiceError, GeocoderParseError ) from geopy.util import logger __all__ = ("Yandex", ) class Yandex(Geocoder): # pylint: disable=W0223 """ Yandex geocoder, documentation at: http://api.yandex.com/maps/doc/geocoder/desc/concepts/input_params.xml """ def __init__( self, api_key=None, lang=None, timeout=DEFAULT_TIMEOUT, proxies=None, user_agent=None, ): """ Create a Yandex-based geocoder. .. versionadded:: 1.5.0 :param string api_key: Yandex API key (not obligatory) http://api.yandex.ru/maps/form.xml :param string lang: response locale, the following locales are supported: "ru_RU" (default), "uk_UA", "be_BY", "en_US", "tr_TR" :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. :param dict proxies: If specified, routes this geocoder's requests through the specified proxy. E.g., {"https": "192.0.2.0"}. For more information, see documentation on :class:`urllib2.ProxyHandler`. """ super(Yandex, self).__init__( scheme='http', timeout=timeout, proxies=proxies, user_agent=user_agent ) self.api_key = api_key self.lang = lang self.api = 'http://geocode-maps.yandex.ru/1.x/' def geocode(self, query, exactly_one=True, timeout=None): # pylint: disable=W0221 """ Geocode a location query. :param string query: The address or query you wish to geocode. :param bool exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Set this only if you wish to override, on this call only, the value set during the geocoder's initialization. """ params = { 'geocode': query, 'format': 'json' } if not self.api_key is None: params['key'] = self.api_key if not self.lang is None: params['lang'] = self.lang if exactly_one is True: params['results'] = 1 url = "?".join((self.api, urlencode(params))) logger.debug("%s.geocode: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one, ) def reverse( self, query, exactly_one=False, timeout=None, ): """ Given a point, find an address. :param string query: The coordinates for which you wish to obtain the closest human-readable addresses. :type query: :class:`geopy.point.Point`, list or tuple of (latitude, longitude), or string as "%(latitude)s, %(longitude)s" :param boolean exactly_one: Return one result or a list of results, if available. :param int timeout: Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. """ try: lat, lng = [ x.strip() for x in self._coerce_point_to_string(query).split(',') ] except ValueError: raise ValueError("Must be a coordinate pair or Point") params = { 'geocode': '{0},{1}'.format(lng, lat), 'format': 'json' } if self.api_key is not None: params['key'] = self.api_key if self.lang is not None: params['lang'] = self.lang url = "?".join((self.api, urlencode(params))) logger.debug("%s.reverse: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) def _parse_json(self, doc, exactly_one): """ Parse JSON response body. """ if doc.get('error'): raise GeocoderServiceError(doc['error']['message']) try: places = doc['response']['GeoObjectCollection']['featureMember'] except KeyError: raise GeocoderParseError('Failed to parse server response') def parse_code(place): """ Parse each record. """ try: place = place['GeoObject'] except KeyError: raise GeocoderParseError('Failed to parse server response') longitude, latitude = [ float(_) for _ in place['Point']['pos'].split(' ') ] location = place.get('description') return Location(location, (latitude, longitude), place) if exactly_one: try: return parse_code(places[0]) except IndexError: return None else: return [parse_code(place) for place in places] geopy-1.11.0/geopy/__init__.py0000664000175000017500000000077312571061565015550 0ustar ijlijl00000000000000""" geopy is a Python 2 and 3 client for several popular geocoding web services. geopy makes it easy for Python developers to locate the coordinates of addresses, cities, countries, and landmarks across the globe using third-party geocoders and other data sources. geopy is tested against CPython 2.7, CPython 3.2, CPython 3.4, PyPy, and PyPy3. """ from geopy.point import Point from geopy.location import Location from geopy.geocoders import * # pylint: disable=W0401 from geopy.util import __version__ geopy-1.11.0/geopy/compat.py0000644000175000017500000000615412516742301015262 0ustar ijlijl00000000000000""" Compatibility... """ import sys py3k = sys.version_info >= (3, 0) if py3k: # pragma: no cover string_compare = str else: # pragma: no cover string_compare = (str, unicode) # Unicode compatibility, borrowed from 'six' if py3k: # pragma: no cover def u(s): """ Convert to Unicode with py3k """ return s else: # pragma: no cover def u(s): """ Convert to Unicode with unicode escaping """ return unicode(s.replace(r'\\', r'\\\\'), 'unicode_escape') if py3k: # pragma: no cover from urllib.parse import urlencode, quote # pylint: disable=W0611,F0401,W0611,E0611 from urllib.request import (Request, urlopen, # pylint: disable=W0611,F0401,W0611,E0611 build_opener, ProxyHandler, URLError, install_opener, HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler) from urllib.error import HTTPError # pylint: disable=W0611,F0401,W0611,E0611 def itervalues(d): """ Function for iterating on values due to methods renaming between Python 2 and 3 versions For Python2 """ return iter(d.values()) def iteritems(d): """ Function for iterating on items due to methods renaming between Python 2 and 3 versions For Python2 """ return iter(d.items()) else: # pragma: no cover from urllib import urlencode as original_urlencode, quote # pylint: disable=W0611,F0401,W0611,E0611 from urllib2 import (Request, HTTPError, # pylint: disable=W0611,F0401,W0611,E0611 ProxyHandler, URLError, urlopen, build_opener, install_opener, HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler) def force_str(str_or_unicode): """ Python2-only, ensures that a string is encoding to a str. """ if isinstance(str_or_unicode, unicode): return str_or_unicode.encode('utf-8') else: return str_or_unicode def urlencode(query, doseq=0): """ A version of Python's urllib.urlencode() function that can operate on unicode strings. The parameters are first cast to UTF-8 encoded strings and then encoded as per normal. Based on the urlencode from django.utils.http """ if hasattr(query, 'items'): query = query.items() return original_urlencode( [(force_str(k), [force_str(i) for i in v] if isinstance(v, (list, tuple)) else force_str(v)) for k, v in query], doseq) def itervalues(d): """ Function for iterating on values due to methods renaming between Python 2 and 3 versions For Python3 """ return d.itervalues() def iteritems(d): """ Function for iterating on items due to methods renaming between Python 2 and 3 versions For Python3 """ return d.iteritems() geopy-1.11.0/geopy/distance.py0000644000175000017500000004344112516742301015571 0ustar ijlijl00000000000000""" .. versionadded:: 0.93 Geopy can calculate geodesic distance between two points using the [Vincenty distance](https://en.wikipedia.org/wiki/Vincenty's_formulae) or [great-circle distance](https://en.wikipedia.org/wiki/Great-circle_distance) formulas, with a default of Vincenty available as the function `geopy.distance.distance`. Great-circle distance (:class:`.great_circle`) uses a spherical model of the earth, using the average great-circle radius of 6372.795 kilometers, resulting in an error of up to about 0.5%. The radius value is stored in :const:`distance.EARTH_RADIUS`, so it can be customized (it should always be in kilometers, however). Vincenty distance (:class:`.vincenty`) uses a more accurate ellipsoidal model of the earth. This is the default distance formula, and is thus aliased as ``distance.distance``. There are multiple popular ellipsoidal models, and which one will be the most accurate depends on where your points are located on the earth. The default is the WGS-84 ellipsoid, which is the most globally accurate. geopy includes a few other models in the distance.ELLIPSOIDS dictionary:: model major (km) minor (km) flattening ELLIPSOIDS = {'WGS-84': (6378.137, 6356.7523142, 1 / \ 298.257223563), 'GRS-80': (6378.137, 6356.7523141, 1 / \ 298.257222101), 'Airy (1830)': (6377.563396, 6356.256909, 1 / \ 299.3249646), 'Intl 1924': (6378.388, 6356.911946, 1 / 297.0), 'Clarke (1880)': (6378.249145, 6356.51486955, 1 / 293.465), 'GRS-67': (6378.1600, 6356.774719, 1 / 298.25), } Here's an example usage of distance.vincenty:: >>> from geopy.distance import vincenty >>> newport_ri = (41.49008, -71.312796) >>> cleveland_oh = (41.499498, -81.695391) >>> print(vincenty(newport_ri, cleveland_oh).miles) 538.3904451566326 Using great-circle distance:: >>> from geopy.distance import great_circle >>> newport_ri = (41.49008, -71.312796) >>> cleveland_oh = (41.499498, -81.695391) >>> print(great_circle(newport_ri, cleveland_oh).miles) 537.1485284062816 You can change the ellipsoid model used by the Vincenty formula like so:: >>> distance.vincenty(ne, cl, ellipsoid='GRS-80').miles The above model name will automatically be retrieved from the ELLIPSOIDS dictionary. Alternatively, you can specify the model values directly:: >>> distance.vincenty(ne, cl, ellipsoid=(6377., 6356., 1 / 297.)).miles Distances support simple arithmetic, making it easy to do things like calculate the length of a path:: >>> d = distance.distance >>> _, wa = g.geocode('Washington, DC') >>> _, pa = g.geocode('Palo Alto, CA') >>> print((d(ne, cl) + d(cl, wa) + d(wa, pa)).miles) 3276.157156868931 """ from __future__ import division from math import atan, tan, sin, cos, pi, sqrt, atan2, asin from geopy.units import radians from geopy import units, util from geopy.point import Point from geopy.compat import string_compare # Average great-circle radius in kilometers, from Wikipedia. # Using a sphere with this radius results in an error of up to about 0.5%. EARTH_RADIUS = 6372.795 # From http://www.movable-type.co.uk/scripts/LatLongVincenty.html: # The most accurate and widely used globally-applicable model for the earth # ellipsoid is WGS-84, used in this script. Other ellipsoids offering a # better fit to the local geoid include Airy (1830) in the UK, International # 1924 in much of Europe, Clarke (1880) in Africa, and GRS-67 in South # America. America (NAD83) and Australia (GDA) use GRS-80, functionally # equivalent to the WGS-84 ellipsoid. ELLIPSOIDS = { # model major (km) minor (km) flattening 'WGS-84': (6378.137, 6356.7523142, 1 / 298.257223563), 'GRS-80': (6378.137, 6356.7523141, 1 / 298.257222101), 'Airy (1830)': (6377.563396, 6356.256909, 1 / 299.3249646), 'Intl 1924': (6378.388, 6356.911946, 1 / 297.0), 'Clarke (1880)': (6378.249145, 6356.51486955, 1 / 293.465), 'GRS-67': (6378.1600, 6356.774719, 1 / 298.25) } class Distance(object): """ Base for :class:`.great_circle` and :class:`.vincenty`. """ def __init__(self, *args, **kwargs): kilometers = kwargs.pop('kilometers', 0) if len(args) == 1: # if we only get one argument we assume # it's a known distance instead of # calculating it first kilometers += args[0] elif len(args) > 1: for a, b in util.pairwise(args): kilometers += self.measure(a, b) kilometers += units.kilometers(**kwargs) self.__kilometers = kilometers def __add__(self, other): if isinstance(other, Distance): return self.__class__(self.kilometers + other.kilometers) else: raise TypeError( "Distance instance must be added with Distance instance." ) def __neg__(self): return self.__class__(-self.kilometers) def __sub__(self, other): return self + -other def __mul__(self, other): return self.__class__(self.kilometers * other) def __div__(self, other): if isinstance(other, Distance): return self.kilometers / other.kilometers else: return self.__class__(self.kilometers / other) __truediv__ = __div__ def __abs__(self): return self.__class__(abs(self.kilometers)) def __nonzero__(self): return bool(self.kilometers) __bool__ = __nonzero__ def measure(self, a, b): """ Abstract method for measure """ raise NotImplementedError() def __repr__(self): # pragma: no cover return 'Distance(%s)' % self.kilometers def __str__(self): # pragma: no cover return '%s km' % self.__kilometers def __cmp__(self, other): if isinstance(other, Distance): return cmp(self.kilometers, other.kilometers) else: return cmp(self.kilometers, other) @property def kilometers(self): # pylint: disable=C0111 return self.__kilometers @property def km(self): # pylint: disable=C0111 return self.kilometers @property def meters(self): # pylint: disable=C0111 return units.meters(kilometers=self.kilometers) @property def m(self): # pylint: disable=C0111 return self.meters @property def miles(self): # pylint: disable=C0111 return units.miles(kilometers=self.kilometers) @property def mi(self): # pylint: disable=C0111 return self.miles @property def feet(self): # pylint: disable=C0111 return units.feet(kilometers=self.kilometers) @property def ft(self): # pylint: disable=C0111 return self.feet @property def nautical(self): # pylint: disable=C0111 return units.nautical(kilometers=self.kilometers) @property def nm(self): # pylint: disable=C0111 return self.nautical class great_circle(Distance): """ Use spherical geometry to calculate the surface distance between two geodesic points. This formula can be written many different ways, including just the use of the spherical law of cosines or the haversine formula. Set which radius of the earth to use by specifying a 'radius' keyword argument. It must be in kilometers. The default is to use the module constant `EARTH_RADIUS`, which uses the average great-circle radius. Example:: >>> from geopy.distance import great_circle >>> newport_ri = (41.49008, -71.312796) >>> cleveland_oh = (41.499498, -81.695391) >>> great_circle(newport_ri, cleveland_oh).miles 537.1485284062816 """ def __init__(self, *args, **kwargs): self.RADIUS = kwargs.pop('radius', EARTH_RADIUS) super(great_circle, self).__init__(*args, **kwargs) def measure(self, a, b): a, b = Point(a), Point(b) lat1, lng1 = radians(degrees=a.latitude), radians(degrees=a.longitude) lat2, lng2 = radians(degrees=b.latitude), radians(degrees=b.longitude) sin_lat1, cos_lat1 = sin(lat1), cos(lat1) sin_lat2, cos_lat2 = sin(lat2), cos(lat2) delta_lng = lng2 - lng1 cos_delta_lng, sin_delta_lng = cos(delta_lng), sin(delta_lng) d = atan2(sqrt((cos_lat2 * sin_delta_lng) ** 2 + (cos_lat1 * sin_lat2 - sin_lat1 * cos_lat2 * cos_delta_lng) ** 2), sin_lat1 * sin_lat2 + cos_lat1 * cos_lat2 * cos_delta_lng) return self.RADIUS * d def destination(self, point, bearing, distance=None): # pylint: disable=W0621 """ TODO docs. """ point = Point(point) lat1 = units.radians(degrees=point.latitude) lng1 = units.radians(degrees=point.longitude) bearing = units.radians(degrees=bearing) if distance is None: distance = self if isinstance(distance, Distance): distance = distance.kilometers d_div_r = float(distance) / self.RADIUS lat2 = asin( sin(lat1) * cos(d_div_r) + cos(lat1) * sin(d_div_r) * cos(bearing) ) lng2 = lng1 + atan2( sin(bearing) * sin(d_div_r) * cos(lat1), cos(d_div_r) - sin(lat1) * sin(lat2) ) return Point(units.degrees(radians=lat2), units.degrees(radians=lng2)) class vincenty(Distance): """ Calculate the geodesic distance between two points using the formula devised by Thaddeus Vincenty, with an accurate ellipsoidal model of the earth. Set which ellipsoidal model of the earth to use by specifying an ``ellipsoid`` keyword argument. The default is 'WGS-84', which is the most globally accurate model. If ``ellipsoid`` is a string, it is looked up in the `ELLIPSOIDS` dictionary to obtain the major and minor semiaxes and the flattening. Otherwise, it should be a tuple with those values. See the comments above the `ELLIPSOIDS` dictionary for more information. Example:: >>> from geopy.distance import vincenty >>> newport_ri = (41.49008, -71.312796) >>> cleveland_oh = (41.499498, -81.695391) >>> print(vincenty(newport_ri, cleveland_oh).miles) 538.3904451566326 Note: This implementation of Vincenty distance fails to converge for some valid points. In some cases, a result can be obtained by increasing the number of iterations (`iterations` keyword argument, given in the class `__init__`, with a default of 20). It may be preferable to use :class:`.great_circle`, which is marginally less accurate, but always produces a result. """ ellipsoid_key = None ELLIPSOID = None def __init__(self, *args, **kwargs): self.set_ellipsoid(kwargs.pop('ellipsoid', 'WGS-84')) self.iterations = kwargs.pop('iterations', 20) major, minor, f = self.ELLIPSOID # pylint: disable=W0612 super(vincenty, self).__init__(*args, **kwargs) def set_ellipsoid(self, ellipsoid): """ Change the ellipsoid used in the calculation. """ if not isinstance(ellipsoid, (list, tuple)): try: self.ELLIPSOID = ELLIPSOIDS[ellipsoid] self.ellipsoid_key = ellipsoid except KeyError: raise Exception( "Invalid ellipsoid. See geopy.distance.ELIPSOIDS" ) else: self.ELLIPSOID = ellipsoid self.ellipsoid_key = None return def measure(self, a, b): a, b = Point(a), Point(b) lat1, lng1 = radians(degrees=a.latitude), radians(degrees=a.longitude) lat2, lng2 = radians(degrees=b.latitude), radians(degrees=b.longitude) if isinstance(self.ELLIPSOID, string_compare): major, minor, f = ELLIPSOIDS[self.ELLIPSOID] else: major, minor, f = self.ELLIPSOID delta_lng = lng2 - lng1 reduced_lat1 = atan((1 - f) * tan(lat1)) reduced_lat2 = atan((1 - f) * tan(lat2)) sin_reduced1, cos_reduced1 = sin(reduced_lat1), cos(reduced_lat1) sin_reduced2, cos_reduced2 = sin(reduced_lat2), cos(reduced_lat2) lambda_lng = delta_lng lambda_prime = 2 * pi iter_limit = self.iterations i = 0 while abs(lambda_lng - lambda_prime) > 10e-12 and i <= iter_limit: i += 1 sin_lambda_lng, cos_lambda_lng = sin(lambda_lng), cos(lambda_lng) sin_sigma = sqrt( (cos_reduced2 * sin_lambda_lng) ** 2 + (cos_reduced1 * sin_reduced2 - sin_reduced1 * cos_reduced2 * cos_lambda_lng) ** 2 ) if sin_sigma == 0: return 0 # Coincident points cos_sigma = ( sin_reduced1 * sin_reduced2 + cos_reduced1 * cos_reduced2 * cos_lambda_lng ) sigma = atan2(sin_sigma, cos_sigma) sin_alpha = ( cos_reduced1 * cos_reduced2 * sin_lambda_lng / sin_sigma ) cos_sq_alpha = 1 - sin_alpha ** 2 if cos_sq_alpha != 0: cos2_sigma_m = cos_sigma - 2 * ( sin_reduced1 * sin_reduced2 / cos_sq_alpha ) else: cos2_sigma_m = 0.0 # Equatorial line C = f / 16. * cos_sq_alpha * (4 + f * (4 - 3 * cos_sq_alpha)) lambda_prime = lambda_lng lambda_lng = ( delta_lng + (1 - C) * f * sin_alpha * ( sigma + C * sin_sigma * ( cos2_sigma_m + C * cos_sigma * ( -1 + 2 * cos2_sigma_m ** 2 ) ) ) ) if i > iter_limit: raise ValueError("Vincenty formula failed to converge!") u_sq = cos_sq_alpha * (major ** 2 - minor ** 2) / minor ** 2 A = 1 + u_sq / 16384. * ( 4096 + u_sq * (-768 + u_sq * (320 - 175 * u_sq)) ) B = u_sq / 1024. * (256 + u_sq * (-128 + u_sq * (74 - 47 * u_sq))) delta_sigma = ( B * sin_sigma * ( cos2_sigma_m + B / 4. * ( cos_sigma * ( -1 + 2 * cos2_sigma_m ** 2 ) - B / 6. * cos2_sigma_m * ( -3 + 4 * sin_sigma ** 2 ) * ( -3 + 4 * cos2_sigma_m ** 2 ) ) ) ) s = minor * A * (sigma - delta_sigma) return s def destination(self, point, bearing, distance=None): # pylint: disable=W0621 """ TODO docs. """ point = Point(point) lat1 = units.radians(degrees=point.latitude) lng1 = units.radians(degrees=point.longitude) bearing = units.radians(degrees=bearing) if distance is None: distance = self if isinstance(distance, Distance): distance = distance.kilometers ellipsoid = self.ELLIPSOID if isinstance(ellipsoid, string_compare): ellipsoid = ELLIPSOIDS[ellipsoid] major, minor, f = ellipsoid tan_reduced1 = (1 - f) * tan(lat1) cos_reduced1 = 1 / sqrt(1 + tan_reduced1 ** 2) sin_reduced1 = tan_reduced1 * cos_reduced1 sin_bearing, cos_bearing = sin(bearing), cos(bearing) sigma1 = atan2(tan_reduced1, cos_bearing) sin_alpha = cos_reduced1 * sin_bearing cos_sq_alpha = 1 - sin_alpha ** 2 u_sq = cos_sq_alpha * (major ** 2 - minor ** 2) / minor ** 2 A = 1 + u_sq / 16384. * ( 4096 + u_sq * (-768 + u_sq * (320 - 175 * u_sq)) ) B = u_sq / 1024. * (256 + u_sq * (-128 + u_sq * (74 - 47 * u_sq))) sigma = distance / (minor * A) sigma_prime = 2 * pi while abs(sigma - sigma_prime) > 10e-12: cos2_sigma_m = cos(2 * sigma1 + sigma) sin_sigma, cos_sigma = sin(sigma), cos(sigma) delta_sigma = B * sin_sigma * ( cos2_sigma_m + B / 4. * ( cos_sigma * ( -1 + 2 * cos2_sigma_m ) - B / 6. * cos2_sigma_m * ( -3 + 4 * sin_sigma ** 2 ) * ( -3 + 4 * cos2_sigma_m ** 2 ) ) ) sigma_prime = sigma sigma = distance / (minor * A) + delta_sigma sin_sigma, cos_sigma = sin(sigma), cos(sigma) lat2 = atan2( sin_reduced1 * cos_sigma + cos_reduced1 * sin_sigma * cos_bearing, (1 - f) * sqrt( sin_alpha ** 2 + ( sin_reduced1 * sin_sigma - cos_reduced1 * cos_sigma * cos_bearing ) ** 2 ) ) lambda_lng = atan2( sin_sigma * sin_bearing, cos_reduced1 * cos_sigma - sin_reduced1 * sin_sigma * cos_bearing ) C = f / 16. * cos_sq_alpha * (4 + f * (4 - 3 * cos_sq_alpha)) delta_lng = ( lambda_lng - (1 - C) * f * sin_alpha * ( sigma + C * sin_sigma * ( cos2_sigma_m + C * cos_sigma * ( -1 + 2 * cos2_sigma_m ** 2 ) ) ) ) lng2 = lng1 + delta_lng return Point(units.degrees(radians=lat2), units.degrees(radians=lng2)) # Set the default distance formula to the most generally accurate. distance = VincentyDistance = vincenty GreatCircleDistance = great_circle geopy-1.11.0/geopy/exc.py0000644000175000017500000000463412516742301014557 0ustar ijlijl00000000000000""" Exceptions raised by geopy. """ class GeopyError(Exception): """ Geopy-specific exceptions are all inherited from GeopyError. """ class ConfigurationError(GeopyError): """ When instantiating a geocoder, the arguments given were invalid. See the documentation of each geocoder's `__init__` for more details. """ class GeocoderServiceError(GeopyError): """ There was an exception caused when calling the remote geocoding service, and no more specific exception could be raised by geopy. When calling geocoders' `geocode` or `reverse` methods, this is the most general exception that can be raised, and any non-geopy exception will be caught and turned into this. The exception's message will be that of the original exception. """ class GeocoderQueryError(GeocoderServiceError): """ Either geopy detected input that would cause a request to fail, or a request was made and the remote geocoding service responded that the request was bad. """ class GeocoderQuotaExceeded(GeocoderServiceError): """ The remote geocoding service refused to fulfill the request because the client has used its quota. """ class GeocoderAuthenticationFailure(GeocoderServiceError): """ The remote geocoding service rejects the API key or account credentials this geocoder was instantiated with. """ class GeocoderInsufficientPrivileges(GeocoderServiceError): """ The remote geocoding service refused to fulfill a request using the account credentials given. """ class GeocoderTimedOut(GeocoderServiceError): """ The call to the geocoding service was aborted because no response was receiving within the `timeout` argument of either the geocoding class or, if specified, the method call. Some services are just consistently slow, and a higher timeout may be needed to use them. """ class GeocoderUnavailable(GeocoderServiceError): """ Either it was not possible to establish a connection to the remote geocoding service, or the service responded with a code indicating it was unavailable. """ class GeocoderParseError(GeocoderServiceError): """ Geopy could not parse the service's response. This is a bug in geopy. """ class GeocoderNotFound(GeopyError): """ Caller requested the geocoder matching a string, e.g., "google" > GoogleV3, but no geocoder could be found. """ geopy-1.11.0/geopy/format.py0000644000175000017500000000570512516742301015270 0ustar ijlijl00000000000000""" Formatting... """ from geopy import units from geopy.compat import py3k if py3k: unichr = chr # pylint: disable=W0622 # Unicode characters for symbols that appear in coordinate strings. DEGREE = unichr(176) PRIME = unichr(8242) DOUBLE_PRIME = unichr(8243) ASCII_DEGREE = '' ASCII_PRIME = "'" ASCII_DOUBLE_PRIME = '"' LATIN1_DEGREE = chr(176) HTML_DEGREE = '°' HTML_PRIME = '′' HTML_DOUBLE_PRIME = '″' XML_DECIMAL_DEGREE = '°' XML_DECIMAL_PRIME = '′' XML_DECIMAL_DOUBLE_PRIME = '″' XML_HEX_DEGREE = '&xB0;' XML_HEX_PRIME = '&x2032;' XML_HEX_DOUBLE_PRIME = '&x2033;' ABBR_DEGREE = 'deg' ABBR_ARCMIN = 'arcmin' ABBR_ARCSEC = 'arcsec' DEGREES_FORMAT = ( "%(degrees)d%(deg)s %(minutes)d%(arcmin)s %(seconds)g%(arcsec)s" ) UNICODE_SYMBOLS = { 'deg': DEGREE, 'arcmin': PRIME, 'arcsec': DOUBLE_PRIME } ASCII_SYMBOLS = { 'deg': ASCII_DEGREE, 'arcmin': ASCII_PRIME, 'arcsec': ASCII_DOUBLE_PRIME } LATIN1_SYMBOLS = { 'deg': LATIN1_DEGREE, 'arcmin': ASCII_PRIME, 'arcsec': ASCII_DOUBLE_PRIME } HTML_SYMBOLS = { 'deg': HTML_DEGREE, 'arcmin': HTML_PRIME, 'arcsec': HTML_DOUBLE_PRIME } XML_SYMBOLS = { 'deg': XML_DECIMAL_DEGREE, 'arcmin': XML_DECIMAL_PRIME, 'arcsec': XML_DECIMAL_DOUBLE_PRIME } ABBR_SYMBOLS = { 'deg': ABBR_DEGREE, 'arcmin': ABBR_ARCMIN, 'arcsec': ABBR_ARCSEC } def format_degrees(degrees, fmt=DEGREES_FORMAT, symbols=None): """ TODO docs. """ symbols = symbols or ASCII_SYMBOLS arcminutes = units.arcminutes(degrees=degrees - int(degrees)) arcseconds = units.arcseconds(arcminutes=arcminutes - int(arcminutes)) format_dict = dict( symbols, degrees=degrees, minutes=abs(arcminutes), seconds=abs(arcseconds) ) return fmt % format_dict DISTANCE_FORMAT = "%(magnitude)s%(unit)s" DISTANCE_UNITS = { 'km': lambda d: d, 'm': lambda d: units.meters(kilometers=d), 'mi': lambda d: units.miles(kilometers=d), 'ft': lambda d: units.feet(kilometers=d), 'nm': lambda d: units.nautical(kilometers=d), 'nmi': lambda d: units.nautical(kilometers=d) } def format_distance(kilometers, fmt=DISTANCE_FORMAT, unit='km'): """ TODO docs. """ magnitude = DISTANCE_UNITS[unit](kilometers) return fmt % {'magnitude': magnitude, 'unit': unit} _DIRECTIONS = [ ('north', 'N'), ('north by east', 'NbE'), ('north-northeast', 'NNE'), ('northeast by north', 'NEbN'), ('northeast', 'NE'), ('northeast by east', 'NEbE'), ('east-northeast', 'ENE'), ('east by north', 'EbN'), ('east', 'E'), ('east by south', 'EbS'), ('east-southeast', 'ESE'), ('southeast by east', 'SEbE'), ('southeast', 'SE'), ('southeast by south', 'SEbS'), ] DIRECTIONS, DIRECTIONS_ABBR = zip(*_DIRECTIONS) ANGLE_DIRECTIONS = { n * 11.25: d for n, d in enumerate(DIRECTIONS) } ANGLE_DIRECTIONS_ABBR = { n * 11.25: d for n, d in enumerate(DIRECTIONS_ABBR) } geopy-1.11.0/geopy/location.py0000644000175000017500000000704112516742301015603 0ustar ijlijl00000000000000""" :class:`.Location` returns geocoder results. """ from geopy.point import Point from geopy.compat import string_compare, py3k class Location(object): # pylint: disable=R0903,R0921 """ Contains a parsed geocoder response. Can be iterated over as (location, (latitude, longitude (?P[NS])?[ ]* (?P-?%(FLOAT)s)(?:[%(DEGREE)sD\*\u00B0\s][ ]* (?:(?P%(FLOAT)s)[%(PRIME)s'm][ ]*)? (?:(?P%(FLOAT)s)[%(DOUBLE_PRIME)s"s][ ]*)? )?(?P[NS])?) %(SEP)s (?P (?P[EW])?[ ]* (?P-?%(FLOAT)s)(?:[%(DEGREE)sD\*\u00B0\s][ ]* (?:(?P%(FLOAT)s)[%(PRIME)s'm][ ]*)? (?:(?P%(FLOAT)s)[%(DOUBLE_PRIME)s"s][ ]*)? )?(?P[EW])?)(?: %(SEP)s (?P (?P-?%(FLOAT)s)[ ]* (?Pkm|m|mi|ft|nm|nmi)))? .*?$ """ % { "FLOAT": r'\d+(?:\.\d+)?', "DEGREE": DEGREE, "PRIME": PRIME, "DOUBLE_PRIME": DOUBLE_PRIME, "SEP": r'\s*[,;/\s]\s*', }, re.X) class Point(object): """ A geodetic point with latitude, longitude, and altitude. Latitude and longitude are floating point values in degrees. Altitude is a floating point value in kilometers. The reference level is never considered and is thus application dependent, so be consistent! The default for all values is 0. Points can be created in a number of ways... With longitude, latitude, and altitude:: >>> p1 = Point(41.5, -81, 0) >>> p2 = Point(latitude=41.5, longitude=-81) With a sequence of 0 to 3 values (longitude, latitude, altitude):: >>> p1 = Point([41.5, -81, 0]) >>> p2 = Point((41.5, -81)) Copy another `Point` instance:: >>> p2 = Point(p1) >>> p2 == p1 True >>> p2 is p1 False Give a string containing at least latitude and longitude:: >>> p1 = Point('41.5,-81.0') >>> p2 = Point('41.5 N -81.0 W') >>> p3 = Point('-41.5 S, 81.0 E, 2.5km') >>> p4 = Point('23 26m 22s N 23 27m 30s E 21.0mi') >>> p5 = Point('''3 26' 22" N 23 27' 30" E''') Point values can be accessed by name or by index:: >>> p = Point(41.5, -81.0, 0) >>> p.latitude == p[0] True >>> p.longitude == p[1] True >>> p.altitude == p[2] True When unpacking (or iterating), a (latitude, longitude, altitude) tuple is returned:: >>> latitude, longitude, altitude = p """ __slots__ = ("latitude", "longitude", "altitude", "_items") POINT_PATTERN = POINT_PATTERN def __new__(cls, latitude=None, longitude=None, altitude=None): """ :param float latitude: Latitude of point. :param float longitude: Longitude of point. :param float altitude: Altitude of point. """ single_arg = longitude is None and altitude is None if single_arg and not isinstance(latitude, util.NUMBER_TYPES): arg = latitude if arg is None: # pragma: no cover pass elif isinstance(arg, Point): return cls.from_point(arg) elif isinstance(arg, string_compare): return cls.from_string(arg) else: try: seq = iter(arg) except TypeError: # pragma: no cover raise TypeError( "Failed to create Point instance from %r." % (arg,) ) else: return cls.from_sequence(seq) latitude = float(latitude or 0.0) if abs(latitude) > 90: latitude = ((latitude + 90) % 180) - 90 longitude = float(longitude or 0.0) if abs(longitude) > 180: longitude = ((longitude + 180) % 360) - 180 altitude = float(altitude or 0.0) self = super(Point, cls).__new__(cls) self.latitude = latitude self.longitude = longitude self.altitude = altitude self._items = [self.latitude, self.longitude, self.altitude] return self def __getitem__(self, index): return self._items[index] def __setitem__(self, index, value): self._items[index] = value def __iter__(self): return iter((self.latitude, self.longitude, self.altitude)) def __repr__(self): return "Point(%r, %r, %r)" % tuple(self._items) def format(self, altitude=None, deg_char='', min_char='m', sec_char='s'): """ Format decimal degrees (DD) to degrees minutes seconds (DMS) """ latitude = "%s %s" % ( format_degrees(abs(self.latitude), symbols={ 'deg': deg_char, 'arcmin': min_char, 'arcsec': sec_char }), self.latitude >= 0 and 'N' or 'S' ) longitude = "%s %s" % ( format_degrees(abs(self.longitude), symbols={ 'deg': deg_char, 'arcmin': min_char, 'arcsec': sec_char }), self.longitude >= 0 and 'E' or 'W' ) coordinates = [latitude, longitude] if altitude is None: altitude = bool(self.altitude) if altitude: if not isinstance(altitude, string_compare): altitude = 'km' coordinates.append(self.format_altitude(altitude)) return ", ".join(coordinates) def format_decimal(self, altitude=None): """ Format decimal degrees with altitude """ coordinates = [str(self.latitude), str(self.longitude)] if altitude is None: altitude = bool(self.altitude) if altitude is True: if not isinstance(altitude, string_compare): altitude = 'km' coordinates.append(self.format_altitude(altitude)) return ", ".join(coordinates) def format_altitude(self, unit='km'): """ Foamt altitude with unit """ return format_distance(self.altitude, unit=unit) def __str__(self): return self.format() def __unicode__(self): return self.format( None, DEGREE, PRIME, DOUBLE_PRIME ) def __eq__(self, other): return tuple(self) == tuple(other) def __ne__(self, other): return tuple(self) != tuple(other) @classmethod def parse_degrees(cls, degrees, arcminutes, arcseconds, direction=None): """ Parse degrees minutes seconds including direction (N, S, E, W) """ degrees = float(degrees) negative = degrees < 0 arcminutes = float(arcminutes) arcseconds = float(arcseconds) if arcminutes or arcseconds: more = units.degrees(arcminutes=arcminutes, arcseconds=arcseconds) if negative: degrees -= more else: degrees += more if direction in [None, 'N', 'E']: return degrees elif direction in ['S', 'W']: return -degrees else: raise ValueError("Invalid direction! Should be one of [NSEW].") @classmethod def parse_altitude(cls, distance, unit): """ Parse altitude managing units conversion """ if distance is not None: distance = float(distance) CONVERTERS = { 'km': lambda d: d, 'm': lambda d: units.kilometers(meters=d), 'mi': lambda d: units.kilometers(miles=d), 'ft': lambda d: units.kilometers(feet=d), 'nm': lambda d: units.kilometers(nautical=d), 'nmi': lambda d: units.kilometers(nautical=d) } try: return CONVERTERS[unit](distance) except KeyError: # pragma: no cover raise NotImplementedError( 'Bad distance unit specified, valid are: %r' % CONVERTERS.keys() ) else: return distance @classmethod def from_string(cls, string): """ Create and return a ``Point`` instance from a string containing latitude and longitude, and optionally, altitude. Latitude and longitude must be in degrees and may be in decimal form or indicate arcminutes and arcseconds (labeled with Unicode prime and double prime, ASCII quote and double quote or 'm' and 's'). The degree symbol is optional and may be included after the decimal places (in decimal form) and before the arcminutes and arcseconds otherwise. Coordinates given from south and west (indicated by S and W suffixes) will be converted to north and east by switching their signs. If no (or partial) cardinal directions are given, north and east are the assumed directions. Latitude and longitude must be separated by at least whitespace, a comma, or a semicolon (each with optional surrounding whitespace). Altitude, if supplied, must be a decimal number with given units. The following unit abbrevations (case-insensitive) are supported: - ``km`` (kilometers) - ``m`` (meters) - ``mi`` (miles) - ``ft`` (feet) - ``nm``, ``nmi`` (nautical miles) Some example strings the will work include: - 41.5;-81.0 - 41.5,-81.0 - 41.5 -81.0 - 41.5 N -81.0 W - -41.5 S;81.0 E - 23 26m 22s N 23 27m 30s E - 23 26' 22" N 23 27' 30" E - UT: N 39°20' 0'' / W 74°35' 0'' """ match = re.match(cls.POINT_PATTERN, re.sub(r"''", r'"', string)) if match: latitude_direction = None if match.group("latitude_direction_front"): latitude_direction = match.group("latitude_direction_front") elif match.group("latitude_direction_back"): latitude_direction = match.group("latitude_direction_back") longitude_direction = None if match.group("longitude_direction_front"): longitude_direction = match.group("longitude_direction_front") elif match.group("longitude_direction_back"): longitude_direction = match.group("longitude_direction_back") latitude = cls.parse_degrees( match.group('latitude_degrees') or 0.0, match.group('latitude_arcminutes') or 0.0, match.group('latitude_arcseconds') or 0.0, latitude_direction ) longitude = cls.parse_degrees( match.group('longitude_degrees') or 0.0, match.group('longitude_arcminutes') or 0.0, match.group('longitude_arcseconds') or 0.0, longitude_direction ) altitude = cls.parse_altitude( match.group('altitude_distance'), match.group('altitude_units') ) return cls(latitude, longitude, altitude) else: raise ValueError( "Failed to create Point instance from string: unknown format." ) @classmethod def from_sequence(cls, seq): """ Create and return a new ``Point`` instance from any iterable with 0 to 3 elements. The elements, if present, must be latitude, longitude, and altitude, respectively. """ args = tuple(islice(seq, 4)) return cls(*args) @classmethod def from_point(cls, point): """ Create and return a new ``Point`` instance from another ``Point`` instance. """ return cls(point.latitude, point.longitude, point.altitude) geopy-1.11.0/geopy/units.py0000644000175000017500000000565112516742301015142 0ustar ijlijl00000000000000""" Convert units. """ import math # Angles def degrees(radians=0, arcminutes=0, arcseconds=0): # pylint: disable=W0621 """ TODO docs. """ deg = 0. if radians: deg = math.degrees(radians) if arcminutes: deg += arcminutes / arcmin(degrees=1.) if arcseconds: deg += arcseconds / arcsec(degrees=1.) return deg def radians(degrees=0, arcminutes=0, arcseconds=0): # pylint: disable=W0621 """ TODO docs. """ if arcminutes: degrees += arcminutes / arcmin(degrees=1.) if arcseconds: degrees += arcseconds / arcsec(degrees=1.) return math.radians(degrees) def arcminutes(degrees=0, radians=0, arcseconds=0): # pylint: disable=W0621 """ TODO docs. """ if radians: degrees += math.degrees(radians) if arcseconds: degrees += arcseconds / arcsec(degrees=1.) return degrees * 60. def arcseconds(degrees=0, radians=0, arcminutes=0): # pylint: disable=W0621 """ TODO docs. """ if radians: degrees += math.degrees(radians) if arcminutes: degrees += arcminutes / arcmin(degrees=1.) return degrees * 3600. # Lengths def kilometers(meters=0, miles=0, feet=0, nautical=0): # pylint: disable=W0621 """ TODO docs. """ ret = 0. if meters: ret += meters / 1000. if feet: miles += feet / ft(1.) if nautical: ret += nautical / nm(1.) ret += miles * 1.609344 return ret def meters(kilometers=0, miles=0, feet=0, nautical=0): # pylint: disable=W0621 """ TODO docs. """ return (kilometers + km(nautical=nautical, miles=miles, feet=feet)) * 1000 def miles(kilometers=0, meters=0, feet=0, nautical=0): # pylint: disable=W0621 """ TODO docs. """ ret = 0. if nautical: kilometers += nautical / nm(1.) if feet: ret += feet / ft(1.) if meters: kilometers += meters / 1000. ret += kilometers * 0.621371192 return ret def feet(kilometers=0, meters=0, miles=0, nautical=0): # pylint: disable=W0621 """ TODO docs. """ ret = 0. if nautical: kilometers += nautical / nm(1.) if meters: kilometers += meters / 1000. if kilometers: miles += mi(kilometers=kilometers) ret += miles * 5280 return ret def nautical(kilometers=0, meters=0, miles=0, feet=0): # pylint: disable=W0621 """ TODO docs. """ ret = 0. if feet: miles += feet / ft(1.) if miles: kilometers += km(miles=miles) if meters: kilometers += meters / 1000. ret += kilometers / 1.852 return ret # Compatible names rad = radians # pylint: disable=C0103 arcmin = arcminutes # pylint: disable=C0103 arcsec = arcseconds # pylint: disable=C0103 km = kilometers # pylint: disable=C0103 m = meters # pylint: disable=C0103 mi = miles # pylint: disable=C0103 ft = feet # pylint: disable=C0103 nm = nautical # pylint: disable=C0103 geopy-1.11.0/geopy/util.py0000664000175000017500000000441212571061575014761 0ustar ijlijl00000000000000""" Utils. """ import logging from geopy.compat import py3k if not py3k: # pragma: no cover NUMBER_TYPES = (int, long, float) else: # pragma: no cover NUMBER_TYPES = (int, float) # long -> int in Py3k try: from decimal import Decimal NUMBER_TYPES = NUMBER_TYPES + (Decimal, ) except ImportError: # pragma: no cover pass __version__ = "1.11.0" class NullHandler(logging.Handler): """ No output. """ def emit(self, record): pass logger = logging.getLogger('geopy') # pylint: disable=C0103 logger.setLevel(logging.CRITICAL) def pairwise(seq): """ Pair an iterable, e.g., (1, 2, 3, 4) -> ((1, 2), (3, 4)) """ for i in range(0, len(seq) - 1): yield (seq[i], seq[i + 1]) if not py3k: def join_filter(sep, seq, pred=bool): """ Join with a filter. """ return sep.join([unicode(i) for i in seq if pred(i)]) else: def join_filter(sep, seq, pred=bool): """ Join with a filter. """ return sep.join([str(i) for i in seq if pred(i)]) if not py3k: def decode_page(page): """ Return unicode string of geocoder results. Nearly all services use JSON, so assume UTF8 encoding unless the response specifies otherwise. """ if hasattr(page, 'read'): # urllib # note getparam in py2 encoding = page.headers.getparam("charset") or "utf-8" return unicode(page.read(), encoding=encoding) else: # requests? encoding = page.headers.get("charset", "utf-8") return unicode(page.content, encoding=encoding) else: def decode_page(page): """ Return unicode string of geocoder results. Nearly all services use JSON, so assume UTF8 encoding unless the response specifies otherwise. """ if hasattr(page, 'read'): # urllib # note get_param in py3 encoding = page.headers.get_param("charset") or "utf-8" return str(page.read(), encoding=encoding) else: # requests? encoding = page.headers.get("charset") or "utf-8" return str(page.content, encoding=encoding) def get_version(): from geopy.version import GEOPY_VERSION return str(GEOPY_VERSION) geopy-1.11.0/geopy.egg-info/0000775000175000017500000000000012571061653015120 5ustar ijlijl00000000000000geopy-1.11.0/geopy.egg-info/PKG-INFO0000644000175000017500000000221712571061652016214 0ustar ijlijl00000000000000Metadata-Version: 1.1 Name: geopy Version: 1.11.0 Summary: Python Geocoding Toolbox Home-page: https://github.com/geopy/geopy Author: GeoPy Contributors Author-email: uijllji@gmail License: MIT Download-URL: https://github.com/geopy/geopy/archive/1.11.0.tar.gz Description: UNKNOWN Keywords: geocode geocoding gis geographical maps earth distance Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Scientific/Engineering :: GIS Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy geopy-1.11.0/geopy.egg-info/SOURCES.txt0000644000175000017500000000316212571061653017004 0ustar ijlijl00000000000000LICENSE MANIFEST.in README setup.cfg setup.py geopy/__init__.py geopy/__init__.pyc geopy/compat.py geopy/compat.pyc geopy/distance.py geopy/distance.pyc geopy/exc.py geopy/exc.pyc geopy/format.py geopy/format.pyc geopy/location.py geopy/location.pyc geopy/point.py geopy/point.pyc geopy/units.py geopy/units.pyc geopy/util.py geopy/util.pyc geopy/version.pyc geopy.egg-info/PKG-INFO geopy.egg-info/SOURCES.txt geopy.egg-info/dependency_links.txt geopy.egg-info/pbr.json geopy.egg-info/requires.txt geopy.egg-info/top_level.txt geopy/geocoders/__init__.py geopy/geocoders/__init__.pyc geopy/geocoders/arcgis.py geopy/geocoders/arcgis.pyc geopy/geocoders/baidu.py geopy/geocoders/baidu.pyc geopy/geocoders/base.py geopy/geocoders/base.pyc geopy/geocoders/bing.py geopy/geocoders/bing.pyc geopy/geocoders/databc.py geopy/geocoders/databc.pyc geopy/geocoders/dot_us.py geopy/geocoders/dot_us.pyc geopy/geocoders/geocodefarm.py geopy/geocoders/geocodefarm.pyc geopy/geocoders/geonames.py geopy/geocoders/geonames.pyc geopy/geocoders/googlev3.py geopy/geocoders/googlev3.pyc geopy/geocoders/ignfrance.py geopy/geocoders/ignfrance.pyc geopy/geocoders/navidata.py geopy/geocoders/navidata.pyc geopy/geocoders/opencage.py geopy/geocoders/opencage.pyc geopy/geocoders/openmapquest.py geopy/geocoders/openmapquest.pyc geopy/geocoders/osm.py geopy/geocoders/osm.pyc geopy/geocoders/photon.py geopy/geocoders/photon.pyc geopy/geocoders/placefinder.py geopy/geocoders/placefinder.pyc geopy/geocoders/smartystreets.py geopy/geocoders/smartystreets.pyc geopy/geocoders/what3words.py geopy/geocoders/what3words.pyc geopy/geocoders/yandex.py geopy/geocoders/yandex.pycgeopy-1.11.0/geopy.egg-info/dependency_links.txt0000644000175000017500000000000112571061652021163 0ustar ijlijl00000000000000 geopy-1.11.0/geopy.egg-info/pbr.json0000664000175000017500000000005712571061652016577 0ustar ijlijl00000000000000{"is_release": false, "git_version": "cefd924"}geopy-1.11.0/geopy.egg-info/requires.txt0000644000175000017500000000007112571061652017513 0ustar ijlijl00000000000000 [placefinder] requests_oauthlib>=0.4.0 [timezone] pytz geopy-1.11.0/geopy.egg-info/top_level.txt0000644000175000017500000000001312571061652017641 0ustar ijlijl00000000000000geopy test geopy-1.11.0/LICENSE0000644000175000017500000000206412516742274013314 0ustar ijlijl00000000000000Copyright (c) 2006-2015 geopy authors (see AUTHORS) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. geopy-1.11.0/MANIFEST.in0000644000175000017500000000026212516742274014043 0ustar ijlijl00000000000000include LICENSE include MANIFEST.in include CHANGELOG include README recursive-include geopy * recursive-exclude test * prune geopy/__pycache__ prune geopy/geocoders/__pycache__ geopy-1.11.0/README0000664000175000017500000001006012571061652013157 0ustar ijlijl00000000000000geopy ===== |Build Status| geopy is a Python 2 and 3 client for several popular geocoding web services. geopy makes it easy for Python developers to locate the coordinates of addresses, cities, countries, and landmarks across the globe using third-party geocoders and other data sources. geopy includes geocoder classes for the `OpenStreetMap Nominatim `__, `ESRI ArcGIS `__, `Google Geocoding API (V3) `__, `Baidu Maps `__, `Bing Maps API `__, `Yahoo! PlaceFinder `__, `Yandex `__, `IGN France `__, `GeoNames `__, `NaviData `__, `OpenMapQuest `__, `What3Words `__, `OpenCage `__, `SmartyStreets `__, `geocoder.us `__, and `GeocodeFarm `__ geocoder services. The various geocoder classes are located in `geopy.geocoders `__. geopy is tested against CPython 2.7, CPython 3.2, CPython 3.4, PyPy, and PyPy3. © geopy contributors 2006-2015 (see AUTHORS) under the `MIT License `__. Installation ------------ Install using `pip `__ with: :: pip install geopy Or, `download a wheel or source archive from PyPI `__. Geocoding --------- To geolocate a query to an address and coordinates: :: >>> from geopy.geocoders import Nominatim >>> geolocator = Nominatim() >>> location = geolocator.geocode("175 5th Avenue NYC") >>> print(location.address) Flatiron Building, 175, 5th Avenue, Flatiron, New York, NYC, New York, ... >>> print((location.latitude, location.longitude)) (40.7410861, -73.9896297241625) >>> print(location.raw) {'place_id': '9167009604', 'type': 'attraction', ...} To find the address corresponding to a set of coordinates: :: >>> from geopy.geocoders import Nominatim >>> geolocator = Nominatim() >>> location = geolocator.reverse("52.509669, 13.376294") >>> print(location.address) Potsdamer Platz, Mitte, Berlin, 10117, Deutschland, European Union >>> print((location.latitude, location.longitude)) (52.5094982, 13.3765983) >>> print(location.raw) {'place_id': '654513', 'osm_type': 'node', ...} Measuring Distance ------------------ Geopy can calculate geodesic distance between two points using the `Vincenty distance `__ or `great-circle distance `__ formulas, with a default of Vincenty available as the class ``geopy.distance.distance``, and the computed distance available as attributes (e.g., ``miles``, ``meters``, etc.). Here's an example usage of Vincenty distance: :: >>> from geopy.distance import vincenty >>> newport_ri = (41.49008, -71.312796) >>> cleveland_oh = (41.499498, -81.695391) >>> print(vincenty(newport_ri, cleveland_oh).miles) 538.3904451566326 Using great-circle distance: :: >>> from geopy.distance import great_circle >>> newport_ri = (41.49008, -71.312796) >>> cleveland_oh = (41.499498, -81.695391) >>> print(great_circle(newport_ri, cleveland_oh).miles) 537.1485284062816 Documentation ------------- More documentation and examples can be found at `Read the Docs `__. .. |Build Status| image:: https://travis-ci.org/geopy/geopy.svg?branch=master :target: https://travis-ci.org/geopy/geopy geopy-1.11.0/setup.py0000775000175000017500000000313712571061561014022 0ustar ijlijl00000000000000#!/usr/bin/env python """ geopy """ from setuptools import setup, find_packages from geopy import __version__ as version INSTALL_REQUIRES = [] TESTS_REQUIRES = [ 'nose-cov', 'pylint', 'tox', 'mock' ] setup( name='geopy', version=version, description='Python Geocoding Toolbox', author='GeoPy Contributors', author_email='uijllji@gmail', url='https://github.com/geopy/geopy', download_url=( 'https://github.com/geopy/geopy/archive/%s.tar.gz' % version ), packages=find_packages(), install_requires=INSTALL_REQUIRES, tests_require=TESTS_REQUIRES, extras_require={ "placefinder": ["requests_oauthlib>=0.4.0"], "timezone": ["pytz"], }, license='MIT', keywords='geocode geocoding gis geographical maps earth distance', classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: Science/Research", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Topic :: Scientific/Engineering :: GIS", "Topic :: Software Development :: Libraries :: Python Modules", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", ] ) geopy-1.11.0/PKG-INFO0000664000175000017500000000221712571061653013402 0ustar ijlijl00000000000000Metadata-Version: 1.1 Name: geopy Version: 1.11.0 Summary: Python Geocoding Toolbox Home-page: https://github.com/geopy/geopy Author: GeoPy Contributors Author-email: uijllji@gmail License: MIT Download-URL: https://github.com/geopy/geopy/archive/1.11.0.tar.gz Description: UNKNOWN Keywords: geocode geocoding gis geographical maps earth distance Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Scientific/Engineering :: GIS Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy geopy-1.11.0/setup.cfg0000644000175000017500000000012212571061653014115 0ustar ijlijl00000000000000[wheel] universal = 1 [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0