python-package/lets_plot/geo_data/core.py (54 lines of code) (raw):

from typing import Any import numpy as np from .geocoder import Geocoder, ReverseGeocoder, NamesGeocoder __all__ = [ 'distance', 'geocode', 'geocode_cities', 'geocode_counties', 'geocode_states', 'geocode_countries', 'reverse_geocode' ] UNITS_DICT = { 'mi': 3959, 'km': 6371 } GEOFUNC_TYPES = { 'centroids': 'centroids', 'boundaries': 'boundaries', 'limits': 'limits', 'region': 'regions' } def geocode(level=None, names=None, countries=None, states=None, counties=None, scope=None) -> NamesGeocoder: """ Create a `NamesGeocoder <https://lets-plot.org/python/pages/api/lets_plot.geo_data.NamesGeocoder.html>`__. Allow to refine ambiguous request with `where() <https://lets-plot.org/python/pages/api/lets_plot.geo_data.NamesGeocoder.html#lets_plot.geo_data.NamesGeocoder.where>`__ method, scope that limits area of geocoding or with parents. Parameters ---------- level : {'country', 'state', 'county', 'city'} The level of administrative division. Autodetection by default. names : list or str Names of objects to be geocoded. For 'state' level: 'US-48' returns continental part of United States (48 states) in a compact form. countries : list Parent countries. Should have same size as names. Can contain strings or ``Geocoder`` objects. states : list Parent states. Should have same size as names. Can contain strings or ``Geocoder`` objects. counties : list Parent counties. Should have same size as names. Can contain strings or ``Geocoder`` objects. scope : str or ``Geocoder`` Limits area of geocoding. If parent country is set then error will be generated. If type is a string - geoobject should have geocoded scope in parents. If type is a ``Geocoder`` - geoobject should have geocoded scope in parents. Scope should contain only one entry. Returns ------- ``NamesGeocoder`` Geocoder object specification. Examples -------- .. jupyter-execute:: :linenos: :emphasize-lines: 5 from IPython.display import display from lets_plot import * from lets_plot.geo_data import * LetsPlot.setup_html() states = geocode('state').scope('Italy').get_boundaries(6) display(states.head()) ggplot() + geom_map(data=states) | .. jupyter-execute:: :linenos: :emphasize-lines: 5, 8 from IPython.display import display from lets_plot import * from lets_plot.geo_data import * LetsPlot.setup_html() states = geocode(level='state', scope='US').get_geocodes() display(states.head()) names = ['York'] * len(states.state) cities = geocode(names=names, states=states.state).ignore_not_found().get_centroids() display(cities.head()) ggplot() + \\ geom_livemap() + \\ geom_point(data=cities, tooltips=layer_tooltips().line('@{found name}')) """ return NamesGeocoder(level, names) \ .scope(scope) \ .countries(countries) \ .states(states) \ .counties(counties) def geocode_cities(names=None) -> NamesGeocoder: """ Create a `NamesGeocoder <https://lets-plot.org/python/pages/api/lets_plot.geo_data.NamesGeocoder.html>`__ object for cities. Allow to refine ambiguous request with `where() <https://lets-plot.org/python/pages/api/lets_plot.geo_data.NamesGeocoder.html#lets_plot.geo_data.NamesGeocoder.where>`__ method, with a scope that limits area of geocoding or with parents. Parameters ---------- names : str or list Names of objects to be geocoded. Returns ------- ``NamesGeocoder`` Geocoder object specification. Examples -------- .. jupyter-execute:: :linenos: :emphasize-lines: 5 from IPython.display import display from lets_plot import * from lets_plot.geo_data import * LetsPlot.setup_html() cities = geocode_cities(['York', 'Jersey'])\\ .where(name='Jersey', scope='New Jersey').get_boundaries() display(cities) ggplot() + geom_map(aes(fill='found name'), data=cities, color='white') """ return NamesGeocoder('city', names) def geocode_counties(names=None) -> NamesGeocoder: """ Create a `NamesGeocoder <https://lets-plot.org/python/pages/api/lets_plot.geo_data.NamesGeocoder.html>`__ object for counties. Allow to refine ambiguous request with `where() <https://lets-plot.org/python/pages/api/lets_plot.geo_data.NamesGeocoder.html#lets_plot.geo_data.NamesGeocoder.where>`__ method, with a scope that limits area of geocoding or with parents. Parameters ---------- names : str or list Names of objects to be geocoded. Returns ------- ``NamesGeocoder`` Geocoder object specification. Examples -------- .. jupyter-execute:: :linenos: :emphasize-lines: 5 from IPython.display import display from lets_plot import * from lets_plot.geo_data import * LetsPlot.setup_html() counties = geocode_counties().scope('NY').get_boundaries(9) display(counties.head()) ggplot() + geom_map(data=counties) + ggtitle('New York State Counties') """ return NamesGeocoder('county', names) def geocode_states(names=None) -> NamesGeocoder: """ Create a `NamesGeocoder <https://lets-plot.org/python/pages/api/lets_plot.geo_data.NamesGeocoder.html>`__ object for states. Allow to refine ambiguous request with `where() <https://lets-plot.org/python/pages/api/lets_plot.geo_data.NamesGeocoder.html#lets_plot.geo_data.NamesGeocoder.where>`__ method, with a scope that limits area of geocoding or with parents. Parameters ---------- names : str or list Names of objects to be geocoded. Returns ------- ``NamesGeocoder`` Geocoder object specification. Examples -------- .. jupyter-execute:: :linenos: :emphasize-lines: 5 from IPython.display import display from lets_plot import * from lets_plot.geo_data import * LetsPlot.setup_html() states = geocode_states().scope('UK').get_boundaries() display(states) ggplot() + \\ geom_map(aes(fill='found name'), data=states, color='white') + \\ ggtitle('UK States') """ return NamesGeocoder('state', names) def geocode_countries(names=None) -> NamesGeocoder: """ Create a `NamesGeocoder <https://lets-plot.org/python/pages/api/lets_plot.geo_data.NamesGeocoder.html>`__ object for countries. Allow to refine ambiguous request with `where() <https://lets-plot.org/python/pages/api/lets_plot.geo_data.NamesGeocoder.html#lets_plot.geo_data.NamesGeocoder.where>`__ method. Parameters ---------- names : str or list Names of objects to be geocoded. Returns ------- ``NamesGeocoder`` Geocoder object specification. Examples -------- .. jupyter-execute:: :linenos: :emphasize-lines: 5 from IPython.display import display from lets_plot import * from lets_plot.geo_data import * LetsPlot.setup_html() countries = geocode_countries(['Germany', 'Poland']).inc_res().get_boundaries() display(countries) ggplot() + geom_map(aes(fill='found name'), data=countries, color='white') """ return NamesGeocoder('country', names) def reverse_geocode(lon, lat, level=None, scope=None) -> ReverseGeocoder: """ Convert a location as described by geographic coordinates to a ``ReverseGeocoder`` object. Parameters ---------- lon : float Longitude coordinate of the geoobject. lat : float Latitude coordinate of the geoobject. level : {'country', 'state', 'county', 'city'} The level of administrative division. scope : str or ``Geocoder`` Specify this for resolving conflicts for disputed territories. Returns ------- ``ReverseGeocoder`` Geocoder object specification. Examples -------- .. jupyter-execute:: :linenos: :emphasize-lines: 5 from IPython.display import display from lets_plot import * from lets_plot.geo_data import * LetsPlot.setup_html() city = reverse_geocode(-73.87, 40.68, level='city').get_boundaries() display(city) ggplot() + geom_map(data=city) + ggtitle(city.iloc[0]['found name']) """ return ReverseGeocoder(lon, lat, level, scope) def distance(lon0, lat0, lon1, lat1, units='km'): """ Calculate the distance between two points. Return result in kilometers or miles. Parameters ---------- lon0 : float Longitude coordinate of the first point. lat0 : float Latitude coordinate of the first point. lon1 : float Longitude coordinate of the second point. lat1 : float Latitude coordinate of the second point. units : {'mi', 'km'}, default='km' The units in which the result will be obtained. There are shorthands for values: 'mi' (miles), 'km' (kilometers). Returns ------- float Distance between the points. Examples -------- .. jupyter-execute:: :linenos: :emphasize-lines: 3 from lets_plot.geo_data import * cities = geocode_cities(['New York', 'Chicago']).get_centroids().geometry distance(cities[0].x, cities[0].y, cities[1].x, cities[1].y, units='mi') """ return _calc_distance(lon0, lat0, lon1, lat1, units) def _calc_distance(lon0, lat0, lon1, lat1, u): r = _prepare_units(u) lon0, lat0, lon1, lat1 = map(np.radians, [lon0, lat0, lon1, lat1]) dlon = lon1 - lon0 dlat = lat1 - lat0 a = np.sin(dlat / 2.0) ** 2 + np.cos(lat0) * np.cos(lat1) * np.sin(dlon / 2.0) ** 2 c = 2 * np.arcsin(np.sqrt(a)) return c * r def _prepare_units(units: Any) -> float: try: return UNITS_DICT[units] except KeyError: raise ValueError('Wrong units: {}. The units can take the following values: ' 'mi (miles), km (kilometers).'.format(units))