geopandas
¶geopandas
provides native access to the geopy
Pyhton package, which in turn provides access to a wide range of geocoding services.
Some of these services require and API key, some don't. The Nominatim OpenStreetMap geocoder doesn't require an API key but does require a user-agent string (which can be anything...).
We can do simple geocoding directly on a pandas dataframe:
#Nominatim is the openstreetmap geocoder
from geopy.geocoders import Nominatim
#It doesn't need an API key, bit it likes to know who's calling
geolocator = Nominatim(user_agent="TM351 demo")
location = geolocator.geocode("Milton Keynes")
location
Location(Milton Keynes, South East, England, MK9 3PD, UK, (52.0429797, -0.7589607, 0.0))
#dir(location)
#The point is a shapely object. Shapely types play nice with lots of geo things...
location.address, location.latitude, location.longitude, location.point
('Milton Keynes, South East, England, MK9 3PD, UK', 52.0429797, -0.7589607, Point(52.0429797, -0.7589607, 0.0))
We could geocode items in a panda dataframe using something like this:
import pandas as pd
df = pd.DataFrame({'towns':['Milton Keynes', 'London','Newport, Isle of WIght']})
df
towns | |
---|---|
0 | Milton Keynes |
1 | London |
2 | Newport, Isle of WIght |
df['towns'].apply(geolocator.geocode)
0 (Milton Keynes, South East, England, MK9 3PD, ... 1 (London, Greater London, England, SW1A 2DX, UK... 2 (Newport, Carisbrooke, Isle of Wight, South Ea... Name: towns, dtype: object
Now do the geocoding:
df['location']=df['towns'].apply(geolocator.geocode)
df
towns | location | |
---|---|---|
0 | Milton Keynes | (Milton Keynes, South East, England, MK9 3PD, ... |
1 | London | (London, Greater London, England, SW1A 2DX, UK... |
2 | Newport, Isle of WIght | (Newport, Carisbrooke, Isle of Wight, South Ea... |
#Here's how we could pull state out the the return object
df['address'] = df['location'].apply(lambda x: x.address)
df['latitude'] = df['location'].apply(lambda x: x.latitude)
df['longitude'] = df['location'].apply(lambda x: x.longitude)
df['point'] = df['location'].apply(lambda x: x.point)
df
towns | location | address | latitude | longitude | point | |
---|---|---|---|---|---|---|
0 | Milton Keynes | (Milton Keynes, South East, England, MK9 3PD, ... | Milton Keynes, South East, England, MK9 3PD, UK | 52.0429797 | -0.7589607 | 52 2m 34.7269s N, 0 45m 32.2585s W |
1 | London | (London, Greater London, England, SW1A 2DX, UK... | London, Greater London, England, SW1A 2DX, UK | 51.5073219 | -0.1276474 | 51 30m 26.3588s N, 0 7m 39.5306s W |
2 | Newport, Isle of WIght | (Newport, Carisbrooke, Isle of Wight, South Ea... | Newport, Carisbrooke, Isle of Wight, South Eas... | 50.6913105 | -1.3163556 | 50 41m 28.7178s N, 1 18m 58.8802s W |
We can then variously plot things on a map. Having a shapely Point
object to hand, we might as well make use of that.
import folium
mymap = folium.Map([51,0],zoom_start=6, tiles='openstreetmap')
for point in df['point']:
folium.Marker(point).add_to(mymap)
mymap
geopandas
¶We can also work with geopandas, an extended pandas dataframe format that has support for spatial objects and manipulations.
Let's create a simple geopandas dataframe:
import geopandas as gpd
gdf = gpd.GeoDataFrame({'towns':['Milton Keynes', 'London','Newport, Isle of WIght']})
#Or we could cast from pandas using: gpd.GeoDataFrame(df)
gdf
towns | |
---|---|
0 | Milton Keynes |
1 | London |
2 | Newport, Isle of WIght |
The geopandas docs suggest the geocoder lives in gpd.tools.geocode
, so let's have a quick look at its docs:
gpd.tools.geocode?
We can also look up docs for the service providers supported by geopy
:
import geopy
dir(geopy)
geopy.Nominatim?
#Nominatim requires a user agent
#Not sure offhand how to expicitly make this play nice? Maybe it does anyway?
gpd.tools.geocode(gdf['towns'], provider='nominatim', user_agent='TM351 demo')
address | geometry | |
---|---|---|
0 | Milton Keynes, South East, England, MK9 3PD, UK | POINT (-0.7589607 52.0429797) |
1 | London, Greater London, England, SW1A 2DX, UK | POINT (-0.1276474 51.5073219) |
2 | Newport, Carisbrooke, Isle of Wight, South Eas... | POINT (-1.31635559782082 50.6913105) |
gdf[['address','geometry']] = gpd.tools.geocode(gdf['towns'], provider='nominatim', user_agent='TM351 demo')
gdf
towns | address | geometry | |
---|---|---|---|
0 | Milton Keynes | Milton Keynes, South East, England, MK9 3PD, UK | POINT (-0.7589607 52.0429797) |
1 | London | London, Greater London, England, SW1A 2DX, UK | POINT (-0.1276474 51.5073219) |
2 | Newport, Isle of WIght | Newport, Carisbrooke, Isle of Wight, South Eas... | POINT (-1.31635559782082 50.6913105) |
We can plot these on a folium map easily enough by extracting the points as geojson, converting them to a format folium is happy with, and plotting that...
#Get a geojson export of the point locations
geojson = gdf.to_json()
mymap = folium.Map([51,0],zoom_start=6, tiles='openstreetmap')
#Create an element from the geojson that folium can plot as a map layer
points = folium.features.GeoJson(geojson)
#Add the layer
mymap.add_child(points)
#Preview the map
mymap