Example of Simple Geocoding in 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:

In [1]:
#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
Out[1]:
Location(Milton Keynes, South East, England, MK9 3PD, UK, (52.0429797, -0.7589607, 0.0))
In [2]:
#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
Out[2]:
('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:

In [3]:
import pandas as pd
df = pd.DataFrame({'towns':['Milton Keynes', 'London','Newport, Isle of WIght']})
df
Out[3]:
towns
0 Milton Keynes
1 London
2 Newport, Isle of WIght
In [4]:
df['towns'].apply(geolocator.geocode)
Out[4]:
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:

In [5]:
df['location']=df['towns'].apply(geolocator.geocode)
df
Out[5]:
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...
In [6]:
#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
Out[6]:
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.

In [7]:
import folium

mymap = folium.Map([51,0],zoom_start=6, tiles='openstreetmap')

for point in df['point']:
    folium.Marker(point).add_to(mymap)

mymap
Out[7]:

Using 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:

In [8]:
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
Out[8]:
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:

In [9]:
gpd.tools.geocode?

We can also look up docs for the service providers supported by geopy:

In [ ]:
import geopy
dir(geopy)
In [11]:
geopy.Nominatim?
In [12]:
#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')
Out[12]:
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)
In [13]:
gdf[['address','geometry']] = gpd.tools.geocode(gdf['towns'], provider='nominatim', user_agent='TM351 demo')
gdf
Out[13]:
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...

In [14]:
#Get a geojson export of the point locations
geojson = gdf.to_json()
In [15]:
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
Out[15]:
In [ ]: