geopy-1.20.0/0000755000076500000240000000000013472475530013364 5ustar kostyastaff00000000000000geopy-1.20.0/AUTHORS0000644000076500000240000000660213472474123014435 0ustar kostyastaff00000000000000Adam Tygart Adrián López Afonso Queiros Albina Alessandro Pasotti Andrea Tosatto Ann Paul Antonis Kanouras Arsen Mamikonyan Arsen Mamikonyan Arthur Pemberton Artur avdd Benjamin Henne Benjamin Trigona-Harany Benjamin Trigona-Harany Benoit Grégoire Bernd Schlapsi Brian Beck Charles Karney crccheck Dale Daniel Thul Danny Finkelstein Dave Arter David Gilman deeplook Demeter Sztanko Dody Suria Wijaya dutko.adam Edward Betts Emile Aben Eric Palakovich Carr exogen Fabien Reboia Feanil Patel gary.bernhardt Gregory Nicholas groovecoder Hanno Schlichting Ian Edwards Ian Wilson ijl ironfroggy@gmail.com Isaac Sijaranamual James Maddox James Mills jhmaddox Joel Natividad John.L.Clark Jonathan Batchelor Jordan Bouvier Jose Martin jqnatividad Karimov Dmitriy Kostya Esmukov Luca Marra Luke Hubbard Magnus Hiie Marc-Olivier Titeux Marco Milanesi Mariana Georgieva Martin Mesut Öncel Micah Cochran michal Michal Migurski Mike Hansen Mike Tigas Mike Toews mtmail mz navidata nucflash Oleg Oskar Hollmann Pavel Paweł Mandera Pedro Rodrigues Peter Gullekson Philip Kimmey Risent Zhang Rocky Meza Ryan Nagle scottessner Sebastian Illing Sebastian Neubauer SemiNormal Sergey Lyapustin Serphentas svalee Svetlana Konovalova Sébastien Barré TheRealZeljko Thomas Tom Wallroth tony tristan Vladimir Kalinkin William Hammond willr Yorick Holkamp geopy-1.20.0/LICENSE0000644000076500000240000000206413322440422014355 0ustar kostyastaff00000000000000Copyright (c) 2006-2018 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.20.0/MANIFEST.in0000644000076500000240000000023013314632132015101 0ustar kostyastaff00000000000000include AUTHORS include LICENSE include README.rst recursive-exclude test * global-exclude __pycache__ global-exclude *.py[co] global-exclude .DS_Store geopy-1.20.0/PKG-INFO0000644000076500000240000001412413472475530014463 0ustar kostyastaff00000000000000Metadata-Version: 2.1 Name: geopy Version: 1.20.0 Summary: Python Geocoding Toolbox Home-page: https://github.com/geopy/geopy Author: GeoPy Contributors Author-email: uijllji@gmail.com License: MIT Download-URL: https://github.com/geopy/geopy/archive/1.20.0.tar.gz Description: geopy ===== .. image:: https://img.shields.io/pypi/v/geopy.svg?style=flat-square :target: https://pypi.python.org/pypi/geopy/ :alt: Latest Version .. image:: https://img.shields.io/travis/geopy/geopy.svg?style=flat-square :target: https://travis-ci.org/geopy/geopy :alt: Build Status .. image:: https://img.shields.io/github/license/geopy/geopy.svg?style=flat-square :target: https://pypi.python.org/pypi/geopy/ :alt: License 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`_, `Google Geocoding API (V3)`_, and many other geocoding services. The full list is available on the `Geocoders doc section`_. Geocoder classes are located in `geopy.geocoders`_. .. _OpenStreetMap Nominatim: https://wiki.openstreetmap.org/wiki/Nominatim .. _Google Geocoding API (V3): https://developers.google.com/maps/documentation/geocoding/ .. _Geocoders doc section: https://geopy.readthedocs.io/en/latest/#geocoders .. _geopy.geocoders: https://github.com/geopy/geopy/tree/master/geopy/geocoders geopy is tested against CPython (versions 2.7, 3.4, 3.5, 3.6, 3.7), PyPy, and PyPy3. geopy does not and will not support CPython 2.6. © geopy contributors 2006-2018 (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: .. code:: python >>> from geopy.geocoders import Nominatim >>> geolocator = Nominatim(user_agent="specify_your_app_name_here") >>> 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: .. code:: python >>> from geopy.geocoders import Nominatim >>> geolocator = Nominatim(user_agent="specify_your_app_name_here") >>> 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 `geodesic distance `_ or the `great-circle distance `_, with a default of the geodesic distance available as the function `geopy.distance.distance`. Here's an example usage of the geodesic distance: .. code:: python >>> from geopy.distance import geodesic >>> newport_ri = (41.49008, -71.312796) >>> cleveland_oh = (41.499498, -81.695391) >>> print(geodesic(newport_ri, cleveland_oh).miles) 538.390445368 Using great-circle distance: .. code:: python >>> 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) 536.997990696 Documentation ------------- More documentation and examples can be found at `Read the Docs `__. 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.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Provides-Extra: dev Provides-Extra: dev-test Provides-Extra: timezone Provides-Extra: dev-lint Provides-Extra: dev-docs geopy-1.20.0/README.rst0000644000076500000240000000747413446072254015064 0ustar kostyastaff00000000000000geopy ===== .. image:: https://img.shields.io/pypi/v/geopy.svg?style=flat-square :target: https://pypi.python.org/pypi/geopy/ :alt: Latest Version .. image:: https://img.shields.io/travis/geopy/geopy.svg?style=flat-square :target: https://travis-ci.org/geopy/geopy :alt: Build Status .. image:: https://img.shields.io/github/license/geopy/geopy.svg?style=flat-square :target: https://pypi.python.org/pypi/geopy/ :alt: License 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`_, `Google Geocoding API (V3)`_, and many other geocoding services. The full list is available on the `Geocoders doc section`_. Geocoder classes are located in `geopy.geocoders`_. .. _OpenStreetMap Nominatim: https://wiki.openstreetmap.org/wiki/Nominatim .. _Google Geocoding API (V3): https://developers.google.com/maps/documentation/geocoding/ .. _Geocoders doc section: https://geopy.readthedocs.io/en/latest/#geocoders .. _geopy.geocoders: https://github.com/geopy/geopy/tree/master/geopy/geocoders geopy is tested against CPython (versions 2.7, 3.4, 3.5, 3.6, 3.7), PyPy, and PyPy3. geopy does not and will not support CPython 2.6. © geopy contributors 2006-2018 (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: .. code:: python >>> from geopy.geocoders import Nominatim >>> geolocator = Nominatim(user_agent="specify_your_app_name_here") >>> 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: .. code:: python >>> from geopy.geocoders import Nominatim >>> geolocator = Nominatim(user_agent="specify_your_app_name_here") >>> 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 `geodesic distance `_ or the `great-circle distance `_, with a default of the geodesic distance available as the function `geopy.distance.distance`. Here's an example usage of the geodesic distance: .. code:: python >>> from geopy.distance import geodesic >>> newport_ri = (41.49008, -71.312796) >>> cleveland_oh = (41.499498, -81.695391) >>> print(geodesic(newport_ri, cleveland_oh).miles) 538.390445368 Using great-circle distance: .. code:: python >>> 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) 536.997990696 Documentation ------------- More documentation and examples can be found at `Read the Docs `__. geopy-1.20.0/geopy/0000755000076500000240000000000013472475530014507 5ustar kostyastaff00000000000000geopy-1.20.0/geopy/__init__.py0000644000076500000240000000156713446072254016626 0ustar kostyastaff00000000000000""" 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 (versions 2.7, 3.4, 3.5, 3.6, 3.7), PyPy, and PyPy3. geopy does not and will not support CPython 2.6. """ from geopy.geocoders import * # noqa from geopy.location import Location # noqa from geopy.point import Point # noqa from geopy.timezone import Timezone # noqa from geopy.util import __version__ # noqa # geopy.geocoders.options must not be importable as `geopy.options`, # because that is ambiguous (which options are that). del options # noqa # `__all__` is intentionally not defined in order to not duplicate # the same list of geocoders as in `geopy.geocoders` package. geopy-1.20.0/geopy/compat.py0000644000076500000240000001154513446072254016347 0ustar kostyastaff00000000000000""" Compatibility... """ import inspect import sys import warnings from time import sleep from timeit import default_timer py3k = sys.version_info >= (3, 0) if py3k: # pragma: no cover string_compare = str else: # pragma: no cover string_compare = (str, unicode) # noqa if py3k: # pragma: no cover text_type = str else: # pragma: no cover text_type = unicode # noqa # 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') # noqa if py3k: # pragma: no cover def cmp(a, b): return (a > b) - (a < b) else: # pragma: no cover cmp = cmp # builtin in py2 if py3k: from math import isfinite else: from math import isinf, isnan def isfinite(x): return not isinf(x) and not isnan(x) if py3k: # pragma: no cover from urllib.error import HTTPError from urllib.parse import parse_qs, quote, quote_plus, urlencode, urlparse from urllib.request import (HTTPBasicAuthHandler, HTTPPasswordMgrWithDefaultRealm, HTTPSHandler, ProxyHandler, Request, URLError, build_opener, urlopen) 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 quote, quote_plus # noqa from urllib import urlencode as original_urlencode from urllib2 import (HTTPBasicAuthHandler, HTTPError, # noqa HTTPPasswordMgrWithDefaultRealm, HTTPSHandler, ProxyHandler, Request, URLError, build_opener, urlopen) from urlparse import parse_qs, urlparse # noqa def force_str(str_or_unicode): """ Python2-only, ensures that a string is encoding to a str. """ if isinstance(str_or_unicode, unicode): # noqa 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() def _is_urllib_context_supported(HTTPSHandler_=HTTPSHandler): context_arg = 'context' if py3k: argspec = inspect.getfullargspec(HTTPSHandler_.__init__) return context_arg in argspec.args or context_arg in argspec.kwonlyargs else: return context_arg in inspect.getargspec(HTTPSHandler_.__init__).args _URLLIB_SUPPORTS_SSL_CONTEXT = _is_urllib_context_supported() def build_opener_with_context(context=None, *handlers): # `context` has been added in Python 2.7.9 and 3.4.3. if _URLLIB_SUPPORTS_SSL_CONTEXT: https_handler = HTTPSHandler(context=context) else: warnings.warn( ("SSL context is not supported in your environment for urllib " "calls. Perhaps your Python version is obsolete? " "This probably means that TLS verification doesn't happen, " "which is insecure. Please consider upgrading your Python " "interpreter version."), UserWarning) https_handler = HTTPSHandler() return build_opener(https_handler, *handlers) def sleep_at_least(secs): # Before Python 3.5 time.sleep(secs) can be interrupted by os signals. # See https://docs.python.org/3/library/time.html#time.sleep # This function ensures that the sleep took *at least* `secs`. now = default_timer() deadline = now + secs while deadline > now: sleep(max(deadline - now, 0.1)) now = default_timer() geopy-1.20.0/geopy/distance.py0000644000076500000240000005545213446072254016663 0ustar kostyastaff00000000000000""" Geopy can calculate geodesic distance between two points using the `geodesic distance `_ or the `great-circle distance `_, with a default of the geodesic distance available as the function ``geopy.distance.distance``. Great-circle distance (:class:`.great_circle`) uses a spherical model of the earth, using the mean earth radius as defined by the International Union of Geodesy and Geophysics, (2\\ *a* + *b*)/3 = 6371.0087714150598 kilometers approx 6371.009 km (for WGS-84), 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). The geodesic distance is the shortest distance on the surface of an ellipsoidal model of the earth. The default algorithm uses the method is given by `Karney (2013) `_ (:class:`.geodesic`); this is accurate to round-off and always converges. An older *deprecated* method due to `Vincenty (1975) `_ (:class:`.vincenty`) is also available; this is only accurate to 0.2 mm and the distance calculation fails to converge for nearly antipodal points. ``geopy.distance.distance`` currently uses :class:`.geodesic`. 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 :const:`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 are examples of ``distance.distance`` usage:: >>> from geopy import distance >>> newport_ri = (41.49008, -71.312796) >>> cleveland_oh = (41.499498, -81.695391) >>> print(distance.distance(newport_ri, cleveland_oh).miles) 538.39044536 >>> wellington = (-41.32, 174.81) >>> salamanca = (40.96, -5.50) >>> print(distance.distance(wellington, salamanca).km) 19959.6792674 The second example above fails with :class:`.vincenty`. Using :class:`.great_circle` distance:: >>> print(distance.great_circle(newport_ri, cleveland_oh).miles) 536.997990696 You can change the ellipsoid model used by the geodesic formulas like so:: >>> ne, cl = newport_ri, cleveland_oh >>> print(distance.geodesic(ne, cl, ellipsoid='GRS-80').miles) The above model name will automatically be retrieved from the :const:`distance.ELLIPSOIDS` dictionary. Alternatively, you can specify the model values directly:: >>> distance.geodesic(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:: >>> from geopy import Nominatim >>> d = distance.distance >>> g = Nominatim(user_agent="specify_your_app_name_here") >>> _, wa = g.geocode('Washington, DC') >>> _, pa = g.geocode('Palo Alto, CA') >>> print((d(ne, cl) + d(cl, wa) + d(wa, pa)).miles) 3277.30439191 """ from __future__ import division import warnings from math import asin, atan, atan2, cos, pi, sin, sqrt, tan from geographiclib.geodesic import Geodesic from geopy import units, util from geopy.compat import cmp, py3k, string_compare from geopy.point import Point from geopy.units import radians # IUGG mean earth radius in kilometers, from # https://en.wikipedia.org/wiki/Earth_radius#Mean_radius. Using a # sphere with this radius results in an error of up to about 0.5%. EARTH_RADIUS = 6371.009 # 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) } def lonlat(x, y, z=0): """ ``geopy.distance.distance`` accepts coordinates in ``(y, x)``/``(lat, lon)`` order, while some other libraries and systems might use ``(x, y)``/``(lon, lat)``. This function provides a convenient way to convert coordinates of the ``(x, y)``/``(lon, lat)`` format to a :class:`geopy.point.Point` instance. Example:: >>> from geopy.distance import lonlat, distance >>> newport_ri_xy = (-71.312796, 41.49008) >>> cleveland_oh_xy = (-81.695391, 41.499498) >>> print(distance(lonlat(*newport_ri_xy), lonlat(*cleveland_oh_xy)).miles) 538.3904453677203 :param x: longitude :param y: latitude :param z: (optional) altitude :return: Point(latitude, longitude, altitude) """ return Point(y, x, z) class Distance(object): """ Base for :class:`.great_circle`, :class:`.vincenty`, and :class:`.geodesic`. """ 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): # py2 only if isinstance(other, Distance): return cmp(self.kilometers, other.kilometers) else: return cmp(self.kilometers, other) if py3k: def __eq__(self, other): return self.__cmp__(other) == 0 def __ne__(self, other): return self.__cmp__(other) != 0 def __gt__(self, other): return self.__cmp__(other) > 0 def __lt__(self, other): return self.__cmp__(other) < 0 def __ge__(self, other): return self.__cmp__(other) >= 0 def __le__(self, other): return self.__cmp__(other) <= 0 @property def kilometers(self): return self.__kilometers @property def km(self): return self.kilometers @property def meters(self): return units.meters(kilometers=self.kilometers) @property def m(self): return self.meters @property def miles(self): return units.miles(kilometers=self.kilometers) @property def mi(self): return self.miles @property def feet(self): return units.feet(kilometers=self.kilometers) @property def ft(self): return self.feet @property def nautical(self): return units.nautical(kilometers=self.kilometers) @property def nm(self): return self.nautical class great_circle(Distance): """ Use spherical geometry to calculate the surface distance between two points. 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) >>> print(great_circle(newport_ri, cleveland_oh).miles) 536.997990696 """ 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): """ 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)) GreatCircleDistance = great_circle class geodesic(Distance): """ Calculate the geodesic distance between two points. 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 geodesic >>> newport_ri = (41.49008, -71.312796) >>> cleveland_oh = (41.499498, -81.695391) >>> print(geodesic(newport_ri, cleveland_oh).miles) 538.390445368 .. versionadded:: 1.13.0 """ ellipsoid_key = None ELLIPSOID = None geod = None def __init__(self, *args, **kwargs): self.set_ellipsoid(kwargs.pop('ellipsoid', 'WGS-84')) if 'iterations' in kwargs: warnings.warn('Ignoring unused `iterations` kwarg for geodesic ' 'distance.', DeprecationWarning, stacklevel=2) kwargs.pop('iterations', 0) major, minor, f = self.ELLIPSOID super(geodesic, 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.ELLIPSOIDS" ) else: self.ELLIPSOID = ellipsoid self.ellipsoid_key = None return # Call geographiclib routines for measure and destination def measure(self, a, b): a, b = Point(a), Point(b) lat1, lon1 = a.latitude, a.longitude lat2, lon2 = b.latitude, b.longitude if not (isinstance(self.geod, Geodesic) and self.geod.a == self.ELLIPSOID[0] and self.geod.f == self.ELLIPSOID[2]): self.geod = Geodesic(self.ELLIPSOID[0], self.ELLIPSOID[2]) s12 = self.geod.Inverse(lat1, lon1, lat2, lon2, Geodesic.DISTANCE)['s12'] return s12 def destination(self, point, bearing, distance=None): """ TODO docs. """ point = Point(point) lat1 = point.latitude lon1 = point.longitude azi1 = bearing if distance is None: distance = self if isinstance(distance, Distance): distance = distance.kilometers if not (isinstance(self.geod, Geodesic) and self.geod.a == self.ELLIPSOID[0] and self.geod.f == self.ELLIPSOID[2]): self.geod = Geodesic(self.ELLIPSOID[0], self.ELLIPSOID[2]) r = self.geod.Direct(lat1, lon1, azi1, distance, Geodesic.LATITUDE | Geodesic.LONGITUDE) return Point(r['lat2'], r['lon2']) GeodesicDistance = geodesic class vincenty(Distance): """ .. deprecated:: 1.13 Use :class:`.geodesic` instead. Vincenty will be removed in geopy 2.0. Calculate the geodesic distance between two points using the Vincenty's method. 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.390445362 Note: Vincenty's method for distance fails to converge for some valid (nearly antipodal) points. In such cases, use :class:`.geodesic` which always produces an accurate result. """ ellipsoid_key = None ELLIPSOID = None _show_deprecation_warning = True def __init__(self, *args, **kwargs): if self._show_deprecation_warning: warnings.warn('Vincenty is deprecated and is going to be removed ' 'in geopy 2.0. Use `geopy.distance.geodesic` ' '(or the default `geopy.distance.distance`) ' 'instead, which is more accurate and always converges.', DeprecationWarning, stacklevel=2) self.set_ellipsoid(kwargs.pop('ellipsoid', 'WGS-84')) self.iterations = kwargs.pop('iterations', 20) major, minor, f = self.ELLIPSOID 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 (i == 0 or (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): """ 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 ** 2 ) - 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)) VincentyDistance = vincenty # Set the default distance formula distance = GeodesicDistance geopy-1.20.0/geopy/exc.py0000644000076500000240000000472213374047210015634 0ustar kostyastaff00000000000000""" 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 generic 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 rejected 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 has been received 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 probably due to 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.20.0/geopy/extra/0000755000076500000240000000000013472475530015632 5ustar kostyastaff00000000000000geopy-1.20.0/geopy/extra/__init__.py0000644000076500000240000000000013376351756017737 0ustar kostyastaff00000000000000geopy-1.20.0/geopy/extra/rate_limiter.py0000644000076500000240000001254613446072254020671 0ustar kostyastaff00000000000000# coding: utf-8 from itertools import chain, count from timeit import default_timer from geopy.compat import sleep_at_least from geopy.exc import GeocoderServiceError from geopy.util import logger def _is_last_gen(count): """list(_is_last_gen(2)) -> [False, False, True]""" return chain((False for _ in range(count)), [True]) class RateLimiter(object): """RateLimiter allows to perform bulk operations while gracefully handling error responses and adding delays when needed. In the example below a delay of 1 second (``min_delay_seconds=1``) will be added between each pair of ``geolocator.geocode`` calls; all :class:`geopy.exc.GeocoderServiceError` exceptions will be retried (up to ``max_retries`` times):: import pandas as pd df = pd.DataFrame({'name': ['paris', 'berlin', 'london']}) from geopy.geocoders import Nominatim geolocator = Nominatim(user_agent="specify_your_app_name_here") from geopy.extra.rate_limiter import RateLimiter geocode = RateLimiter(geolocator.geocode, min_delay_seconds=1) df['location'] = df['name'].apply(geocode) df['point'] = df['location'].apply(lambda loc: tuple(loc.point) if loc else None) This would produce the following DataFrame:: >>> df name location \\ 0 paris (Paris, Île-de-France, France métropolitaine, ... 1 berlin (Berlin, 10117, Deutschland, (52.5170365, 13.3... 2 london (London, Greater London, England, SW1A 2DU, UK... point 0 (48.8566101, 2.3514992, 0.0) 1 (52.5170365, 13.3888599, 0.0) 2 (51.5073219, -0.1276474, 0.0) To pass extra options to the `geocode` call:: from functools import partial df['location'] = df['name'].apply(partial(geocode, language='de')) To see a progress bar:: from tqdm import tqdm tqdm.pandas() df['location'] = df['name'].progress_apply(geocode) Before using this class, please consult with the Geocoding service ToS, which might explicitly consider bulk requests (even throttled) a violation. .. versionadded:: 1.16.0 """ def __init__(self, func, min_delay_seconds=0.0, max_retries=2, error_wait_seconds=5.0, swallow_exceptions=True, return_value_on_exception=None): """ :param callable func: A function which should be wrapped by the :class:`.RateLimiter`. :param float min_delay_seconds: Minimum delay in seconds between the wrapped ``func`` calls. :param int max_retries: Number of retries on exceptions. Only :class:`geopy.exc.GeocoderServiceError` exceptions are retried -- others are always re-raised. ``max_retries + 1`` requests would be performed at max per query. Set ``max_retries=0`` to disable retries. :param float error_wait_seconds: Time to wait between retries after errors. Must be greater or equal to ``min_delay_seconds``. :param bool swallow_exceptions: Should an exception be swallowed after retries? If not, it will be re-raised. If yes, the ``return_value_on_exception`` will be returned. :param return_value_on_exception: Value to return on failure when ``swallow_exceptions=True``. """ self.func = func self.min_delay_seconds = min_delay_seconds self.max_retries = max_retries self.error_wait_seconds = error_wait_seconds self.swallow_exceptions = swallow_exceptions self.return_value_on_exception = return_value_on_exception assert error_wait_seconds >= min_delay_seconds assert max_retries >= 0 self._last_call = self._clock() - min_delay_seconds def _clock(self): # pragma: no coverage return default_timer() def _sleep(self, seconds): # pragma: no coverage logger.debug('RateLimiter sleep(%r)', seconds) sleep_at_least(seconds) def _sleep_between(self): seconds_since_last_call = self._clock() - self._last_call wait = self.min_delay_seconds - seconds_since_last_call if wait > 0: self._sleep(wait) def __call__(self, *args, **kwargs): self._sleep_between() for i, is_last_try in zip(count(), _is_last_gen(self.max_retries)): try: return self.func(*args, **kwargs) except GeocoderServiceError: if not is_last_try: logger.warning( 'RateLimiter caught an error, retrying ' '(%s/%s tries). Called with (*%r, **%r).', i, self.max_retries, args, kwargs, exc_info=True ) self._sleep(self.error_wait_seconds) continue if self.swallow_exceptions: logger.warning( 'RateLimiter swallowed an error after %r retries. ' 'Called with (*%r, **%r).', i, args, kwargs, exc_info=True ) return self.return_value_on_exception else: raise finally: self._last_call = self._clock() geopy-1.20.0/geopy/format.py0000644000076500000240000000566113446072254016356 0ustar kostyastaff00000000000000""" Formatting... """ from geopy import units from geopy.compat import py3k if py3k: unichr = chr # 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.20.0/geopy/geocoders/0000755000076500000240000000000013472475530016461 5ustar kostyastaff00000000000000geopy-1.20.0/geopy/geocoders/__init__.py0000644000076500000240000001520013400756056020564 0ustar kostyastaff00000000000000""" Each geolocation service you might use, such as Google Maps, Bing Maps, or Nominatim, 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(user_agent="specify_your_app_name_here") >>> 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(user_agent="specify_your_app_name_here") >>> 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' ``geocode`` and ``reverse`` methods require the argument ``query``, and also accept at least the argument ``exactly_one``, which is ``True`` by default. Geocoders may have additional attributes, e.g., Bing accepts ``user_location``, the effect of which is to bias results near that location. ``geocode`` 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 full geocoder's 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 Nominatim >>> geolocator = Nominatim(user_agent="specify_your_app_name_here", ... format_string="%s, Cleveland OH") >>> address, (latitude, longitude) = geolocator.geocode("11111 Euclid Ave") >>> print(address, latitude, longitude) Thwing Center, 11111, Euclid Avenue, Magnolia-Wade Park Historic District, \ University Circle, Cleveland, Cuyahoga County, Ohio, 44106, USA \ 41.5074076 -81.6083649792596 """ __all__ = ( "get_geocoder_for_service", "options", # The order of classes below should correspond to the order of their # files in the ``geocoders`` directory ordered by name. # # If you're adding a new geocoder class, then you should mention it in # this module 3 times: # 1. In this ``__all__`` tuple. # 2. In the imports block below. # 3. In the ``SERVICE_TO_GEOCODER`` dict below. # # Also don't forget to pull up the list of geocoders # in the docs: docs/index.rst "ArcGIS", "AzureMaps", "Baidu", "BANFrance", "Bing", "DataBC", "GeocodeEarth", "GeocodeFarm", "GeoNames", "GoogleV3", "Geolake", "Here", "IGNFrance", "MapBox", "OpenCage", "OpenMapQuest", "PickPoint", "Nominatim", "Pelias", "Photon", "LiveAddress", "TomTom", "What3Words", "Yandex", ) from geopy.exc import GeocoderNotFound from geopy.geocoders.arcgis import ArcGIS from geopy.geocoders.azure import AzureMaps from geopy.geocoders.baidu import Baidu from geopy.geocoders.banfrance import BANFrance from geopy.geocoders.base import options from geopy.geocoders.bing import Bing from geopy.geocoders.databc import DataBC from geopy.geocoders.geocodeearth import GeocodeEarth from geopy.geocoders.geocodefarm import GeocodeFarm from geopy.geocoders.geolake import Geolake from geopy.geocoders.geonames import GeoNames from geopy.geocoders.googlev3 import GoogleV3 from geopy.geocoders.here import Here from geopy.geocoders.ignfrance import IGNFrance from geopy.geocoders.mapbox import MapBox from geopy.geocoders.opencage import OpenCage from geopy.geocoders.openmapquest import OpenMapQuest from geopy.geocoders.osm import Nominatim from geopy.geocoders.pelias import Pelias from geopy.geocoders.photon import Photon from geopy.geocoders.pickpoint import PickPoint from geopy.geocoders.smartystreets import LiveAddress from geopy.geocoders.tomtom import TomTom from geopy.geocoders.what3words import What3Words from geopy.geocoders.yandex import Yandex SERVICE_TO_GEOCODER = { "arcgis": ArcGIS, "azure": AzureMaps, "baidu": Baidu, "banfrance": BANFrance, "bing": Bing, "databc": DataBC, "geocodeearth": GeocodeEarth, "geocodefarm": GeocodeFarm, "geonames": GeoNames, "google": GoogleV3, "googlev3": GoogleV3, "geolake": Geolake, "here": Here, "ignfrance": IGNFrance, "mapbox": MapBox, "opencage": OpenCage, "openmapquest": OpenMapQuest, "pickpoint": PickPoint, "nominatim": Nominatim, "pelias": Pelias, "photon": Photon, "liveaddress": LiveAddress, "tomtom": TomTom, "what3words": What3Words, "yandex": Yandex, } 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.20.0/geopy/geocoders/arcgis.py0000644000076500000240000003166113446072254020307 0ustar kostyastaff00000000000000import json import warnings from time import time from geopy.compat import Request, string_compare, urlencode from geopy.exc import ( ConfigurationError, GeocoderAuthenticationFailure, GeocoderServiceError, ) from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.util import logger __all__ = ("ArcGIS", ) DEFAULT_WKID = 4326 class ArcGIS(Geocoder): """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_path = '/sharing/generateToken' geocode_path = '/arcgis/rest/services/World/GeocodeServer/findAddressCandidates' reverse_path = '/arcgis/rest/services/World/GeocodeServer/reverseGeocode' def __init__( self, username=None, password=None, referer=None, token_lifetime=60, scheme=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, format_string=None, ssl_context=DEFAULT_SENTINEL, auth_domain='www.arcgis.com', domain='geocode.arcgis.com', ): """ :param str username: ArcGIS username. Required if authenticated mode is desired. :param str password: ArcGIS password. Required if authenticated mode is desired. :param str 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 str scheme: See :attr:`geopy.geocoders.options.default_scheme`. If authenticated mode is in use, it must be ``'https'``. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. .. versionadded:: 1.12.0 :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. .. versionadded:: 1.14.0 :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. .. versionadded:: 1.14.0 :param str auth_domain: Domain where the target ArcGIS auth service is hosted. Used only in authenticated mode (i.e. username, password and referer are set). .. versionadded:: 1.17.0 :param str domain: Domain where the target ArcGIS service is hosted. .. versionadded:: 1.17.0 """ super(ArcGIS, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) 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.auth_domain = auth_domain.strip('/') self.auth_api = ( '%s://%s%s' % (self.scheme, self.auth_domain, self.auth_path) ) self.token = None self.token_lifetime = token_lifetime * 60 # store in seconds self.token_expiry = None self.retry = 1 self.domain = domain.strip('/') self.api = ( '%s://%s%s' % (self.scheme, self.domain, self.geocode_path) ) self.reverse_api = ( '%s://%s%s' % (self.scheme, self.domain, self.reverse_path) ) def _authenticated_call_geocoder(self, url, timeout=DEFAULT_SENTINEL): """ Wrap self._call_geocoder, handling tokens. """ if self.token is None or int(time()) > self.token_expiry: self._refresh_authentication_token() request = Request( "&".join((url, urlencode({"token": self.token}))), headers={"Referer": self.referer} ) return self._base_call_geocoder(request, timeout=timeout) def geocode(self, query, exactly_one=True, timeout=DEFAULT_SENTINEL, out_fields=None): """ Return a location point by address. :param str 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 out_fields: A list of output fields to be returned in the attributes field of the raw data. This can be either a python list/tuple of fields or a comma-separated string. See https://developers.arcgis.com/rest/geocode/api-reference/geocoding-service-output.htm for a list of supported output fields. If you want to return all supported output fields, set ``out_fields="*"``. .. versionadded:: 1.14.0 :type out_fields: str or iterable :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ params = {'singleLine': self.format_string % query, 'f': 'json'} if exactly_one: params['maxLocations'] = 1 if out_fields is not None: if isinstance(out_fields, string_compare): params['outFields'] = out_fields else: params['outFields'] = ",".join(out_fields) 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['candidates']): return None geocoded = [] for resource in response['candidates']: geometry = resource['location'] geocoded.append( Location( resource['address'], (geometry['y'], geometry['x']), resource ) ) if exactly_one: return geocoded[0] return geocoded def reverse(self, query, exactly_one=True, timeout=DEFAULT_SENTINEL, distance=None, wkid=DEFAULT_WKID): """ Return an address by location 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 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 int distance: Distance from the query location, in meters, within which to search. ArcGIS has a default of 100 meters, if not specified. :param str wkid: WKID to use for both input and output coordinates. .. deprecated:: 1.14.0 It wasn't working before because it was specified incorrectly in the request parameters, and won't work even if we fix the request, because :class:`geopy.point.Point` normalizes the coordinates according to WKID 4326. Please open an issue in the geopy issue tracker if you believe that custom wkid values should be supported. This parameter is scheduled for removal in geopy 2.0. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ location = self._coerce_point_to_string(query, "%(lon)s,%(lat)s") if wkid != DEFAULT_WKID: warnings.warn("%s.reverse: custom wkid value has been ignored. " "It wasn't working before because it was specified " "incorrectly in the request parameters, and won't " "work even if we fix the request, because geopy.Point " "normalizes the coordinates according to WKID %s. " "Please open an issue in the geopy issue tracker " "if you believe that custom wkid values should be " "supported." % (type(self).__name__, DEFAULT_WKID), DeprecationWarning, stacklevel=2) wkid = DEFAULT_WKID 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) # https://developers.arcgis.com/rest/geocode/api-reference/geocoding-service-output.htm if response['error']['code'] == 400: # 'details': ['Unable to find address for the specified location.']} try: if 'Unable to find' in response['error']['details'][0]: return None except (KeyError, IndexError): pass raise GeocoderServiceError(str(response['error'])) address = ( "%(Address)s, %(City)s, %(Region)s %(Postal)s," " %(CountryCode)s" % response['address'] ) location = Location( address, (response['location']['y'], response['location']['x']), response['address'] ) if exactly_one: return location else: return [location] 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, 'referer': self.referer, 'expiration': self.token_lifetime, 'f': 'json' } url = "?".join((self.auth_api, urlencode(token_request_arguments))) 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 'token' not 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.20.0/geopy/geocoders/azure.py0000644000076500000240000000427413446072254020165 0ustar kostyastaff00000000000000 from geopy.geocoders.base import DEFAULT_SENTINEL from geopy.geocoders.tomtom import TomTom __all__ = ("AzureMaps", ) class AzureMaps(TomTom): """AzureMaps geocoder based on TomTom. Documentation at: https://docs.microsoft.com/en-us/azure/azure-maps/index .. versionadded:: 1.15.0 """ geocode_path = '/search/address/json' reverse_path = '/search/address/reverse/json' def __init__( self, subscription_key, format_string=None, scheme=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, ssl_context=DEFAULT_SENTINEL, domain='atlas.microsoft.com', ): """ :param str subscription_key: Azure Maps subscription key. :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. :param str domain: Domain where the target Azure Maps service is hosted. """ super(AzureMaps, self).__init__( api_key=subscription_key, format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, domain=domain, ) def _geocode_params(self, formatted_query): return { 'api-version': '1.0', 'subscription-key': self.api_key, 'query': formatted_query, } def _reverse_params(self, position): return { 'api-version': '1.0', 'subscription-key': self.api_key, 'query': position, } geopy-1.20.0/geopy/geocoders/baidu.py0000644000076500000240000002136513446072254020123 0ustar kostyastaff00000000000000import hashlib from geopy.compat import quote_plus, urlencode from geopy.exc import ( GeocoderAuthenticationFailure, GeocoderQueryError, GeocoderQuotaExceeded, GeocoderServiceError, ) from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder 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://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding .. versionadded:: 1.0.0 """ api_path = '/geocoder/v2/' def __init__( self, api_key, scheme=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, format_string=None, ssl_context=DEFAULT_SENTINEL, security_key=None, ): """ :param str api_key: The API key (AK) required by Baidu Map to perform geocoding requests. API keys are managed through the Baidu APIs console (http://lbsyun.baidu.com/apiconsole/key). :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. .. versionchanged:: 1.14.0 Default scheme has been changed from ``http`` to ``https``. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. .. versionadded:: 1.12.0 :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. .. versionadded:: 1.14.0 :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. .. versionadded:: 1.14.0 :param str security_key: The security key (SK) to calculate the SN parameter in request if authentication setting requires (http://lbsyun.baidu.com/index.php?title=lbscloud/api/appendix). .. versionadded:: 1.15.0 """ super(Baidu, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) self.api_key = api_key self.api = '%s://api.map.baidu.com%s' % (self.scheme, self.api_path) self.security_key = security_key @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=DEFAULT_SENTINEL, ): """ Return a location point by address. :param str 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. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ params = { 'ak': self.api_key, 'output': 'json', 'address': self.format_string % query, } url = self._construct_url(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, exactly_one=True, timeout=DEFAULT_SENTINEL): """ Return an address by location 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 of results, if available. Baidu's API will always return at most one result. .. versionadded:: 1.14.0 :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. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ params = { 'ak': self.api_key, 'output': 'json', 'location': self._coerce_point_to_string(query), } url = self._construct_url(params) logger.debug("%s.reverse: %s", self.__class__.__name__, url) return self._parse_reverse_json( self._call_geocoder(url, timeout=timeout), exactly_one=exactly_one ) def _parse_reverse_json(self, page, exactly_one=True): """ Parses a location from a single-result reverse API call. """ place = page.get('result') if not place: self._check_status(page.get('status')) return None location = place.get('formatted_address').encode('utf-8') latitude = place['location']['lat'] longitude = place['location']['lng'] location = Location(location, (latitude, longitude), place) if exactly_one: return location else: return [location] def _parse_json(self, page, exactly_one=True): """ Returns location, (latitude, longitude) from JSON feed. """ place = page.get('result') 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 GeocoderServiceError( '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 GeocoderAuthenticationFailure( 'No AK' ) elif status == 102: raise GeocoderAuthenticationFailure( 'MCODE Error' ) elif status == 200: raise GeocoderAuthenticationFailure( 'Invalid AK' ) elif status == 211: raise GeocoderAuthenticationFailure( 'Invalid SN' ) elif 200 <= status < 300: raise GeocoderAuthenticationFailure( 'Authentication Failure' ) elif 300 <= status < 500: raise GeocoderQuotaExceeded( 'Quota Error.' ) else: raise GeocoderQueryError('Unknown error. Status: %r' % status) def _construct_url(self, params): query_string = urlencode(params) if self.security_key is None: return "%s?%s" % (self.api, query_string) else: # http://lbsyun.baidu.com/index.php?title=lbscloud/api/appendix raw = "%s?%s%s" % (self.api_path, query_string, self.security_key) sn = hashlib.md5(quote_plus(raw).encode('utf-8')).hexdigest() return "%s?%s&sn=%s" % (self.api, query_string, sn) geopy-1.20.0/geopy/geocoders/banfrance.py0000644000076500000240000001320013446072254020743 0ustar kostyastaff00000000000000from geopy.compat import urlencode from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.util import logger __all__ = ("BANFrance", ) class BANFrance(Geocoder): """Geocoder using the Base Adresse Nationale France API. Documentation at: https://adresse.data.gouv.fr/api .. versionadded:: 1.18.0 """ geocode_path = '/search' reverse_path = '/reverse' def __init__( self, domain='api-adresse.data.gouv.fr', format_string=None, scheme=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, ssl_context=DEFAULT_SENTINEL, ): """ :param str domain: Currently it is ``'api-adresse.data.gouv.fr'``, can be changed for testing purposes. :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. """ super(BANFrance, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) self.domain = domain.strip('/') self.geocode_api = ( '%s://%s%s' % (self.scheme, self.domain, self.geocode_path) ) self.reverse_api = ( '%s://%s%s' % (self.scheme, self.domain, self.reverse_path) ) def geocode( self, query, limit=None, exactly_one=True, timeout=DEFAULT_SENTINEL, ): """ Return a location point by address. :param str query: The address or query you wish to geocode. :param int limit: Defines the maximum number of items in the response structure. If not provided and there are multiple results the BAN API will return 5 results by default. This will be reset to one if ``exactly_one`` is True. :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 bool exactly_one: Return one result or a list of results, if available. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ params = { 'q': self.format_string % query, } if limit is not None: params['limit'] = limit url = "?".join((self.geocode_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=DEFAULT_SENTINEL, ): """ Return an address by location 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 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. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ try: lat, lng = self._coerce_point_to_string(query).split(',') except ValueError: raise ValueError("Must be a coordinate pair or Point") params = { 'lat': lat, 'lng': lng, } 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_feature(feature): # Parse each resource. latitude = feature.get('geometry', {}).get('coordinates', [])[1] longitude = feature.get('geometry', {}).get('coordinates', [])[0] placename = feature.get('properties', {}).get('label') return Location(placename, (latitude, longitude), feature) @classmethod def _parse_json(self, response, exactly_one): if response is None or 'features' not in response: return None features = response['features'] if not len(features): return None if exactly_one: return self._parse_feature(features[0]) else: return [self._parse_feature(feature) for feature in features] geopy-1.20.0/geopy/geocoders/base.py0000644000076500000240000004010713446072254017744 0ustar kostyastaff00000000000000import functools import json import warnings from socket import timeout as SocketTimeout from ssl import SSLError from geopy.compat import ( HTTPError, ProxyHandler, Request, URLError, build_opener_with_context, py3k, string_compare, ) from geopy.exc import ( ConfigurationError, GeocoderAuthenticationFailure, GeocoderInsufficientPrivileges, GeocoderParseError, GeocoderQueryError, GeocoderQuotaExceeded, GeocoderServiceError, GeocoderTimedOut, GeocoderUnavailable, ) from geopy.point import Point from geopy.util import __version__, decode_page, logger __all__ = ( "Geocoder", "options", ) _DEFAULT_USER_AGENT = "geopy/%s" % __version__ class options(object): """The `options` object contains default configuration values for geocoders, e.g. `timeout` and `User-Agent`. Instead of passing a custom value to each geocoder individually, you can override a default value in this object. Please note that not all geocoders use all attributes of this object. For example, some geocoders don't respect the ``default_scheme`` attribute. Refer to the specific geocoder's initializer doc for a list of parameters which that geocoder accepts. Example for overriding default ``timeout`` and ``user_agent``:: >>> import geopy.geocoders >>> from geopy.geocoders import Nominatim >>> geopy.geocoders.options.default_user_agent = 'my_app/1' >>> geopy.geocoders.options.default_timeout = 7 >>> geolocator = Nominatim() >>> print(geolocator.headers) {'User-Agent': 'my_app/1'} >>> print(geolocator.timeout) 7 Attributes: default_format_string String containing ``'%s'`` where the string to geocode should be interpolated before querying the geocoder. Used by `geocode` calls only. For example: ``'%s, Mountain View, CA'``. default_proxies Tunnel requests through HTTP proxy. By default the system proxies are respected (e.g. `HTTP_PROXY` and `HTTPS_PROXY` env vars or platform-specific proxy settings, such as macOS or Windows native preferences -- see :class:`urllib.request.ProxyHandler` for more details). The `proxies` value for using system proxies is ``None``. To disable system proxies and issue requests directly, explicitly pass an empty dict as a value for `proxies`: ``{}``. To use a custom HTTP proxy location, pass a string. Valid examples are: - ``"192.0.2.0:8080"`` - ``"john:passw0rd@192.0.2.0:8080"`` - ``"http://john:passw0rd@192.0.2.0:8080"`` Please note: - Scheme part (``http://``) of the proxy is ignored. - Only `http` proxy is supported. Even if the proxy scheme is `https`, it will be ignored, and the connection between client and proxy would still be unencrypted. However, `https` requests via `http` proxy are still supported (via `HTTP CONNECT` method). Raw urllib-style `proxies` dict might be provided instead of a string: - ``{"https": "192.0.2.0:8080"}`` -- means that HTTP proxy would be used only for requests having `https` scheme. String `proxies` value is automatically used for both schemes, and is provided as a shorthand for the urllib-style `proxies` dict. For more information, see documentation on :class:`urllib.request.ProxyHandler`. .. versionchanged:: 1.15.0 Added support for the string value. default_scheme Use ``'https'`` or ``'http'`` as the API URL's scheme. default_ssl_context An :class:`ssl.SSLContext` instance with custom TLS verification settings. Pass ``None`` to use the interpreter's defaults (starting from Python 2.7.9 and 3.4.3 that is to use the system's trusted CA certificates; the older versions don't support TLS verification completely). For older versions of Python (before 2.7.9 and 3.4.3) this argument is ignored, as `urlopen` doesn't accept an ssl context there, and a warning is issued. To use the CA bundle used by `requests` library:: import ssl import certifi import geopy.geocoders ctx = ssl.create_default_context(cafile=certifi.where()) geopy.geocoders.options.default_ssl_context = ctx To disable TLS certificate verification completely:: import ssl import geopy.geocoders ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE geopy.geocoders.options.default_ssl_context = ctx See docs for the :class:`ssl.SSLContext` class for more examples. default_timeout Time, in seconds, to wait for the geocoding service to respond before raising a :class:`geopy.exc.GeocoderTimedOut` exception. Pass `None` to disable timeout. .. note:: Currently ``None`` as a value is processed correctly only for the ``geopy.geocoders.options.default_timeout`` option value. ``timeout=None`` as a method argument (i.e. ``geocoder.geocode(..., timeout=None)``) would be treated as "use timeout, as set in ``geopy.geocoders.options.default_timeout``", and a deprecation warning would be raised. In geopy 2.0 this will change, so that ``timeout=None`` would actually disable timeout. default_user_agent User-Agent header to send with the requests to geocoder API. """ # Please keep the attributes sorted (Sphinx sorts them in the rendered # docs) and make sure that each attr has a corresponding section in # the docstring above. # # It's bad to have the attrs docs separated from the attrs # themselves. Although Sphinx supports docstrings for each attr [1], # this is not standardized and won't work with `help()` function and # in the ReadTheDocs (at least out of the box) [2]. # # [1]: http://www.sphinx-doc.org/en/master/ext/autodoc.html#directive-autoattribute # [2]: https://github.com/rtfd/readthedocs.org/issues/855#issuecomment-261337038 default_format_string = '%s' default_proxies = None default_scheme = 'https' default_ssl_context = None default_timeout = 1 default_user_agent = _DEFAULT_USER_AGENT # Create an object which `repr` returns 'DEFAULT_SENTINEL'. Sphinx (docs) uses # this value when generating method's signature. DEFAULT_SENTINEL = type('object', (object,), {'__repr__': lambda self: 'DEFAULT_SENTINEL'})() ERROR_CODE_MAP = { 400: GeocoderQueryError, 401: GeocoderAuthenticationFailure, 402: GeocoderQuotaExceeded, 403: GeocoderInsufficientPrivileges, 407: GeocoderAuthenticationFailure, 412: GeocoderQueryError, 413: GeocoderQueryError, 414: GeocoderQueryError, 429: GeocoderQuotaExceeded, 502: GeocoderServiceError, 503: GeocoderTimedOut, 504: GeocoderTimedOut } class Geocoder(object): """ Template object for geocoders. """ def __init__( self, format_string=None, scheme=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, ssl_context=DEFAULT_SENTINEL, ): """ Mostly-common geocoder validation, proxies, &c. Not all geocoders specify format_string and such. """ self.format_string = format_string or options.default_format_string self.scheme = scheme or options.default_scheme if self.scheme not in ('http', 'https'): raise ConfigurationError( 'Supported schemes are `http` and `https`.' ) self.timeout = (timeout if timeout is not DEFAULT_SENTINEL else options.default_timeout) self.proxies = (proxies if proxies is not DEFAULT_SENTINEL else options.default_proxies) self.headers = {'User-Agent': user_agent or options.default_user_agent} self.ssl_context = (ssl_context if ssl_context is not DEFAULT_SENTINEL else options.default_ssl_context) if isinstance(self.proxies, string_compare): self.proxies = {'http': self.proxies, 'https': self.proxies} # `ProxyHandler` should be present even when actually there're # no proxies. `build_opener` contains it anyway. By specifying # it here explicitly we can disable system proxies (i.e. # from HTTP_PROXY env var) by setting `self.proxies` to `{}`. # Otherwise, if we didn't specify ProxyHandler for empty # `self.proxies` here, build_opener would have used one internally # which could have unwillingly picked up the system proxies. opener = build_opener_with_context( self.ssl_context, ProxyHandler(self.proxies), ) self.urlopen = opener.open @staticmethod def _coerce_point_to_string(point, output_format="%(lat)s,%(lon)s"): """ Do the right thing on "point" input. For geocoders with reverse methods. """ try: if not isinstance(point, Point): point = Point(point) except ValueError as e: if isinstance(point, string_compare): warnings.warn( 'Unable to parse the string as Point: "%s". Using the value ' 'as-is for the query. In geopy 2.0 this will become an ' 'exception.' % str(e), DeprecationWarning, stacklevel=3 ) return point raise else: # Altitude is silently dropped. return output_format % dict(lat=point.latitude, lon=point.longitude) @staticmethod def _format_bounding_box(bbox, output_format="%(lat1)s,%(lon1)s,%(lat2)s,%(lon2)s"): """ Transform bounding box boundaries to a string matching `output_format` from the following formats: - [Point(lat1, lon1), Point(lat2, lon2)] - [[lat1, lon1], [lat2, lon2]] - ["lat1,lon1", "lat2,lon2"] It is guaranteed that lat1 <= lat2 and lon1 <= lon2. """ if len(bbox) != 2: raise GeocoderQueryError("Unsupported format for a bounding box") p1, p2 = bbox p1, p2 = Point(p1), Point(p2) return output_format % dict(lat1=min(p1.latitude, p2.latitude), lon1=min(p1.longitude, p2.longitude), lat2=max(p1.latitude, p2.latitude), lon2=max(p1.longitude, p2.longitude)) def _geocoder_exception_handler(self, error, message): """ Geocoder-specific exceptions handler. Override if custom exceptions processing is needed. For example, raising an appropriate GeocoderQuotaExceeded on non-200 response with a textual message in the body about the exceeded quota. """ pass def _call_geocoder( self, url, timeout=DEFAULT_SENTINEL, raw=False, requester=None, deserializer=json.loads, **kwargs ): """ For a generated query URL, get the results. """ if requester: req = url # Don't construct an urllib's Request for a custom requester. # `requester` might be anything which can issue an HTTP request. # Assume that `requester` is a method of the `requests` library. # Requests, however, doesn't accept SSL context in its HTTP # request methods. A custom HTTP adapter has to be created for that. # So the current usage is not directly compatible with `requests`. requester = functools.partial(requester, context=self.ssl_context, proxies=self.proxies, headers=self.headers) else: if isinstance(url, Request): # copy Request headers = self.headers.copy() headers.update(url.header_items()) req = Request(url=url.get_full_url(), headers=headers) else: req = Request(url=url, headers=self.headers) requester = requester or self.urlopen if timeout is None: warnings.warn( ('`timeout=None` has been passed to a geocoder call. Using ' 'default geocoder timeout. In geopy 2.0 the ' 'behavior will be different: None will mean "no timeout" ' 'instead of "default geocoder timeout". Pass ' 'geopy.geocoders.base.DEFAULT_SENTINEL instead of None ' 'to get rid of this warning.'), DeprecationWarning, stacklevel=3) timeout = DEFAULT_SENTINEL timeout = (timeout if timeout is not DEFAULT_SENTINEL else self.timeout) try: page = requester(req, timeout=timeout, **kwargs) except Exception as error: message = ( str(error) if not py3k else ( str(error.args[0]) if len(error.args) else str(error) ) ) self._geocoder_exception_handler(error, message) if isinstance(error, HTTPError): code = error.getcode() body = self._read_http_error_body(error) if body: logger.info('Received an HTTP error (%s): %s', code, body, exc_info=False) 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 _read_http_error_body(self, error): try: return decode_page(error) except Exception: logger.debug('Unable to fetch body for a non-successful HTTP response', exc_info=True) return None def geocode(self, query, exactly_one=True, timeout=DEFAULT_SENTINEL): """ Implemented in subclasses. """ raise NotImplementedError() def reverse(self, query, exactly_one=True, timeout=DEFAULT_SENTINEL): """ Implemented in subclasses. """ raise NotImplementedError() geopy-1.20.0/geopy/geocoders/bing.py0000644000076500000240000002221013446072254017744 0ustar kostyastaff00000000000000from geopy.compat import quote, urlencode from geopy.exc import ( GeocoderAuthenticationFailure, GeocoderInsufficientPrivileges, GeocoderQuotaExceeded, GeocoderServiceError, GeocoderUnavailable, ) from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.util import join_filter, logger __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', } geocode_path = '/REST/v1/Locations' reverse_path = '/REST/v1/Locations/%(point)s' def __init__( self, api_key, format_string=None, scheme=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, ssl_context=DEFAULT_SENTINEL, ): """ :param str api_key: Should be a valid Bing Maps API key (https://www.microsoft.com/en-us/maps/create-a-bing-maps-key). :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. .. versionadded:: 1.12.0 :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. .. versionadded:: 1.14.0 """ super(Bing, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) self.api_key = api_key domain = 'dev.virtualearth.net' self.geocode_api = '%s://%s%s' % (self.scheme, domain, self.geocode_path) self.reverse_api = '%s://%s%s' % (self.scheme, domain, self.reverse_path) def geocode( self, query, exactly_one=True, user_location=None, timeout=DEFAULT_SENTINEL, culture=None, include_neighborhood=None, include_country_code=False ): """ Return a location point by address. :param str 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. :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. :param str culture: Affects the language of the response, must be a two-letter country code. .. versionadded:: 1.4.0 :param bool include_neighborhood: Sets whether to include the neighborhood field in the response. .. versionadded:: 1.4.0 :param bool 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 :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ 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: 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.geocode_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=DEFAULT_SENTINEL, culture=None, include_country_code=False ): """ Return an address by location 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 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 str culture: Affects the language of the response, must be a two-letter country code. :param bool include_country_code: Sets whether to include the two-letter ISO code of the country in the response (field name 'countryRegionIso2'). :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ point = self._coerce_point_to_string(query) params = {'key': self.api_key} if culture: params['culture'] = culture if include_country_code: params['include'] = 'ciso2' # the only acceptable value quoted_point = quote(point.encode('utf-8')) url = "?".join((self.reverse_api % dict(point=quoted_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): """ 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): 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.20.0/geopy/geocoders/databc.py0000644000076500000240000001143113446072254020246 0ustar kostyastaff00000000000000from geopy.compat import urlencode from geopy.exc import GeocoderQueryError from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder 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 """ geocode_path = '/pub/geocoder/addresses.geojson' def __init__( self, scheme=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, format_string=None, ssl_context=DEFAULT_SENTINEL, ): """ :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. .. versionadded:: 1.12.0 :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. .. versionadded:: 1.14.0 :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. .. versionadded:: 1.14.0 """ super(DataBC, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) domain = 'apps.gov.bc.ca' self.api = '%s://%s%s' % (self.scheme, domain, self.geocode_path) def geocode( self, query, max_results=25, set_back=0, location_descriptor='any', exactly_one=True, timeout=DEFAULT_SENTINEL, ): """ Return a location point by address. :param str 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 str 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. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ params = {'addressString': self.format_string % 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: 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: 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.20.0/geopy/geocoders/geocodeearth.py0000644000076500000240000000545413446072517021473 0ustar kostyastaff00000000000000from geopy.geocoders.base import DEFAULT_SENTINEL from geopy.geocoders.pelias import Pelias __all__ = ("GeocodeEarth", ) class GeocodeEarth(Pelias): """geocode.earth, a Pelias-based service provided by the developers of Pelias itself. .. versionadded:: 1.15.0 """ def __init__( self, api_key, format_string=None, boundary_rect=None, country_bias=None, domain='api.geocode.earth', timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, scheme=None, ssl_context=DEFAULT_SENTINEL, ): """ :param str api_key: Geocode.earth API key, required. :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. :type boundary_rect: list or tuple of 2 items of :class:`geopy.point.Point` or ``(latitude, longitude)`` or ``"%(latitude)s, %(longitude)s"``. :param boundary_rect: Coordinates to restrict search within. Example: ``[Point(22, 180), Point(-22, -180)]``. .. versionchanged:: 1.17.0 Previously boundary_rect could be a list of 4 strings or numbers in the format of ``[longitude, latitude, longitude, latitude]``. This format is now deprecated in favor of a list/tuple of a pair of geopy Points and will be removed in geopy 2.0. .. deprecated:: 1.19.0 This argument will be removed in geopy 2.0. Use `geocode`'s `boundary_rect` instead. :param str country_bias: Bias results to this country (ISO alpha-3). .. deprecated:: 1.19.0 This argument will be removed in geopy 2.0. Use `geocode`'s `country_bias` instead. :param str domain: Specify a custom domain for Pelias API. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. """ super(GeocodeEarth, self).__init__( api_key=api_key, format_string=format_string, boundary_rect=boundary_rect, country_bias=country_bias, domain=domain, timeout=timeout, proxies=proxies, user_agent=user_agent, scheme=scheme, ssl_context=ssl_context, ) geopy-1.20.0/geopy/geocoders/geocodefarm.py0000644000076500000240000001570213446072254021310 0ustar kostyastaff00000000000000from geopy.compat import urlencode from geopy.exc import ( GeocoderAuthenticationFailure, GeocoderQuotaExceeded, GeocoderServiceError, ) from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.util import logger __all__ = ("GeocodeFarm", ) class GeocodeFarm(Geocoder): """Geocoder using the GeocodeFarm API. Documentation at: https://www.geocode.farm/geocoding/free-api-documentation/ """ geocode_path = '/v3/json/forward/' reverse_path = '/v3/json/reverse/' def __init__( self, api_key=None, format_string=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, ssl_context=DEFAULT_SENTINEL, scheme=None, ): """ :param str api_key: (optional) The API key required by GeocodeFarm to perform geocoding requests. :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. .. versionadded:: 1.12.0 :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. .. versionadded:: 1.14.0 :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. .. versionadded:: 1.14.0 """ super(GeocodeFarm, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) self.api_key = api_key domain = 'www.geocode.farm' self.api = ( "%s://%s%s" % (self.scheme, domain, self.geocode_path) ) self.reverse_api = ( "%s://%s%s" % (self.scheme, domain, self.reverse_path) ) def geocode(self, query, exactly_one=True, timeout=DEFAULT_SENTINEL): """ Return a location point by address. :param str 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. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ 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=DEFAULT_SENTINEL): """ Return an address by location 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 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. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ try: lat, lon = 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): # TODO make this a private API # 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 placename is None: placename = result.get('formatted_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) if "NO_RESULTS" in geocoding_results.get("STATUS", {}).get("status", ""): return None places = self.parse_code(geocoding_results) if exactly_one: 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", {}) if "NO_RESULTS" in status_result.get("status", ""): return 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.20.0/geopy/geocoders/geolake.py0000644000076500000240000001227413446072517020447 0ustar kostyastaff00000000000000from geopy.compat import string_compare, urlencode from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.util import logger __all__ = ("Geolake", ) class Geolake(Geocoder): """Geocoder using the Geolake API. Documentation at: https://geolake.com/docs/api Terms of Service at: https://geolake.com/terms-of-use .. versionadded:: 1.18.0 """ structured_query_params = { 'country', 'state', 'city', 'zipcode', 'street', 'address', 'houseNumber', 'subNumber', } api_path = '/v1/geocode' def __init__( self, api_key, domain='api.geolake.com', scheme=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, format_string=None, ssl_context=DEFAULT_SENTINEL, ): """ :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. :param str api_key: The API key required by Geolake to perform geocoding requests. You can get your key here: https://geolake.com/ :param str domain: Currently it is ``'api.geolake.com'``, can be changed for testing purposes. :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. """ super(Geolake, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) self.api_key = api_key self.domain = domain.strip('/') self.api = '%s://%s%s' % (self.scheme, self.domain, self.api_path) def geocode( self, query, country_codes=None, exactly_one=True, timeout=DEFAULT_SENTINEL, ): """ Return a location point by address. :param str query: The address or query you wish to geocode. For a structured query, provide a dictionary whose keys are one of: `country`, `state`, `city`, `zipcode`, `street`, `address`, `houseNumber` or `subNumber`. :param country_codes: Provides the geocoder with a list of country codes that the query may reside in. This value will limit the geocoder to the supplied countries. The country code is a 2 character code as defined by the ISO-3166-1 alpha-2 standard (e.g. ``FR``). Multiple countries can be specified with a Python list. .. versionchanged:: 1.19.0 Previously only a Python list of countries could be specified. Now a single country as a string can be specified as well. :type country_codes: str or list :param bool exactly_one: Return one result or a list of 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. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ if isinstance(query, dict): params = { key: val for key, val in query.items() if key in self.structured_query_params } params['api_key'] = self.api_key else: params = { 'api_key': self.api_key, 'q': self.format_string % query, } if not country_codes: country_codes = [] if isinstance(country_codes, string_compare): country_codes = [country_codes] if country_codes: params['countryCodes'] = ",".join(country_codes) 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 _parse_json(self, page, exactly_one): """Returns location, (latitude, longitude) from json feed.""" if not page.get('success'): return None latitude = page['latitude'] longitude = page['longitude'] place = page.get('place') address = ", ".join([place['city'], place['countryCode']]) result = Location(address, (latitude, longitude), page) if exactly_one: return result else: return [result] geopy-1.20.0/geopy/geocoders/geonames.py0000644000076500000240000003500413446072517020632 0ustar kostyastaff00000000000000import warnings from geopy.compat import string_compare, urlencode from geopy.exc import ( ConfigurationError, GeocoderAuthenticationFailure, GeocoderInsufficientPrivileges, GeocoderQueryError, GeocoderQuotaExceeded, GeocoderServiceError, ) from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.timezone import ( ensure_pytz_is_installed, from_fixed_gmt_offset, from_timezone_name, ) from geopy.util import logger __all__ = ("GeoNames", ) class GeoNames(Geocoder): """GeoNames geocoder. Documentation at: http://www.geonames.org/export/geonames-search.html Reverse geocoding documentation at: http://www.geonames.org/export/web-services.html#findNearbyPlaceName """ geocode_path = '/searchJSON' reverse_path = '/findNearbyPlaceNameJSON' reverse_nearby_path = '/findNearbyJSON' timezone_path = '/timezoneJSON' def __init__( self, country_bias=None, username=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, format_string=None, ssl_context=DEFAULT_SENTINEL, scheme='http', ): """ :param str country_bias: Records from the country_bias are listed first. Two letter country code ISO-3166. .. deprecated:: 1.19.0 This argument will be removed in geopy 2.0. Use `geocode`'s `country_bias` instead. :param str username: GeoNames username, required. Sign up here: http://www.geonames.org/login :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. .. versionadded:: 1.12.0 :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. .. versionadded:: 1.14.0 :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. .. versionadded:: 1.14.0 :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. Note that at the time of writing GeoNames doesn't support `https`, so the default scheme is `http`. The value of :attr:`geopy.geocoders.options.default_scheme` is not respected. This parameter is present to make it possible to switch to `https` once GeoNames adds support for it. .. versionadded:: 1.18.0 """ super(GeoNames, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) if username is 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 if country_bias is not None: warnings.warn( '`country_bias` argument of the %(cls)s.__init__ ' 'is deprecated and will be removed in geopy 2.0. Use ' '%(cls)s.geocode(country_bias=%(value)r) instead.' % dict(cls=type(self).__name__, value=country_bias), DeprecationWarning, stacklevel=2 ) self.country_bias = country_bias domain = 'api.geonames.org' self.api = ( "%s://%s%s" % (self.scheme, domain, self.geocode_path) ) self.api_reverse = ( "%s://%s%s" % (self.scheme, domain, self.reverse_path) ) self.api_reverse_nearby = ( "%s://%s%s" % (self.scheme, domain, self.reverse_nearby_path) ) self.api_timezone = ( "%s://%s%s" % (self.scheme, domain, self.timezone_path) ) def geocode( self, query, exactly_one=True, timeout=DEFAULT_SENTINEL, country=None, country_bias=None, ): """ Return a location point by address. :param str 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 country: Limit records to the specified countries. Two letter country code ISO-3166 (e.g. ``FR``). Might be a single string or a list of strings. .. versionadded:: 1.19.0 :type country: str or list :param str country_bias: Records from the country_bias are listed first. Two letter country code ISO-3166. .. versionadded:: 1.19.0 :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ params = [ ('q', self.format_string % query), ('username', self.username), ] if country_bias is None: country_bias = self.country_bias if country_bias: params.append(('countryBias', country_bias)) if not country: country = [] if isinstance(country, string_compare): country = [country] for country_item in country: params.append(('country', country_item)) if exactly_one: params.append(('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=DEFAULT_SENTINEL, timeout=DEFAULT_SENTINEL, feature_code=None, lang=None, find_nearby_type='findNearbyPlaceName', ): """ Return an address by location point. .. versionadded:: 1.2.0 :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. .. versionchanged:: 1.14.0 Default value for ``exactly_one`` was ``False``, which differs from the conventional default across geopy. Please always pass this argument explicitly, otherwise you would get a warning. In geopy 2.0 the default value will become ``True``. :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 str feature_code: A GeoNames feature code .. versionadded:: 1.18.0 :param str lang: language of the returned ``name`` element (the pseudo language code 'local' will return it in local language) Full list of supported languages can be found here: https://www.geonames.org/countries/ .. versionadded:: 1.18.0 :param str find_nearby_type: A flag to switch between different GeoNames API endpoints. The default value is ``findNearbyPlaceName`` which returns the closest populated place. Another currently implemented option is ``findNearby`` which returns the closest toponym for the lat/lng query. .. versionadded:: 1.18.0 :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ if exactly_one is DEFAULT_SENTINEL: warnings.warn('%s.reverse: default value for `exactly_one` ' 'argument will become True in geopy 2.0. ' 'Specify `exactly_one=False` as the argument ' 'explicitly to get rid of this warning.' % type(self).__name__, DeprecationWarning, stacklevel=2) exactly_one = False try: lat, lng = self._coerce_point_to_string(query).split(',') except ValueError: raise ValueError("Must be a coordinate pair or Point") if find_nearby_type == 'findNearbyPlaceName': # default if feature_code: raise ValueError( "find_nearby_type=findNearbyPlaceName doesn't support " "the `feature_code` param" ) params = self._reverse_find_nearby_place_name_params( lat=lat, lng=lng, lang=lang, ) url = "?".join((self.api_reverse, urlencode(params))) elif find_nearby_type == 'findNearby': if lang: raise ValueError( "find_nearby_type=findNearby doesn't support the `lang` param" ) params = self._reverse_find_nearby_params( lat=lat, lng=lng, feature_code=feature_code, ) url = "?".join((self.api_reverse_nearby, urlencode(params))) else: raise GeocoderQueryError( '`%s` find_nearby_type is not supported by geopy' % find_nearby_type ) logger.debug("%s.reverse: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) def _reverse_find_nearby_params(self, lat, lng, feature_code): params = { 'lat': lat, 'lng': lng, 'username': self.username, } if feature_code: params['featureCode'] = feature_code return params def _reverse_find_nearby_place_name_params(self, lat, lng, lang): params = { 'lat': lat, 'lng': lng, 'username': self.username, } if lang: params['lang'] = lang return params def reverse_timezone(self, query, timeout=DEFAULT_SENTINEL): """ Find the timezone for a point in `query`. GeoNames always returns a timezone: if the point being queried doesn't have an assigned Olson timezone id, a ``pytz.FixedOffset`` timezone is used to produce the :class:`geopy.timezone.Timezone`. .. versionadded:: 1.18.0 :param query: The coordinates for which you want a timezone. :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. :rtype: :class:`geopy.timezone.Timezone` """ ensure_pytz_is_installed() try: lat, lng = 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_timezone, urlencode(params))) logger.debug("%s.reverse_timezone: %s", self.__class__.__name__, url) return self._parse_json_timezone( self._call_geocoder(url, timeout=timeout) ) def _raise_for_error(self, body): err = body.get('status') if err: code = err['value'] message = err['message'] # http://www.geonames.org/export/webservice-exception.html if message.startswith("user account not enabled to use"): raise GeocoderInsufficientPrivileges(message) if code == 10: raise GeocoderAuthenticationFailure(message) if code in (18, 19, 20): raise GeocoderQuotaExceeded(message) raise GeocoderServiceError(message) def _parse_json_timezone(self, response): self._raise_for_error(response) timezone_id = response.get("timezoneId") if timezone_id is None: # Sometimes (e.g. for Antarctica) GeoNames doesn't return # a `timezoneId` value, but it returns GMT offsets. # Apparently GeoNames always returns these offsets -- for # every single point on the globe. raw_offset = response["rawOffset"] return from_fixed_gmt_offset(raw_offset, raw=response) else: return from_timezone_name(timezone_id, raw=response) def _parse_json(self, doc, exactly_one): """ Parse JSON response body. """ places = doc.get('geonames', []) self._raise_for_error(doc) 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('adminName1', None) country = place.get('countryName', 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.20.0/geopy/geocoders/googlev3.py0000644000076500000240000004665713446340735020601 0ustar kostyastaff00000000000000import base64 import hashlib import hmac import warnings from calendar import timegm from datetime import datetime from numbers import Number from geopy.compat import urlencode from geopy.exc import ConfigurationError, GeocoderQueryError, GeocoderQuotaExceeded from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.timezone import ensure_pytz_is_installed, from_timezone_name from geopy.util import logger __all__ = ("GoogleV3", ) class GoogleV3(Geocoder): """Geocoder using the Google Maps v3 API. Documentation at: https://developers.google.com/maps/documentation/geocoding/ .. attention:: Since July 2018 Google requires each request to have an API key. See https://developers.google.com/maps/documentation/geocoding/usage-and-billing """ api_path = '/maps/api/geocode/json' timezone_path = '/maps/api/timezone/json' def __init__( self, api_key=None, domain='maps.googleapis.com', scheme=None, client_id=None, secret_key=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, format_string=None, ssl_context=DEFAULT_SENTINEL, channel='', ): """ :param str 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). Make sure to have both ``Geocoding API`` and ``Time Zone API`` services enabled for this API key. :param str 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 str scheme: See :attr:`geopy.geocoders.options.default_scheme`. :param str client_id: If using premier, the account client id. :param str secret_key: If using premier, the account secret key. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. .. versionadded:: 1.12.0 :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. .. versionadded:: 1.14.0 :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. .. versionadded:: 1.14.0 :param str channel: If using premier, the channel identifier. .. versionadded:: 1.12.0 """ super(GoogleV3, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) 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.premier = bool(client_id and secret_key) self.client_id = client_id self.secret_key = secret_key if not self.premier and not api_key: warnings.warn( 'Since July 2018 Google requires each request to have an API key. ' 'Pass a valid `api_key` to GoogleV3 geocoder to hide this warning. ' 'See https://developers.google.com/maps/documentation/geocoding/usage-and-billing', # noqa UserWarning, stacklevel=2 ) self.api_key = api_key self.domain = domain.strip('/') self.channel = channel self.api = '%s://%s%s' % (self.scheme, self.domain, self.api_path) self.tz_api = '%s://%s%s' % (self.scheme, self.domain, self.timezone_path) 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 if self.channel: params['channel'] = self.channel path = "?".join((self.api_path, 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()) ) def geocode( self, query=None, exactly_one=True, timeout=DEFAULT_SENTINEL, bounds=None, region=None, components=None, place_id=None, language=None, sensor=False, ): """ Return a location point by address. :param str query: The address or query you wish to geocode. Optional, if ``components`` param is set:: >>> g.geocode(components={"city": "Paris", "country": "FR"}) Location(France, (46.227638, 2.213749, 0.0)) .. versionchanged:: 1.14.0 Now ``query`` is optional if ``components`` param is set. :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. :type bounds: list or tuple of 2 items of :class:`geopy.point.Point` or ``(latitude, longitude)`` or ``"%(latitude)s, %(longitude)s"``. :param bounds: The bounding box of the viewport within which to bias geocode results more prominently. Example: ``[Point(22, 180), Point(-22, -180)]``. .. versionchanged:: 1.17.0 Previously the only supported format for bounds was a list like ``[latitude, longitude, latitude, longitude]``. This format is now deprecated in favor of a list/tuple of a pair of geopy Points and will be removed in geopy 2.0. :param str 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. :param str place_id: Retrieve a Location using a Place ID. Cannot be not used with ``query`` or ``bounds`` parameters. >>> g.geocode(place_id='ChIJOcfP0Iq2j4ARDrXUa7ZWs34') .. versionadded:: 1.19.0 :param str language: The language in which to return results. :param bool sensor: Whether the geocoding request comes from a device with a location sensor. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ params = { 'sensor': str(sensor).lower() } if place_id and (bounds or query): raise ValueError( 'Only one of the `query` or `place id` or `bounds` ' ' parameters must be entered.') if place_id is not None: params['place_id'] = place_id if query is not None: params['address'] = self.format_string % query if query is None and place_id is None and not components: raise ValueError('Either `query` or `components` or `place_id` ' 'must be set.') if self.api_key: params['key'] = self.api_key if bounds: if len(bounds) == 4: warnings.warn( 'GoogleV3 `bounds` format of ' '`[latitude, longitude, latitude, longitude]` is now ' 'deprecated and will not be supported in geopy 2.0. ' 'Use `[Point(latitude, longitude), Point(latitude, longitude)]` ' 'instead.', DeprecationWarning, stacklevel=2 ) lat1, lon1, lat2, lon2 = bounds bounds = [[lat1, lon1], [lat2, lon2]] params['bounds'] = self._format_bounding_box( bounds, "%(lat1)s,%(lon1)s|%(lat2)s,%(lon2)s") if region: params['region'] = region if components: params['components'] = self._format_components_param(components) if language: params['language'] = language if self.premier: url = self._get_signed_url(params) else: 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=DEFAULT_SENTINEL, timeout=DEFAULT_SENTINEL, language=None, sensor=False, ): """ Return an address by location 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 of results, if available. .. versionchanged:: 1.14.0 Default value for ``exactly_one`` was ``False``, which differs from the conventional default across geopy. Please always pass this argument explicitly, otherwise you would get a warning. In geopy 2.0 the default value will become ``True``. :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 str language: The language in which to return results. :param bool sensor: Whether the geocoding request comes from a device with a location sensor. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ if exactly_one is DEFAULT_SENTINEL: warnings.warn('%s.reverse: default value for `exactly_one` ' 'argument will become True in geopy 2.0. ' 'Specify `exactly_one=False` as the argument ' 'explicitly to get rid of this warning.' % type(self).__name__, DeprecationWarning, stacklevel=2) exactly_one = False 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=DEFAULT_SENTINEL): """ Find the timezone a `location` was in for a specified `at_time`, and return a pytz timezone object. .. versionadded:: 1.2.0 .. deprecated:: 1.18.0 Use :meth:`GoogleV3.reverse_timezone` instead. This method will be removed in geopy 2.0. .. versionchanged:: 1.18.1 Previously a :class:`KeyError` was raised for a point without an assigned Olson timezone id (e.g. for Antarctica). Now this method returns None for such requests. :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. Timezone-aware datetimes are correctly handled and naive datetimes are silently treated as UTC. .. versionchanged:: 1.18.0 Previously this parameter accepted raw unix timestamp as int or float. This is now deprecated in favor of datetimes and support for numbers will be removed in geopy 2.0. :type at_time: :class:`datetime.datetime` or None :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. :rtype: ``None`` or pytz timezone. See :func:`pytz.timezone`. """ warnings.warn('%(cls)s.timezone method is deprecated in favor of ' '%(cls)s.reverse_timezone, which returns geopy.Timezone ' 'object containing pytz timezone and a raw response ' 'instead of just pytz timezone. This method will ' 'be removed in geopy 2.0.' % dict(cls=type(self).__name__), DeprecationWarning, stacklevel=2) timezone = self.reverse_timezone(location, at_time, timeout) if timezone is None: return None return timezone.pytz_timezone def reverse_timezone(self, query, at_time=None, timeout=DEFAULT_SENTINEL): """ Find the timezone a point in `query` was in for a specified `at_time`. .. versionadded:: 1.18.0 .. versionchanged:: 1.18.1 Previously a :class:`KeyError` was raised for a point without an assigned Olson timezone id (e.g. for Antarctica). Now this method returns None for such requests. :param query: The coordinates for which you want a timezone. :type query: :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. Timezone-aware datetimes are correctly handled and naive datetimes are silently treated as UTC. :type at_time: :class:`datetime.datetime` or None :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. :rtype: ``None`` or :class:`geopy.timezone.Timezone` """ ensure_pytz_is_installed() location = self._coerce_point_to_string(query) timestamp = self._normalize_timezone_at_time(at_time) params = { "location": location, "timestamp": timestamp, } if self.api_key: params['key'] = self.api_key url = "?".join((self.tz_api, urlencode(params))) logger.debug("%s.reverse_timezone: %s", self.__class__.__name__, url) return self._parse_json_timezone( self._call_geocoder(url, timeout=timeout) ) def _parse_json_timezone(self, response): status = response.get('status') if status != 'OK': self._check_status(status) timezone_id = response.get("timeZoneId") if timezone_id is None: # Google returns `status: ZERO_RESULTS` for uncovered # points (e.g. for Antarctica), so there's nothing # meaningful to be returned as the `raw` response, # hence we return `None`. return None return from_timezone_name(timezone_id, raw=response) def _normalize_timezone_at_time(self, at_time): if at_time is None: timestamp = timegm(datetime.utcnow().utctimetuple()) elif isinstance(at_time, Number): warnings.warn( 'Support for `at_time` as int/float is deprecated ' 'and will be removed in geopy 2.0. ' 'Pass a `datetime.datetime` instance instead.', DeprecationWarning, stacklevel=3 ) timestamp = at_time elif isinstance(at_time, datetime): # Naive datetimes are silently treated as UTC. # Timezone-aware datetimes are handled correctly. timestamp = timegm(at_time.utctimetuple()) else: raise GeocoderQueryError( "`at_time` must be an instance of `datetime.datetime`" ) return timestamp 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.20.0/geopy/geocoders/here.py0000644000076500000240000003035713446072254017763 0ustar kostyastaff00000000000000""" :class:`.Here` geocoder. """ from geopy.compat import urlencode from geopy.exc import ( GeocoderAuthenticationFailure, GeocoderInsufficientPrivileges, GeocoderQuotaExceeded, GeocoderServiceError, GeocoderUnavailable, ) from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.util import join_filter, logger __all__ = ("Here", ) class Here(Geocoder): """Geocoder using the HERE Geocoder API. Documentation at: https://developer.here.com/documentation/geocoder/ .. versionadded:: 1.15.0 """ structured_query_params = { 'city', 'county', 'district', 'country', 'state', 'street', 'housenumber', 'postalcode', } geocode_path = '/6.2/geocode.json' reverse_path = '/6.2/reversegeocode.json' def __init__( self, app_id, app_code, format_string=None, scheme=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, ssl_context=DEFAULT_SENTINEL, ): """ :param str app_id: Should be a valid HERE Maps APP ID See https://developer.here.com/authenticationpage. :param str app_code: Should be a valid HERE Maps APP CODE See https://developer.here.com/authenticationpage. :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. """ super(Here, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) self.app_id = app_id self.app_code = app_code self.api = "%s://geocoder.api.here.com%s" % (self.scheme, self.geocode_path) self.reverse_api = ( "%s://reverse.geocoder.api.here.com%s" % (self.scheme, self.reverse_path) ) def geocode( self, query, bbox=None, mapview=None, exactly_one=True, maxresults=None, pageinformation=None, language=None, additional_data=False, timeout=DEFAULT_SENTINEL ): """ Return a location point by address. This implementation supports only a subset of all available parameters. A list of all parameters of the pure REST API is available here: https://developer.here.com/documentation/geocoder/topics/resource-geocode.html :param str query: The address or query you wish to geocode. For a structured query, provide a dictionary whose keys are one of: `city`, `county`, `district`, `country`, `state`, `street`, `housenumber`, or `postalcode`. :param bbox: A type of spatial filter, limits the search for any other attributes in the request. Specified by two coordinate (lat/lon) pairs -- corners of the box. `The bbox search is currently similar to mapview but it is not extended` (cited from the REST API docs). Relevant global results are also returned. Example: ``[Point(22, 180), Point(-22, -180)]``. :type bbox: list or tuple of 2 items of :class:`geopy.point.Point` or ``(latitude, longitude)`` or ``"%(latitude)s, %(longitude)s"``. :param mapview: The app's viewport, given as two coordinate pairs, specified by two lat/lon pairs -- corners of the bounding box, respectively. Matches from within the set map view plus an extended area are ranked highest. Relevant global results are also returned. Example: ``[Point(22, 180), Point(-22, -180)]``. :type mapview: list or tuple of 2 items of :class:`geopy.point.Point` or ``(latitude, longitude)`` or ``"%(latitude)s, %(longitude)s"``. :param bool exactly_one: Return one result or a list of results, if available. :param int maxresults: Defines the maximum number of items in the response structure. If not provided and there are multiple results the HERE API will return 10 results by default. This will be reset to one if ``exactly_one`` is True. :param int pageinformation: A key which identifies the page to be returned when the response is separated into multiple pages. Only useful when ``maxresults`` is also provided. :param str language: Affects the language of the response, must be a RFC 4647 language code, e.g. 'en-US'. :param str additional_data: A string with key-value pairs as described on https://developer.here.com/documentation/geocoder/topics/resource-params-additional.html. These will be added as one query parameter to the URL. :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. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ if isinstance(query, dict): params = { key: val for key, val in query.items() if key in self.structured_query_params } params['app_id'] = self.app_id params['app_code'] = self.app_code else: params = { 'searchtext': self.format_string % query, 'app_id': self.app_id, 'app_code': self.app_code } if bbox: params['bbox'] = self._format_bounding_box( bbox, "%(lat2)s,%(lon1)s;%(lat1)s,%(lon2)s") if mapview: params['mapview'] = self._format_bounding_box( mapview, "%(lat2)s,%(lon1)s;%(lat1)s,%(lon2)s") if pageinformation: params['pageinformation'] = pageinformation if maxresults: params['maxresults'] = maxresults if exactly_one: params['maxresults'] = 1 if language: params['language'] = language if additional_data: params['additionaldata'] = additional_data 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, radius=None, exactly_one=True, maxresults=None, pageinformation=None, language=None, mode='retrieveAddresses', timeout=DEFAULT_SENTINEL ): """ Return an address by location point. This implementation supports only a subset of all available parameters. A list of all parameters of the pure REST API is available here: https://developer.here.com/documentation/geocoder/topics/resource-reverse-geocode.html :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 float radius: Proximity radius in meters. :param bool exactly_one: Return one result or a list of results, if available. :param int maxresults: Defines the maximum number of items in the response structure. If not provided and there are multiple results the HERE API will return 10 results by default. This will be reset to one if ``exactly_one`` is True. :param int pageinformation: A key which identifies the page to be returned when the response is separated into multiple pages. Only useful when ``maxresults`` is also provided. :param str language: Affects the language of the response, must be a RFC 4647 language code, e.g. 'en-US'. :param str mode: Affects the type of returned response items, must be one of: 'retrieveAddresses' (default), 'retrieveAreas', 'retrieveLandmarks', 'retrieveAll', or 'trackPosition'. See online documentation for more information. :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. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ point = self._coerce_point_to_string(query) params = { 'app_id': self.app_id, 'app_code': self.app_code, 'mode': mode, 'prox': point, } if radius is not None: params['prox'] = '%s,%s' % (params['prox'], float(radius)) if pageinformation: params['pageinformation'] = pageinformation if maxresults: params['maxresults'] = maxresults if exactly_one: params['maxresults'] = 1 if language: params['language'] = language url = "%s?%s" % (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_json(doc, exactly_one=True): """ 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) try: resources = doc['Response']['View'][0]['Result'] except IndexError: resources = None if not resources: return None def parse_resource(resource): """ Parse each return object. """ stripchars = ", \n" addr = resource['Location']['Address'] address = addr.get('Label', '').strip(stripchars) city = addr.get('City', '').strip(stripchars) state = addr.get('State', '').strip(stripchars) zipcode = addr.get('PostalCode', '').strip(stripchars) country = addr.get('Country', '').strip(stripchars) city_state = join_filter(", ", [city, state]) place = join_filter(" ", [city_state, zipcode]) location = join_filter(", ", [address, place, country]) display_pos = resource['Location']['DisplayPosition'] latitude = float(display_pos['Latitude']) longitude = float(display_pos['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.20.0/geopy/geocoders/ignfrance.py0000644000076500000240000004766313446072254021004 0ustar kostyastaff00000000000000import warnings import xml.etree.ElementTree as ET from geopy.compat import ( HTTPBasicAuthHandler, HTTPPasswordMgrWithDefaultRealm, Request, build_opener, iteritems, u, urlencode, ) from geopy.exc import ConfigurationError, GeocoderQueryError from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.util import logger __all__ = ("IGNFrance", ) class IGNFrance(Geocoder): """Geocoder using the IGN France GeoCoder OpenLS API. Documentation at: https://geoservices.ign.fr/documentation/geoservices/index.html """ xml_request = """ {sub_request} """ api_path = '/%(api_key)s/geoportail/ols' def __init__( self, api_key, username=None, password=None, referer=None, domain='wxs.ign.fr', scheme=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, format_string=None, ssl_context=DEFAULT_SENTINEL, ): """ :param str api_key: The API key required by IGN France API to perform geocoding requests. You can get your key here: https://geoservices.ign.fr/documentation/services-acces.html. Mandatory. For authentication with referer and with username/password, the api key always differ. :param str username: When making a call need HTTP simple authentication username. Mandatory if no referer set :param str password: When making a call need HTTP simple authentication password. Mandatory if no referer set :param str referer: When making a call need HTTP referer. Mandatory if no password and username :param str 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 str scheme: See :attr:`geopy.geocoders.options.default_scheme`. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. .. versionadded:: 1.12.0 :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. .. versionadded:: 1.14.0 :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. .. versionadded:: 1.14.0 """ super(IGNFrance, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) # Catch if no api key with username and password # or no api key with referer if not ((api_key and username and password) or (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('/') api_path = self.api_path % dict(api_key=self.api_key) self.api = '%s://%s%s' % (self.scheme, self.domain, api_path) 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=DEFAULT_SENTINEL, ): """ Return a location point by address. :param str query: The query string to be geocoded. :param str 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 str is_freeform: Set if return is structured with freeform structure or a more structured returned. By default, value is False. :param str 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. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ query = self.format_string % query # 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=DEFAULT_SENTINEL, timeout=DEFAULT_SENTINEL, ): """ Return an address by location 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 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 str 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. .. versionchanged:: 1.14.0 Default value for ``exactly_one`` was ``False``, which differs from the conventional default across geopy. Please always pass this argument explicitly, otherwise you would get a warning. In geopy 2.0 the default value will become ``True``. :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. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ if exactly_one is DEFAULT_SENTINEL: warnings.warn('%s.reverse: default value for `exactly_one` ' 'argument will become True in geopy 2.0. ' 'Specify `exactly_one=False` as the argument ' 'explicitly to get rid of this warning.' % type(self).__name__, DeprecationWarning, stacklevel=2) exactly_one = False 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, "%(lat)s %(lon)s") 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): # TODO make this a private API # 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. self.urlopen = opener.open 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 is 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 address 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.20.0/geopy/geocoders/mapbox.py0000644000076500000240000001653213472474123020325 0ustar kostyastaff00000000000000from geopy.compat import quote, string_compare, urlencode from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.point import Point from geopy.util import logger __all__ = ("MapBox", ) class MapBox(Geocoder): """Geocoder using the Mapbox API. Documentation at: https://www.mapbox.com/api-documentation/ .. versionadded:: 1.17.0 """ api_path = '/geocoding/v5/mapbox.places/%(query)s.json/' def __init__( self, api_key, format_string=None, scheme=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, ssl_context=DEFAULT_SENTINEL, domain='api.mapbox.com', ): """ :param str api_key: The API key required by Mapbox to perform geocoding requests. API keys are managed through Mapox's account page (https://www.mapbox.com/account/access-tokens). :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. :param str domain: base api domain for mapbox """ super(MapBox, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) self.api_key = api_key self.domain = domain.strip('/') self.api = "%s://%s%s" % (self.scheme, self.domain, self.api_path) def _parse_json(self, json, exactly_one=True): '''Returns location, (latitude, longitude) from json feed.''' features = json['features'] if features == []: return None def parse_feature(feature): location = feature['place_name'] longitude = feature['geometry']['coordinates'][0] latitude = feature['geometry']['coordinates'][1] return Location(location, (latitude, longitude), feature) if exactly_one: return parse_feature(features[0]) else: return [parse_feature(feature) for feature in features] def geocode( self, query, exactly_one=True, timeout=DEFAULT_SENTINEL, proximity=None, country=None, bbox=None, ): """ Return a location point by address .. versionchanged:: 1.20.0 Previously due to a bug the resulting :class:`geopy.location.Location`'s ``raw`` attribute contained a single string instead of a full service response. :param str query: The address or query you wish to geocode. :param bool exactly_one: Return one result or a list of results, if available. .. versionchanged:: 1.20.0 Previously due to a bug this parameter wasn't respected. :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 proximity: A coordinate to bias local results based on a provided location. :type proximity: :class:`geopy.point.Point`, list or tuple of ``(latitude, longitude)``, or string as ``"%(latitude)s, %(longitude)s"``. :param country: Country to filter result in form of ISO 3166-1 alpha-2 country code (e.g. ``FR``). Might be a Python list of strings. .. versionchanged:: 1.19.0 Previously only a single string could be specified. Now a Python list of individual countries is supported. :type country: str or list :param bbox: The bounding box of the viewport within which to bias geocode results more prominently. Example: ``[Point(22, 180), Point(-22, -180)]``. :type bbox: list or tuple of 2 items of :class:`geopy.point.Point` or ``(latitude, longitude)`` or ``"%(latitude)s, %(longitude)s"``. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ params = {} params['access_token'] = self.api_key query = self.format_string % query if bbox: params['bbox'] = self._format_bounding_box( bbox, "%(lon1)s,%(lat1)s,%(lon2)s,%(lat2)s") if not country: country = [] if isinstance(country, string_compare): country = [country] if country: params['country'] = ",".join(country) if proximity: p = Point(proximity) params['proximity'] = "%s,%s" % (p.longitude, p.latitude) quoted_query = quote(query.encode('utf-8')) url = "?".join((self.api % dict(query=quoted_query), 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=DEFAULT_SENTINEL, ): """ Return an address by location point. .. versionchanged:: 1.20.0 Previously due to a bug the resulting :class:`geopy.location.Location`'s ``raw`` attribute contained a single string instead of a full service response. :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. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ params = {} params['access_token'] = self.api_key point = self._coerce_point_to_string(query, "%(lon)s,%(lat)s") quoted_query = quote(point.encode('utf-8')) url = "?".join((self.api % dict(query=quoted_query), urlencode(params))) logger.debug("%s.reverse: %s", self.__class__.__name__, url) return self._parse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) geopy-1.20.0/geopy/geocoders/opencage.py0000644000076500000240000002331313446072517020615 0ustar kostyastaff00000000000000import warnings from geopy.compat import string_compare, urlencode from geopy.exc import GeocoderQueryError, GeocoderQuotaExceeded from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.util import logger __all__ = ("OpenCage", ) class OpenCage(Geocoder): """Geocoder using the OpenCageData API. Documentation at: https://opencagedata.com/api .. versionadded:: 1.1.0 """ api_path = '/geocode/v1/json' def __init__( self, api_key, domain='api.opencagedata.com', scheme=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, format_string=None, ssl_context=DEFAULT_SENTINEL, ): """ :param str api_key: The API key required by OpenCageData to perform geocoding requests. You can get your key here: https://opencagedata.com/ :param str domain: Currently it is ``'api.opencagedata.com'``, can be changed for testing purposes. :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. .. versionadded:: 1.12.0 :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. .. versionadded:: 1.14.0 :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. .. versionadded:: 1.14.0 """ super(OpenCage, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) self.api_key = api_key self.domain = domain.strip('/') self.api = '%s://%s%s' % (self.scheme, self.domain, self.api_path) def geocode( self, query, bounds=None, country=None, language=None, exactly_one=True, timeout=DEFAULT_SENTINEL, ): """ Return a location point by address. :param str query: The address or query you wish to geocode. :param str 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. :type bounds: list or tuple of 2 items of :class:`geopy.point.Point` or ``(latitude, longitude)`` or ``"%(latitude)s, %(longitude)s"``. :param 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 2 coordinate points -- corners of a bounding box. Example: ``[Point(22, 180), Point(-22, -180)]``. .. versionchanged:: 1.17.0 Previously the only supported format for bounds was a string of ``"longitude,latitude,longitude,latitude"``. This format is now deprecated in favor of a list/tuple of a pair of geopy Points and will be removed in geopy 2.0. :param country: Restricts the results to the specified country or countries. The country code is a 2 character code as defined by the ISO 3166-1 Alpha 2 standard (e.g. ``fr``). Might be a Python list of strings. .. versionchanged:: 1.19.0 This parameter didn't seem to be respected previously. Also, previously only a single string could be specified. Now a Python list of individual countries is supported. :type country: str or list :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. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ params = { 'key': self.api_key, 'q': self.format_string % query, } if bounds: if isinstance(bounds, string_compare): warnings.warn( 'OpenCage `bounds` format of ' '`"longitude,latitude,longitude,latitude"` is now ' 'deprecated and will not be supported in geopy 2.0. ' 'Use `[Point(latitude, longitude), Point(latitude, longitude)]` ' 'instead.', DeprecationWarning, stacklevel=2 ) lon1, lat1, lon2, lat2 = bounds.split(',') bounds = [[lat1, lon1], [lat2, lon2]] params['bounds'] = self._format_bounding_box( bounds, "%(lon1)s,%(lat1)s,%(lon2)s,%(lat2)s") if language: params['language'] = language if not country: country = [] if isinstance(country, string_compare): country = [country] if country: params['countrycode'] = ",".join(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=DEFAULT_SENTINEL, timeout=DEFAULT_SENTINEL, ): """ Return an address by location 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 str language: The language in which to return results. :param bool exactly_one: Return one result or a list of results, if available. .. versionchanged:: 1.14.0 Default value for ``exactly_one`` was ``False``, which differs from the conventional default across geopy. Please always pass this argument explicitly, otherwise you would get a warning. In geopy 2.0 the default value will become ``True``. :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. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ if exactly_one is DEFAULT_SENTINEL: warnings.warn('%s.reverse: default value for `exactly_one` ' 'argument will become True in geopy 2.0. ' 'Specify `exactly_one=False` as the argument ' 'explicitly to get rid of this warning.' % type(self).__name__, DeprecationWarning, stacklevel=2) exactly_one = False 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.20.0/geopy/geocoders/openmapquest.py0000644000076500000240000001003113446072517021546 0ustar kostyastaff00000000000000from geopy.exc import ConfigurationError from geopy.geocoders.base import DEFAULT_SENTINEL from geopy.geocoders.osm import Nominatim __all__ = ("OpenMapQuest", ) class OpenMapQuest(Nominatim): """Geocoder using MapQuest Open Platform Web Services. Documentation at: https://developer.mapquest.com/documentation/open/ .. versionchanged:: 1.17.0 OpenMapQuest now extends the Nominatim class. """ geocode_path = '/nominatim/v1/search' reverse_path = '/nominatim/v1/reverse' def __init__( self, api_key=None, format_string=None, view_box=None, bounded=None, country_bias=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, domain='open.mapquestapi.com', scheme=None, user_agent=None, ssl_context=DEFAULT_SENTINEL, ): """ :param str api_key: API key provided by MapQuest, required. .. versionchanged:: 1.12.0 OpenMapQuest now requires an API key. Using an empty key will result in a :class:`geopy.exc.ConfigurationError`. :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. :type view_box: list or tuple of 2 items of :class:`geopy.point.Point` or ``(latitude, longitude)`` or ``"%(latitude)s, %(longitude)s"``. :param view_box: Coordinates to restrict search within. Example: ``[Point(22, 180), Point(-22, -180)]``. .. versionadded:: 1.17.0 .. deprecated:: 1.19.0 This argument will be removed in geopy 2.0. Use `geocode`'s `viewbox` instead. :param bool bounded: Restrict the results to only items contained within the bounding view_box. .. versionadded:: 1.17.0 .. deprecated:: 1.19.0 This argument will be removed in geopy 2.0. Use `geocode`'s `bounded` instead. :type country_bias: str or list :param country_bias: Limit search results to a specific country. This param sets a default value for the `geocode`'s ``country_codes``. .. versionadded:: 1.17.0 .. deprecated:: 1.19.0 This argument will be removed in geopy 2.0. Use `geocode`'s `country_codes` instead. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str domain: Domain where the target Nominatim service is hosted. .. versionadded:: 1.17.0 :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. .. versionadded:: 1.12.0 :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. .. versionadded:: 1.14.0 """ super(OpenMapQuest, self).__init__( format_string=format_string, view_box=view_box, bounded=bounded, country_bias=country_bias, timeout=timeout, proxies=proxies, domain=domain, scheme=scheme, user_agent=user_agent, ssl_context=ssl_context, ) if not api_key: raise ConfigurationError('OpenMapQuest requires an API key') self.api_key = api_key def _construct_url(self, base_api, params): """ Construct geocoding request url. Overridden. :param str base_api: Geocoding function base address - self.api or self.reverse_api. :param dict params: Geocoding params. :return: string URL. """ params['key'] = self.api_key return super(OpenMapQuest, self)._construct_url(base_api, params) geopy-1.20.0/geopy/geocoders/osm.py0000644000076500000240000004076413446072517017643 0ustar kostyastaff00000000000000import warnings from geopy.compat import string_compare, urlencode from geopy.exc import GeocoderQueryError from geopy.geocoders.base import _DEFAULT_USER_AGENT, DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.util import logger __all__ = ("Nominatim", ) _DEFAULT_NOMINATIM_DOMAIN = 'nominatim.openstreetmap.org' class Nominatim(Geocoder): """Nominatim geocoder for OpenStreetMap data. Documentation at: https://wiki.openstreetmap.org/wiki/Nominatim .. attention:: Using Nominatim with the default `user_agent` is strongly discouraged, as it violates Nominatim's Usage Policy https://operations.osmfoundation.org/policies/nominatim/ and may possibly cause 403 and 429 HTTP errors. Please make sure to specify a custom `user_agent` with ``Nominatim(user_agent="my-application")`` or by overriding the default `user_agent`: ``geopy.geocoders.options.default_user_agent = "my-application"``. In geopy 2.0 an exception will be thrown when a custom `user_agent` is not specified. .. versionchanged:: 1.16.0 A warning is now issued when a default `user_agent` is used which restates the `Attention` block above. """ structured_query_params = { 'street', 'city', 'county', 'state', 'country', 'postalcode', } geocode_path = '/search' reverse_path = '/reverse' def __init__( self, format_string=None, view_box=None, bounded=None, country_bias=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, domain=_DEFAULT_NOMINATIM_DOMAIN, scheme=None, user_agent=None, ssl_context=DEFAULT_SENTINEL, # Make sure to synchronize the changes of this signature in the # inheriting classes (e.g. PickPoint). ): """ :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. :type view_box: list or tuple of 2 items of :class:`geopy.point.Point` or ``(latitude, longitude)`` or ``"%(latitude)s, %(longitude)s"``. :param view_box: Coordinates to restrict search within. Example: ``[Point(22, 180), Point(-22, -180)]``. .. versionchanged:: 1.15.0 Previously only a list of stringified coordinates was supported. .. versionchanged:: 1.17.0 Previously view_box could be a list of 4 strings or numbers in the format of ``[longitude, latitude, longitude, latitude]``. This format is now deprecated in favor of a list/tuple of a pair of geopy Points and will be removed in geopy 2.0. .. deprecated:: 1.19.0 This argument will be removed in geopy 2.0. Use `geocode`'s `viewbox` instead. :param bool bounded: Restrict the results to only items contained within the bounding view_box. .. versionadded:: 1.15.0 .. deprecated:: 1.19.0 This argument will be removed in geopy 2.0. Use `geocode`'s `bounded` instead. :type country_bias: str or list :param country_bias: Limit search results to a specific country. This param sets a default value for the `geocode`'s ``country_codes``. .. deprecated:: 1.19.0 This argument will be removed in geopy 2.0. Use `geocode`'s `country_codes` instead. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str domain: Domain where the target Nominatim service is hosted. .. versionadded:: 1.8.2 :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. .. versionadded:: 1.8.2 :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. .. versionadded:: 1.12.0 :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. .. versionadded:: 1.14.0 """ super(Nominatim, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) if country_bias is not None: warnings.warn( '`country_bias` argument of the %(cls)s.__init__ ' 'is deprecated and will be removed in geopy 2.0. Use ' '%(cls)s.geocode(country_codes=%(value)r) instead.' % dict(cls=type(self).__name__, value=country_bias), DeprecationWarning, stacklevel=2 ) self.country_bias = country_bias if view_box is not None: warnings.warn( '`view_box` argument of the %(cls)s.__init__ ' 'is deprecated and will be removed in geopy 2.0. Use ' '%(cls)s.geocode(viewbox=%(value)r) instead.' % dict(cls=type(self).__name__, value=view_box), DeprecationWarning, stacklevel=2 ) self.view_box = view_box if bounded is not None: warnings.warn( '`bounded` argument of the %(cls)s.__init__ ' 'is deprecated and will be removed in geopy 2.0. Use ' '%(cls)s.geocode(bounded=%(value)r) instead.' % dict(cls=type(self).__name__, value=bounded), DeprecationWarning, stacklevel=2 ) self.bounded = bounded self.domain = domain.strip('/') if (self.domain == _DEFAULT_NOMINATIM_DOMAIN and self.headers['User-Agent'] == _DEFAULT_USER_AGENT): warnings.warn( 'Using Nominatim with the default "%s" `user_agent` is ' 'strongly discouraged, as it violates Nominatim\'s ToS ' 'https://operations.osmfoundation.org/policies/nominatim/ ' 'and may possibly cause 403 and 429 HTTP errors. ' 'Please specify a custom `user_agent` with ' '`Nominatim(user_agent="my-application")` or by ' 'overriding the default `user_agent`: ' '`geopy.geocoders.options.default_user_agent = "my-application"`. ' 'In geopy 2.0 this will become an exception.' % _DEFAULT_USER_AGENT, DeprecationWarning, stacklevel=2 ) self.api = "%s://%s%s" % (self.scheme, self.domain, self.geocode_path) self.reverse_api = "%s://%s%s" % (self.scheme, self.domain, self.reverse_path) def _construct_url(self, base_api, params): """ Construct geocoding request url. The method can be overriden in Nominatim-based geocoders in order to extend URL parameters. :param str base_api: Geocoding function base address - self.api or self.reverse_api. :param dict params: Geocoding params. :return: string URL. """ return "?".join((base_api, urlencode(params))) def geocode( self, query, exactly_one=True, timeout=DEFAULT_SENTINEL, limit=None, addressdetails=False, language=False, geometry=None, extratags=False, country_codes=None, viewbox=None, bounded=None, # TODO: change default value to `False` in geopy 2.0 ): """ Return a location point by address. :param query: The address, query or a structured query you wish to geocode. .. versionchanged:: 1.0.0 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 str :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 int limit: Maximum amount of results to return from Nominatim. Unless exactly_one is set to False, limit will always be 1. .. versionadded:: 1.13.0 :param bool addressdetails: If you want in *Location.raw* to include addressdetails such as city_district, etc set it to True :param str language: Preferred language in which to return results. Either uses standard `RFC2616 `_ accept-language string or a simple comma-separated list of language codes. .. versionadded:: 1.0.0 :param str 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 :param bool extratags: Include additional information in the result if available, e.g. wikipedia link, opening hours. .. versionadded:: 1.17.0 :param country_codes: Limit search results to a specific country (or a list of countries). A country_code should be the ISO 3166-1alpha2 code, e.g. ``gb`` for the United Kingdom, ``de`` for Germany, etc. .. versionadded:: 1.19.0 :type country_codes: str or list :type viewbox: list or tuple of 2 items of :class:`geopy.point.Point` or ``(latitude, longitude)`` or ``"%(latitude)s, %(longitude)s"``. :param viewbox: Coordinates to restrict search within. Example: ``[Point(22, 180), Point(-22, -180)]``. .. versionadded:: 1.19.0 :param bool bounded: Restrict the results to only items contained within the bounding view_box. Defaults to `False`. .. versionadded:: 1.19.0 :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ 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' }) if exactly_one: params['limit'] = 1 elif limit is not None: limit = int(limit) if limit < 1: raise ValueError("Limit cannot be less than 1") params['limit'] = limit if viewbox is None: viewbox = self.view_box if viewbox: if len(viewbox) == 4: warnings.warn( '%s `viewbox` format of ' '`[longitude, latitude, longitude, latitude]` is now ' 'deprecated and will not be supported in geopy 2.0. ' 'Use `[Point(latitude, longitude), Point(latitude, longitude)]` ' 'instead.' % type(self).__name__, DeprecationWarning, stacklevel=2 ) lon1, lat1, lon2, lat2 = viewbox viewbox = [[lat1, lon1], [lat2, lon2]] params['viewbox'] = self._format_bounding_box( viewbox, "%(lon1)s,%(lat1)s,%(lon2)s,%(lat2)s") if bounded is None: bounded = self.bounded if bounded: params['bounded'] = 1 if country_codes is None: country_codes = self.country_bias if not country_codes: country_codes = [] if isinstance(country_codes, string_compare): country_codes = [country_codes] if country_codes: params['countrycodes'] = ",".join(country_codes) if addressdetails: params['addressdetails'] = 1 if language: params['accept-language'] = language if extratags: params['extratags'] = True 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 = self._construct_url(self.api, 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=DEFAULT_SENTINEL, language=False, addressdetails=True ): """ Return an address by location 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 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 str language: Preferred language in which to return results. Either uses standard `RFC2616 `_ accept-language string or a simple comma-separated list of language codes. .. versionadded:: 1.0.0 :param bool addressdetails: Whether or not to include address details, such as city, county, state, etc. in *Location.raw* .. versionadded:: 1.14.0 :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ try: lat, lon = self._coerce_point_to_string(query).split(',') except ValueError: raise ValueError("Must be a coordinate pair or Point") params = { 'lat': lat, 'lon': lon, 'format': 'json', } if language: params['accept-language'] = language params['addressdetails'] = 1 if addressdetails else 0 url = self._construct_url(self.reverse_api, 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): # TODO make this a private API # Parse each resource. latitude = place.get('lat', None) longitude = place.get('lon', None) placename = place.get('display_name', None) if latitude is not None and longitude is not None: latitude = float(latitude) longitude = float(longitude) return Location(placename, (latitude, longitude), place) def _parse_json(self, places, exactly_one): if not places: return None if not isinstance(places, list): places = [places] if exactly_one: return self.parse_code(places[0]) else: return [self.parse_code(place) for place in places] geopy-1.20.0/geopy/geocoders/pelias.py0000644000076500000240000002275413446072517020321 0ustar kostyastaff00000000000000import warnings from geopy.compat import urlencode from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.util import logger __all__ = ("Pelias", ) class Pelias(Geocoder): """Pelias geocoder. Documentation at: https://github.com/pelias/documentation See also :class:`geopy.geocoders.GeocodeEarth` which is a Pelias-based service provided by the developers of Pelias itself. .. versionchanged:: 1.15.0 ``Mapzen`` geocoder has been renamed to ``Pelias``. """ geocode_path = '/v1/search' reverse_path = '/v1/reverse' def __init__( self, domain, api_key=None, format_string=None, boundary_rect=None, country_bias=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, scheme=None, ssl_context=DEFAULT_SENTINEL, # Make sure to synchronize the changes of this signature in the # inheriting classes (e.g. GeocodeEarth). ): """ :param str domain: Specify a domain for Pelias API. :param str api_key: Pelias API key, optional. :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. :type boundary_rect: list or tuple of 2 items of :class:`geopy.point.Point` or ``(latitude, longitude)`` or ``"%(latitude)s, %(longitude)s"``. :param boundary_rect: Coordinates to restrict search within. Example: ``[Point(22, 180), Point(-22, -180)]``. .. versionchanged:: 1.17.0 Previously boundary_rect could be a list of 4 strings or numbers in the format of ``[longitude, latitude, longitude, latitude]``. This format is now deprecated in favor of a list/tuple of a pair of geopy Points and will be removed in geopy 2.0. .. deprecated:: 1.19.0 This argument will be removed in geopy 2.0. Use `geocode`'s `boundary_rect` instead. :param str country_bias: Bias results to this country (ISO alpha-3). .. deprecated:: 1.19.0 This argument will be removed in geopy 2.0. Use `geocode`'s `country_bias` instead. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. """ super(Pelias, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) if country_bias is not None: warnings.warn( '`country_bias` argument of the %(cls)s.__init__ ' 'is deprecated and will be removed in geopy 2.0. Use ' '%(cls)s.geocode(country_bias=%(value)r) instead.' % dict(cls=type(self).__name__, value=country_bias), DeprecationWarning, stacklevel=2 ) self.country_bias = country_bias if boundary_rect is not None: warnings.warn( '`boundary_rect` argument of the %(cls)s.__init__ ' 'is deprecated and will be removed in geopy 2.0. Use ' '%(cls)s.geocode(boundary_rect=%(value)r) instead.' % dict(cls=type(self).__name__, value=boundary_rect), DeprecationWarning, stacklevel=2 ) self.boundary_rect = boundary_rect self.api_key = api_key self.domain = domain.strip('/') self.geocode_api = ( '%s://%s%s' % (self.scheme, self.domain, self.geocode_path) ) self.reverse_api = ( '%s://%s%s' % (self.scheme, self.domain, self.reverse_path) ) def geocode( self, query, exactly_one=True, timeout=DEFAULT_SENTINEL, boundary_rect=None, country_bias=None, ): """ Return a location point by address. :param str query: The address, query or structured query to geocode 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. :type boundary_rect: list or tuple of 2 items of :class:`geopy.point.Point` or ``(latitude, longitude)`` or ``"%(latitude)s, %(longitude)s"``. :param boundary_rect: Coordinates to restrict search within. Example: ``[Point(22, 180), Point(-22, -180)]``. .. versionadded:: 1.19.0 :param str country_bias: Bias results to this country (ISO alpha-3). .. versionadded:: 1.19.0 :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ params = {'text': self.format_string % query} if self.api_key: params.update({ 'api_key': self.api_key }) if boundary_rect is None: boundary_rect = self.boundary_rect if boundary_rect: if len(boundary_rect) == 4: warnings.warn( '%s `boundary_rect` format of ' '`[longitude, latitude, longitude, latitude]` is now ' 'deprecated and will not be supported in geopy 2.0. ' 'Use `[Point(latitude, longitude), Point(latitude, longitude)]` ' 'instead.' % type(self).__name__, DeprecationWarning, stacklevel=2 ) lon1, lat1, lon2, lat2 = boundary_rect boundary_rect = [[lat1, lon1], [lat2, lon2]] lon1, lat1, lon2, lat2 = self._format_bounding_box( boundary_rect, "%(lon1)s,%(lat1)s,%(lon2)s,%(lat2)s").split(',') params['boundary.rect.min_lon'] = lon1 params['boundary.rect.min_lat'] = lat1 params['boundary.rect.max_lon'] = lon2 params['boundary.rect.max_lat'] = lat2 if country_bias is None: country_bias = self.country_bias if country_bias: params['boundary.country'] = country_bias url = "?".join((self.geocode_api, urlencode(params))) logger.debug("%s.geocode_api: %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=DEFAULT_SENTINEL, ): """ Return an address by location 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 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. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ try: lat, lon = self._coerce_point_to_string(query).split(',') except ValueError: raise ValueError("Must be a coordinate pair or Point") params = { 'point.lat': lat, 'point.lon': lon, } if self.api_key: params.update({ 'api_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(feature): # TODO make this a private API # Parse each resource. latitude = feature.get('geometry', {}).get('coordinates', [])[1] longitude = feature.get('geometry', {}).get('coordinates', [])[0] placename = feature.get('properties', {}).get('name') return Location(placename, (latitude, longitude), feature) def _parse_json(self, response, exactly_one): if response is None: return None features = response['features'] if not len(features): return None if exactly_one: return self.parse_code(features[0]) else: return [self.parse_code(feature) for feature in features] geopy-1.20.0/geopy/geocoders/photon.py0000644000076500000240000002001013446072254020330 0ustar kostyastaff00000000000000from geopy.compat import string_compare, urlencode from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.util import logger __all__ = ("Photon", ) class Photon(Geocoder): """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 Photon/Komoot geocoder aims to let you `search as you type with OpenStreetMap`. No API Key is needed by this platform. """ geocode_path = '/api' reverse_path = '/reverse' def __init__( self, format_string=None, scheme=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, domain='photon.komoot.de', user_agent=None, ssl_context=DEFAULT_SENTINEL, ): """ :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str 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. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. .. versionadded:: 1.12.0 :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. .. versionadded:: 1.14.0 """ super(Photon, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) self.domain = domain.strip('/') self.api = "%s://%s%s" % (self.scheme, self.domain, self.geocode_path) self.reverse_api = "%s://%s%s" % (self.scheme, self.domain, self.reverse_path) def geocode( self, query, exactly_one=True, timeout=DEFAULT_SENTINEL, location_bias=None, language=False, limit=None, osm_tag=None ): """ Return a location point by address. :param str 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. :param str language: Preferred language in which to return results. :param int limit: Limit the number of returned results, defaults to no limit. .. versionadded:: 1.12.0 :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 required as ``['key:!val', '!key', ':!value']``. :type osm_tag: str or list or set :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ params = { 'q': self.format_string % query } if limit: params['limit'] = int(limit) if exactly_one: params['limit'] = 1 if language: params['lang'] = language if location_bias: try: lat, lon = 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: if not isinstance(osm_tag, (list, set)): raise ValueError( "osm_tag must be a string expression or " "a set/list of string expressions" ) params['osm_tag'] = osm_tag url = "?".join((self.api, urlencode(params, doseq=True))) 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=DEFAULT_SENTINEL, language=False, limit=None, ): """ Return an address by location 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 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 str language: Preferred language in which to return results. :param int limit: Limit the number of returned results, defaults to no limit. .. versionadded:: 1.12.0 :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ try: lat, lon = self._coerce_point_to_string(query).split(',') except ValueError: raise ValueError("Must be a coordinate pair or Point") params = { 'lat': lat, 'lon': lon, } if limit: params['limit'] = int(limit) if exactly_one: params['limit'] = 1 if language: params['lang'] = 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 ) @classmethod def _parse_json(cls, resources, exactly_one=True): """ Parse display name, latitude, and longitude from a JSON response. """ if not len(resources['features']): # 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): # TODO make this a private API # Return location and coordinates tuple from dict. name_elements = ['name', 'housenumber', 'street', 'postcode', 'street', 'city', 'state', 'country'] name = [resource['properties'].get(k) for k in name_elements if resource['properties'].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.20.0/geopy/geocoders/pickpoint.py0000644000076500000240000000743413446072517021042 0ustar kostyastaff00000000000000from geopy.geocoders import Nominatim from geopy.geocoders.base import DEFAULT_SENTINEL __all__ = ("PickPoint",) class PickPoint(Nominatim): """PickPoint geocoder is a commercial version of Nominatim. Documentation at: https://pickpoint.io/api-reference .. versionadded:: 1.13.0 """ geocode_path = '/v1/forward' reverse_path = '/v1/reverse' def __init__( self, api_key, format_string=None, view_box=None, bounded=None, country_bias=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, domain='api.pickpoint.io', scheme=None, user_agent=None, ssl_context=DEFAULT_SENTINEL, ): """ :param str api_key: PickPoint API key obtained at https://pickpoint.io. :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. :type view_box: list or tuple of 2 items of :class:`geopy.point.Point` or ``(latitude, longitude)`` or ``"%(latitude)s, %(longitude)s"``. :param view_box: Coordinates to restrict search within. Example: ``[Point(22, 180), Point(-22, -180)]``. .. versionchanged:: 1.17.0 Previously view_box could be a list of 4 strings or numbers in the format of ``[longitude, latitude, longitude, latitude]``. This format is now deprecated in favor of a list/tuple of a pair of geopy Points and will be removed in geopy 2.0. .. deprecated:: 1.19.0 This argument will be removed in geopy 2.0. Use `geocode`'s `viewbox` instead. :param bool bounded: Restrict the results to only items contained within the bounding view_box. .. deprecated:: 1.19.0 This argument will be removed in geopy 2.0. Use `geocode`'s `bounded` instead. :type country_bias: str or list :param country_bias: Limit search results to a specific country. This param sets a default value for the `geocode`'s ``country_codes``. .. deprecated:: 1.19.0 This argument will be removed in geopy 2.0. Use `geocode`'s `country_codes` instead. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str domain: Domain where the target Nominatim service is hosted. :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. .. versionadded:: 1.14.0 """ super(PickPoint, self).__init__( format_string=format_string, view_box=view_box, bounded=bounded, country_bias=country_bias, timeout=timeout, proxies=proxies, domain=domain, scheme=scheme, user_agent=user_agent, ssl_context=ssl_context, ) self.api_key = api_key def _construct_url(self, base_api, params): """ Construct geocoding request url. Overridden. :param str base_api: Geocoding function base address - self.api or self.reverse_api. :param dict params: Geocoding params. :return: string URL. """ params['key'] = self.api_key return super(PickPoint, self)._construct_url(base_api, params) geopy-1.20.0/geopy/geocoders/smartystreets.py0000644000076500000240000001502313446072517021764 0ustar kostyastaff00000000000000import warnings from geopy.compat import urlencode from geopy.exc import ConfigurationError, GeocoderQuotaExceeded from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.util import logger __all__ = ("LiveAddress", ) class LiveAddress(Geocoder): """Geocoder using the LiveAddress API provided by SmartyStreets. Documentation at: https://smartystreets.com/docs/cloud/us-street-api """ geocode_path = '/street-address' def __init__( self, auth_id, auth_token, candidates=None, scheme='https', timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, format_string=None, ssl_context=DEFAULT_SENTINEL, ): """ :param str auth_id: Valid `Auth ID` from SmartyStreets. .. versionadded:: 1.5.0 :param str 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. Defaults to `1`. .. deprecated:: 1.19.0 This argument will be removed in geopy 2.0. Use `geocode`'s `candidates` instead. :param str scheme: Must be ``https``. .. deprecated:: 1.14.0 Don't use this parameter, it's going to be removed in geopy 2.0. .. versionchanged:: 1.8.0 LiveAddress now requires `https`. Specifying `scheme=http` will result in a :class:`geopy.exc.ConfigurationError`. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. .. versionadded:: 1.12.0 :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. .. versionadded:: 1.14.0 :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. .. versionadded:: 1.14.0 """ super(LiveAddress, self).__init__( format_string=format_string, # The `scheme` argument is present for the legacy reasons only. # If a custom value has been passed, it should be validated. # Otherwise use `https` instead of the `options.default_scheme`. scheme=(scheme or 'https'), timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) if self.scheme != "https": raise ConfigurationError("LiveAddress now requires `https`.") 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') if candidates is not None: warnings.warn( '`candidates` argument of the %(cls)s.__init__ ' 'is deprecated and will be removed in geopy 2.0. Use ' '%(cls)s.geocode(candidates=%(value)r) instead.' % dict(cls=type(self).__name__, value=candidates), DeprecationWarning, stacklevel=2 ) self.candidates = candidates domain = 'api.smartystreets.com' self.api = '%s://%s%s' % (self.scheme, domain, self.geocode_path) def geocode( self, query, exactly_one=True, timeout=DEFAULT_SENTINEL, candidates=None, # TODO: change default value to `1` in geopy 2.0 ): """ Return a location point by address. :param str 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 int candidates: An integer between 1 and 10 indicating the max number of candidate addresses to return if a valid address could be found. Defaults to `1`. .. versionadded:: 1.19.0 :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ if candidates is None: candidates = self.candidates if candidates is None: candidates = 1 # TODO: move to default args in geopy 2.0. if candidates: if not (1 <= candidates <= 10): raise ValueError('candidates must be between 1 and 10') query = { 'auth-id': self.auth_id, 'auth-token': self.auth_token, 'street': self.format_string % query, 'candidates': candidates, } url = '{url}?{query}'.format(url=self.api, query=urlencode(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): """ LiveStreets-specific exceptions. """ if "no active subscriptions found" in message.lower(): raise GeocoderQuotaExceeded(message) def _parse_json(self, response, exactly_one=True): """ Parse responses as JSON objects. """ if not len(response): return None if exactly_one: 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.20.0/geopy/geocoders/tomtom.py0000644000076500000240000001706513446072254020360 0ustar kostyastaff00000000000000from geopy.compat import quote, urlencode from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.util import logger __all__ = ("TomTom", ) class TomTom(Geocoder): """TomTom geocoder. Documentation at: https://developer.tomtom.com/online-search/online-search-documentation .. versionadded:: 1.15.0 """ geocode_path = '/search/2/geocode/%(query)s.json' reverse_path = '/search/2/reverseGeocode/%(position)s.json' def __init__( self, api_key, format_string=None, scheme=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, ssl_context=DEFAULT_SENTINEL, domain='api.tomtom.com', ): """ :param str api_key: TomTom API key. :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. :param str domain: Domain where the target TomTom service is hosted. """ super(TomTom, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) self.api_key = api_key self.api = "%s://%s%s" % (self.scheme, domain, self.geocode_path) self.api_reverse = "%s://%s%s" % (self.scheme, domain, self.reverse_path) def geocode( self, query, exactly_one=True, timeout=DEFAULT_SENTINEL, limit=None, typeahead=False, language=None, ): """ Return a location point by address. :param str 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 int limit: Maximum amount of results to return from the service. Unless exactly_one is set to False, limit will always be 1. :param bool typeahead: If the "typeahead" flag is set, the query will be interpreted as a partial input and the search will enter predictive mode. :param str language: Language in which search results should be returned. When data in specified language is not available for a specific field, default language is used. List of supported languages (case-insensitive): https://developer.tomtom.com/online-search/online-search-documentation/supported-languages :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ query = self.format_string % query params = self._geocode_params(query) params['typeahead'] = self._boolean_value(typeahead) if limit: params['limit'] = str(int(limit)) if exactly_one: params['limit'] = '1' if language: params['language'] = language quoted_query = quote(query.encode('utf-8')) url = "?".join((self.api % dict(query=quoted_query), 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=DEFAULT_SENTINEL, language=None, ): """ Return an address by location 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 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 str language: Language in which search results should be returned. When data in specified language is not available for a specific field, default language is used. List of supported languages (case-insensitive): https://developer.tomtom.com/online-search/online-search-documentation/supported-languages .. versionadded:: 1.18.0 :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ position = self._coerce_point_to_string(query) params = self._reverse_params(position) if language: params['language'] = language quoted_position = quote(position.encode('utf-8')) url = "?".join((self.api_reverse % dict(position=quoted_position), urlencode(params))) logger.debug("%s.reverse: %s", self.__class__.__name__, url) return self._parse_reverse_json( self._call_geocoder(url, timeout=timeout), exactly_one ) @staticmethod def _boolean_value(bool_value): return 'true' if bool_value else 'false' def _geocode_params(self, formatted_query): return { 'key': self.api_key, } def _reverse_params(self, position): return { 'key': self.api_key, } def _parse_json(self, resources, exactly_one): if not resources or not resources['results']: return None if exactly_one: return self._parse_search_result(resources['results'][0]) else: return [self._parse_search_result(result) for result in resources['results']] def _parse_search_result(self, result): latitude = result['position']['lat'] longitude = result['position']['lon'] return Location(result['address']['freeformAddress'], (latitude, longitude), result) def _parse_reverse_json(self, resources, exactly_one): if not resources or not resources['addresses']: return None if exactly_one: return self._parse_reverse_result(resources['addresses'][0]) else: return [self._parse_reverse_result(result) for result in resources['addresses']] def _parse_reverse_result(self, result): latitude, longitude = result['position'].split(',') return Location(result['address']['freeformAddress'], (latitude, longitude), result) geopy-1.20.0/geopy/geocoders/what3words.py0000644000076500000240000002040413446072254021135 0ustar kostyastaff00000000000000import re from geopy import exc from geopy.compat import urlencode from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.util import logger __all__ = ("What3Words", ) class What3Words(Geocoder): """What3Words geocoder. Documentation at: https://docs.what3words.com/api/v2/ .. versionadded:: 1.5.0 .. versionchanged:: 1.15.0 API has been updated to v2. """ multiple_word_re = re.compile( r"[^\W\d\_]+\.{1,1}[^\W\d\_]+\.{1,1}[^\W\d\_]+$", re.U ) geocode_path = '/v2/forward' reverse_path = '/v2/reverse' def __init__( self, api_key, format_string=None, scheme='https', timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, ssl_context=DEFAULT_SENTINEL, ): """ :param str api_key: Key provided by What3Words (https://accounts.what3words.com/register). :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. :param str scheme: Must be ``https``. .. deprecated:: 1.15.0 API v2 requires https. Don't use this parameter, it's going to be removed in geopy 2.0. Scheme other than ``https`` would result in a :class:`geopy.exc.ConfigurationError` being thrown. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. .. versionadded:: 1.12.0 :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. .. versionadded:: 1.14.0 """ super(What3Words, self).__init__( format_string=format_string, # The `scheme` argument is present for the legacy reasons only. # If a custom value has been passed, it should be validated. # Otherwise use `https` instead of the `options.default_scheme`. scheme=(scheme or 'https'), timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) if self.scheme != "https": raise exc.ConfigurationError("What3Words now requires `https`.") self.api_key = api_key domain = 'api.what3words.com' self.geocode_api = '%s://%s%s' % (self.scheme, domain, self.geocode_path) self.reverse_api = '%s://%s%s' % (self.scheme, domain, self.reverse_path) def _check_query(self, query): """ Check query validity with regex """ if not self.multiple_word_re.match(query): return False else: return True def geocode(self, query, lang='en', exactly_one=True, timeout=DEFAULT_SENTINEL): """ Return a location point for a `3 words` query. If the `3 words` address doesn't exist, a :class:`geopy.exc.GeocoderQueryError` exception will be thrown. :param str query: The 3-word address you wish to geocode. :param str lang: two character language codes as supported by the API (https://docs.what3words.com/api/v2/#lang). :param bool exactly_one: Return one result or a list of results, if available. Due to the address scheme there is always exactly one result for each `3 words` address, so this parameter is rather useless for this geocoder. .. versionchanged:: 1.14.0 ``exactly_one=False`` now returns a list of a single location. This option wasn't respected before. :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. :rtype: :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ if not self._check_query(query): raise exc.GeocoderQueryError( "Search string must be 'word.word.word'" ) params = { 'addr': self.format_string % query, 'lang': lang.lower(), 'key': self.api_key, } url = "?".join((self.geocode_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 _parse_json(self, resources, exactly_one=True): """ Parse type, words, latitude, and longitude and language from a JSON response. """ code = resources['status'].get('code') if code: # https://docs.what3words.com/api/v2/#errors exc_msg = "Error returned by What3Words: %s" % resources['status']['message'] if code == 401: raise exc.GeocoderAuthenticationFailure(exc_msg) raise exc.GeocoderQueryError(exc_msg) def parse_resource(resource): """ Parse record. """ if 'geometry' in resource: words = resource['words'] position = resource['geometry'] latitude, longitude = position['lat'], position['lng'] if latitude and longitude: latitude = float(latitude) longitude = float(longitude) return Location(words, (latitude, longitude), resource) else: raise exc.GeocoderParseError('Error parsing result.') location = parse_resource(resources) if exactly_one: return location else: return [location] def reverse(self, query, lang='en', exactly_one=True, timeout=DEFAULT_SENTINEL): """ Return a `3 words` address by location point. Each point on surface has a `3 words` address, so there's always a non-empty response. :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 str lang: two character language codes as supported by the API (https://docs.what3words.com/api/v2/#lang). :param bool exactly_one: Return one result or a list of results, if available. Due to the address scheme there is always exactly one result for each `3 words` address, so this parameter is rather useless for this geocoder. .. versionchanged:: 1.14.0 ``exactly_one=False`` now returns a list of a single location. This option wasn't respected before. :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. :rtype: :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ lang = lang.lower() params = { 'coords': self._coerce_point_to_string(query), 'lang': lang.lower(), 'key': self.api_key, } url = "?".join((self.reverse_api, urlencode(params))) logger.debug("%s.reverse: %s", self.__class__.__name__, url) return self._parse_reverse_json( self._call_geocoder(url, timeout=timeout), exactly_one=exactly_one ) def _parse_reverse_json(self, resources, exactly_one=True): """ Parses a location from a single-result reverse API call. """ return self._parse_json(resources, exactly_one) geopy-1.20.0/geopy/geocoders/yandex.py0000644000076500000240000001655213446072254020331 0ustar kostyastaff00000000000000import warnings from geopy.compat import urlencode from geopy.exc import GeocoderParseError, GeocoderServiceError from geopy.geocoders.base import DEFAULT_SENTINEL, Geocoder from geopy.location import Location from geopy.util import logger __all__ = ("Yandex", ) class Yandex(Geocoder): """Yandex geocoder. Documentation at: https://tech.yandex.com/maps/doc/geocoder/desc/concepts/input_params-docpage/ .. versionadded:: 1.5.0 """ api_path = '/1.x/' def __init__( self, api_key=None, lang=None, timeout=DEFAULT_SENTINEL, proxies=DEFAULT_SENTINEL, user_agent=None, scheme=None, format_string=None, ssl_context=DEFAULT_SENTINEL, ): """ .. versionchanged:: 1.14.0 Default scheme has been changed from ``http`` to ``https``. :param str api_key: Yandex API key (not obligatory) https://tech.yandex.ru/maps/keys/get/ :param str lang: response locale, the following locales are supported: ``"ru_RU"`` (default), ``"uk_UA"``, ``"be_BY"``, ``"en_US"``, ``"tr_TR"``. :param int timeout: See :attr:`geopy.geocoders.options.default_timeout`. :param dict proxies: See :attr:`geopy.geocoders.options.default_proxies`. :param str user_agent: See :attr:`geopy.geocoders.options.default_user_agent`. .. versionadded:: 1.12.0 :param str scheme: See :attr:`geopy.geocoders.options.default_scheme`. .. versionadded:: 1.14.0 :param str format_string: See :attr:`geopy.geocoders.options.default_format_string`. .. versionadded:: 1.14.0 :type ssl_context: :class:`ssl.SSLContext` :param ssl_context: See :attr:`geopy.geocoders.options.default_ssl_context`. .. versionadded:: 1.14.0 """ super(Yandex, self).__init__( format_string=format_string, scheme=scheme, timeout=timeout, proxies=proxies, user_agent=user_agent, ssl_context=ssl_context, ) self.api_key = api_key self.lang = lang domain = 'geocode-maps.yandex.ru' self.api = '%s://%s%s' % (self.scheme, domain, self.api_path) def geocode(self, query, exactly_one=True, timeout=DEFAULT_SENTINEL): """ Return a location point by address. :param str 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. :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ params = { 'geocode': self.format_string % query, 'format': 'json' } if self.api_key: params['apikey'] = self.api_key if self.lang: params['lang'] = self.lang if exactly_one: 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=DEFAULT_SENTINEL, timeout=DEFAULT_SENTINEL, kind=None, ): """ Return an address by location 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 of results, if available. .. versionchanged:: 1.14.0 Default value for ``exactly_one`` was ``False``, which differs from the conventional default across geopy. Please always pass this argument explicitly, otherwise you would get a warning. In geopy 2.0 the default value will become ``True``. :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 str kind: Type of toponym. Allowed values: `house`, `street`, `metro`, `district`, `locality`. .. versionadded:: 1.14.0 :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if ``exactly_one=False``. """ if exactly_one is DEFAULT_SENTINEL: warnings.warn('%s.reverse: default value for `exactly_one` ' 'argument will become True in geopy 2.0. ' 'Specify `exactly_one=False` as the argument ' 'explicitly to get rid of this warning.' % type(self).__name__, DeprecationWarning, stacklevel=2) exactly_one = False try: point = self._coerce_point_to_string(query, "%(lon)s,%(lat)s") except ValueError: raise ValueError("Must be a coordinate pair or Point") params = { 'geocode': point, 'format': 'json' } if self.api_key: params['apikey'] = self.api_key if self.lang: params['lang'] = self.lang if kind: params['kind'] = kind 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(' ') ] name_elements = ['name', 'description'] location = ', '.join([place[k] for k in name_elements if place.get(k)]) 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.20.0/geopy/location.py0000644000076500000240000000732113446072254016671 0ustar kostyastaff00000000000000""" :class:`.Location` returns geocoder results. """ from geopy.compat import py3k, string_compare from geopy.point import Point def _location_tuple(location): return location._address, (location._point[0], location._point[1]) class Location(object): """ 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)))? \s*$ """ % { "FLOAT": r'\d+(?:\.\d+)?', "DEGREE": DEGREE, "PRIME": PRIME, "DOUBLE_PRIME": DOUBLE_PRIME, "SEP": r'\s*[,;/\s]\s*', }, re.VERBOSE | re.UNICODE) def _normalize_angle(x, limit): """ Normalize angle `x` to be within `[-limit; limit)` range. """ double_limit = limit * 2.0 modulo = fmod(x, double_limit) or 0.0 # `or 0` is to turn -0 to +0. if modulo < -limit: return modulo + double_limit if modulo >= limit: return modulo - double_limit return modulo def _normalize_coordinates(latitude, longitude, altitude): latitude = float(latitude or 0.0) longitude = float(longitude or 0.0) altitude = float(altitude or 0.0) is_all_finite = all(isfinite(x) for x in (latitude, longitude, altitude)) if not is_all_finite: raise ValueError('Point coordinates must be finite. %r has been passed ' 'as coordinates.' % ((latitude, longitude, altitude),)) if abs(latitude) > 90: warnings.warn('Latitude normalization has been prohibited in the newer ' 'versions of geopy, because the normalized value happened ' 'to be on a different pole, which is probably not what was ' 'meant. If you pass coordinates as positional args, ' 'please make sure that the order is ' '(latitude, longitude) or (y, x) in Cartesian terms.', UserWarning, stacklevel=3) raise ValueError('Latitude must be in the [-90; 90] range.') if abs(longitude) > 180: # Longitude normalization is pretty straightforward and doesn't seem # to be error-prone, so there's nothing to complain about. longitude = _normalize_angle(longitude, 180.0) return latitude, longitude, altitude 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 latitude, longitude, and altitude:: >>> p1 = Point(41.5, -81, 0) >>> p2 = Point(latitude=41.5, longitude=-81) With a sequence of 0 to 3 values (latitude, longitude, 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") 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 = latitude is not None and longitude is None and altitude is None if single_arg and not isinstance(latitude, util.NUMBER_TYPES): arg = latitude if 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: raise TypeError( "Failed to create Point instance from %r." % (arg,) ) else: return cls.from_sequence(seq) if single_arg: warnings.warn('A single number has been passed to the Point ' 'constructor. This is probably a mistake, because ' 'constructing a Point with just a latitude ' 'seems senseless. If this is exactly what was ' 'meant, then pass the zero longitude explicitly ' 'to get rid of this warning. ' 'In geopy 2.0 this will become an exception.', DeprecationWarning, stacklevel=2) latitude, longitude, altitude = \ _normalize_coordinates(latitude, longitude, altitude) self = super(Point, cls).__new__(cls) self.latitude = latitude self.longitude = longitude self.altitude = altitude return self def __getitem__(self, index): return tuple(self)[index] # tuple handles slices def __setitem__(self, index, value): point = list(self) point[index] = value # list handles slices self.latitude, self.longitude, self.altitude = \ _normalize_coordinates(*point) def __iter__(self): return iter((self.latitude, self.longitude, self.altitude)) def __getstate__(self): return tuple(self) def __setstate__(self, state): self.latitude, self.longitude, self.altitude = state def __repr__(self): return "Point(%r, %r, %r)" % tuple(self) 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: if not isinstance(altitude, string_compare): altitude = 'km' coordinates.append(self.format_altitude(altitude)) return ", ".join(coordinates) def format_altitude(self, unit='km'): """ Format 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): if not isinstance(other, collections.Iterable): return NotImplemented return tuple(self) == tuple(other) def __ne__(self, other): return not (self == 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: 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 that 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)) if len(args) > 3: raise ValueError('When creating a Point from sequence, it ' 'must not have more than 3 items.') 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.20.0/geopy/timezone.py0000644000076500000240000000474613446072254016723 0ustar kostyastaff00000000000000from geopy.compat import text_type from geopy.exc import GeocoderParseError try: import pytz pytz_available = True except ImportError: pytz_available = False __all__ = ( "Timezone", ) def ensure_pytz_is_installed(): if not pytz_available: raise ImportError( 'pytz must be installed in order to locate timezones. ' ' Install with `pip install geopy -e ".[timezone]"`.' ) def from_timezone_name(timezone_name, raw=None): ensure_pytz_is_installed() try: pytz_timezone = pytz.timezone(timezone_name) except pytz.UnknownTimeZoneError: raise GeocoderParseError( "pytz could not parse the timezone identifier (%s) " "returned by the service." % timezone_name ) except KeyError: raise GeocoderParseError( "geopy could not find a timezone in this response: %s" % raw ) return Timezone(pytz_timezone, raw) def from_fixed_gmt_offset(gmt_offset_hours, raw=None): ensure_pytz_is_installed() pytz_timezone = pytz.FixedOffset(gmt_offset_hours * 60) return Timezone(pytz_timezone, raw) class Timezone(object): """ Contains a parsed response for a timezone request, which is implemented in few geocoders which provide such lookups. .. versionadded:: 1.18.0 """ __slots__ = ("_pytz_timezone", "_raw") def __init__(self, pytz_timezone, raw=None): self._pytz_timezone = pytz_timezone self._raw = raw @property def pytz_timezone(self): """ pytz timezone instance. :rtype: :class:`pytz.tzinfo.BaseTzInfo` """ return self._pytz_timezone @property def raw(self): """ Timezone's raw, unparsed geocoder response. For details on this, consult the service's documentation. :rtype: dict or None """ return self._raw def __unicode__(self): return text_type(self._pytz_timezone) __str__ = __unicode__ def __repr__(self): return "Timezone(%s)" % repr(self.pytz_timezone) def __getstate__(self): return self._pytz_timezone, self._raw def __setstate__(self, state): self._pytz_timezone, self._raw = state def __eq__(self, other): return ( isinstance(other, Timezone) and self._pytz_timezone == other._pytz_timezone and self.raw == other.raw ) def __ne__(self, other): return not (self == other) geopy-1.20.0/geopy/units.py0000644000076500000240000000503713374047210016217 0ustar kostyastaff00000000000000""" Convert units. """ import math # Angles def degrees(radians=0, arcminutes=0, arcseconds=0): """ 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): """ 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): """ 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): """ 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): """ TODO docs. """ ret = 0. if meters: ret += meters / 1000. if feet: ret += 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): """ TODO docs. """ return (kilometers + km(nautical=nautical, miles=miles, feet=feet)) * 1000 def miles(kilometers=0, meters=0, feet=0, nautical=0): """ TODO docs. """ ret = 0. if nautical: kilometers += nautical / nm(1.) if feet: kilometers += feet / ft(1.) if meters: kilometers += meters / 1000. ret += kilometers / 1.609344 return ret def feet(kilometers=0, meters=0, miles=0, nautical=0): """ 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): """ TODO docs. """ ret = 0. if feet: kilometers += feet / ft(1.) if miles: kilometers += km(miles=miles) if meters: kilometers += meters / 1000. ret += kilometers / 1.852 return ret # Compatible names rad = radians arcmin = arcminutes arcsec = arcseconds km = kilometers m = meters mi = miles ft = feet nm = nautical geopy-1.20.0/geopy/util.py0000644000076500000240000000255013472475436016045 0ustar kostyastaff00000000000000""" Utils. """ import logging from geopy.compat import py3k, text_type if not py3k: # pragma: no cover NUMBER_TYPES = (int, long, float) # noqa 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.20.0" logger = logging.getLogger('geopy') def pairwise(seq): """ Pair an iterable, e.g., (1, 2, 3, 4) -> ((1, 2), (2, 3), (3, 4)) """ for i in range(0, len(seq) - 1): yield (seq[i], seq[i + 1]) def join_filter(sep, seq, pred=bool): """ Join with a filter. """ return sep.join([text_type(i) for i in seq if pred(i)]) 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 if py3k: encoding = page.headers.get_param("charset") or "utf-8" else: encoding = page.headers.getparam("charset") or "utf-8" return text_type(page.read(), encoding=encoding) else: # requests? encoding = page.headers.get("charset") or "utf-8" return text_type(page.content, encoding=encoding) def get_version(): return __version__ geopy-1.20.0/geopy.egg-info/0000755000076500000240000000000013472475530016201 5ustar kostyastaff00000000000000geopy-1.20.0/geopy.egg-info/PKG-INFO0000644000076500000240000001412413472475530017300 0ustar kostyastaff00000000000000Metadata-Version: 2.1 Name: geopy Version: 1.20.0 Summary: Python Geocoding Toolbox Home-page: https://github.com/geopy/geopy Author: GeoPy Contributors Author-email: uijllji@gmail.com License: MIT Download-URL: https://github.com/geopy/geopy/archive/1.20.0.tar.gz Description: geopy ===== .. image:: https://img.shields.io/pypi/v/geopy.svg?style=flat-square :target: https://pypi.python.org/pypi/geopy/ :alt: Latest Version .. image:: https://img.shields.io/travis/geopy/geopy.svg?style=flat-square :target: https://travis-ci.org/geopy/geopy :alt: Build Status .. image:: https://img.shields.io/github/license/geopy/geopy.svg?style=flat-square :target: https://pypi.python.org/pypi/geopy/ :alt: License 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`_, `Google Geocoding API (V3)`_, and many other geocoding services. The full list is available on the `Geocoders doc section`_. Geocoder classes are located in `geopy.geocoders`_. .. _OpenStreetMap Nominatim: https://wiki.openstreetmap.org/wiki/Nominatim .. _Google Geocoding API (V3): https://developers.google.com/maps/documentation/geocoding/ .. _Geocoders doc section: https://geopy.readthedocs.io/en/latest/#geocoders .. _geopy.geocoders: https://github.com/geopy/geopy/tree/master/geopy/geocoders geopy is tested against CPython (versions 2.7, 3.4, 3.5, 3.6, 3.7), PyPy, and PyPy3. geopy does not and will not support CPython 2.6. © geopy contributors 2006-2018 (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: .. code:: python >>> from geopy.geocoders import Nominatim >>> geolocator = Nominatim(user_agent="specify_your_app_name_here") >>> 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: .. code:: python >>> from geopy.geocoders import Nominatim >>> geolocator = Nominatim(user_agent="specify_your_app_name_here") >>> 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 `geodesic distance `_ or the `great-circle distance `_, with a default of the geodesic distance available as the function `geopy.distance.distance`. Here's an example usage of the geodesic distance: .. code:: python >>> from geopy.distance import geodesic >>> newport_ri = (41.49008, -71.312796) >>> cleveland_oh = (41.499498, -81.695391) >>> print(geodesic(newport_ri, cleveland_oh).miles) 538.390445368 Using great-circle distance: .. code:: python >>> 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) 536.997990696 Documentation ------------- More documentation and examples can be found at `Read the Docs `__. 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.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Provides-Extra: dev Provides-Extra: dev-test Provides-Extra: timezone Provides-Extra: dev-lint Provides-Extra: dev-docs geopy-1.20.0/geopy.egg-info/SOURCES.txt0000644000076500000240000000214513472475530020067 0ustar kostyastaff00000000000000AUTHORS LICENSE MANIFEST.in README.rst setup.cfg setup.py geopy/__init__.py geopy/compat.py geopy/distance.py geopy/exc.py geopy/format.py geopy/location.py geopy/point.py geopy/timezone.py geopy/units.py geopy/util.py geopy.egg-info/PKG-INFO geopy.egg-info/SOURCES.txt geopy.egg-info/dependency_links.txt geopy.egg-info/requires.txt geopy.egg-info/top_level.txt geopy/extra/__init__.py geopy/extra/rate_limiter.py geopy/geocoders/__init__.py geopy/geocoders/arcgis.py geopy/geocoders/azure.py geopy/geocoders/baidu.py geopy/geocoders/banfrance.py geopy/geocoders/base.py geopy/geocoders/bing.py geopy/geocoders/databc.py geopy/geocoders/geocodeearth.py geopy/geocoders/geocodefarm.py geopy/geocoders/geolake.py geopy/geocoders/geonames.py geopy/geocoders/googlev3.py geopy/geocoders/here.py geopy/geocoders/ignfrance.py geopy/geocoders/mapbox.py geopy/geocoders/opencage.py geopy/geocoders/openmapquest.py geopy/geocoders/osm.py geopy/geocoders/pelias.py geopy/geocoders/photon.py geopy/geocoders/pickpoint.py geopy/geocoders/smartystreets.py geopy/geocoders/tomtom.py geopy/geocoders/what3words.py geopy/geocoders/yandex.pygeopy-1.20.0/geopy.egg-info/dependency_links.txt0000644000076500000240000000000113472475530022247 0ustar kostyastaff00000000000000 geopy-1.20.0/geopy.egg-info/requires.txt0000644000076500000240000000075013472475530020603 0ustar kostyastaff00000000000000geographiclib<2,>=1.49 [dev] mock six flake8<3.7.0,>=3.6.0 isort<4.4.0,>=4.3.4 coverage pytest>=3.10 readme_renderer sphinx sphinx_rtd_theme>=0.4.0 [dev-docs] readme_renderer sphinx sphinx_rtd_theme>=0.4.0 [dev-lint] mock six flake8<3.7.0,>=3.6.0 isort<4.4.0,>=4.3.4 [dev-lint:python_version < "3.0"] contextlib2 [dev-test] mock six coverage pytest>=3.10 [dev-test:python_version < "3.0"] contextlib2 statistics [dev:python_version < "3.0"] contextlib2 statistics [timezone] pytz geopy-1.20.0/geopy.egg-info/top_level.txt0000644000076500000240000000000613472475530020727 0ustar kostyastaff00000000000000geopy geopy-1.20.0/setup.cfg0000644000076500000240000000056113472475530015207 0ustar kostyastaff00000000000000[wheel] universal = 1 [flake8] max-complexity = 23 max-line-length = 90 exclude = .git, .tox, .venv, __pycache__, build, dist, docs, geopy.egg-info [isort] combine_as_imports = True force_grid_wrap = 0 include_trailing_comma = True known_first_party = test line_length = 88 multi_line_output = 3 not_skip = __init__.py [egg_info] tag_build = tag_date = 0 geopy-1.20.0/setup.py0000755000076500000240000000454213446072254015103 0ustar kostyastaff00000000000000#!/usr/bin/env python """ geopy """ from setuptools import find_packages, setup from geopy import __version__ as version INSTALL_REQUIRES = [ 'geographiclib<2,>=1.49', ] EXTRAS_DEV_TESTFILES_COMMON = [ "contextlib2; python_version<'3.0'", "mock", "six", ] EXTRAS_DEV_LINT = [ "flake8>=3.6.0,<3.7.0", "isort>=4.3.4,<4.4.0", ] EXTRAS_DEV_TEST = [ "coverage", "pytest>=3.10", "statistics; python_version<'3.0'", ] EXTRAS_DEV_DOCS = [ "readme_renderer", "sphinx", "sphinx_rtd_theme>=0.4.0", ] setup( name='geopy', version=version, description='Python Geocoding Toolbox', long_description=open('README.rst').read(), author='GeoPy Contributors', author_email='uijllji@gmail.com', url='https://github.com/geopy/geopy', download_url=( 'https://github.com/geopy/geopy/archive/%s.tar.gz' % version ), packages=find_packages(exclude=["*test*"]), install_requires=INSTALL_REQUIRES, extras_require={ "dev": (EXTRAS_DEV_TESTFILES_COMMON + EXTRAS_DEV_LINT + EXTRAS_DEV_TEST + EXTRAS_DEV_DOCS), "dev-lint": (EXTRAS_DEV_TESTFILES_COMMON + EXTRAS_DEV_LINT), "dev-test": (EXTRAS_DEV_TESTFILES_COMMON + EXTRAS_DEV_TEST), "dev-docs": EXTRAS_DEV_DOCS, "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.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", ] )