OpenStreetMap, Folium and GeoPy for Mapping and Distance Finding

Python has some excellent tools for plotting points on maps, and for calculating distances between geographical points. In this tutorial, we will dive into a few useful tools and learn how to do some interesting geographical operations.

We will walk through a scenario-based example of how to discover how to convert ZIP-Codes to a latitude/longitude pairs, and how to then plot those points on a map using Python's folium library. We will look at how to find the distance between two geographical points using geopy, and how to calculate the mid-point between two points.

The Jupyter Notebook for this tutorial is available on Github at this link.

All libraries used in this tutorial are available in Google Colab notebooks. Alternatively, you can install the libraries - folium, geopy and requests- with pip or conda.

Objectives

After this tutorial, you should understand how to:

  • Convert a non-US ZIP code to a latitude/longitude pair using the OSM API.
  • Plot the point represented by the latitude/longitude on a map, using the Folium library.
  • Calculate the distance between two points using operations from the GeoPy library.
  • Calculate (roughly) the mid-point between two points.

Converting ZIP Code to Coordinates

Let's first consider how to convert a ZIP code to geographical coordinates.

Imagine a scenario where a user is entering their ZIP code into an web-form - perhaps to buy a product, or register an interest in your business. If you are the business owner, you might want to be able to plot where all your users are located, and find out which areas your business is doing best in, or which areas you may need to work on marketing and business strategy.

Working with raw ZIP codes is not optimal, as there is no intrinsic geographical properties that you can work with. It would be more useful to convert the ZIP codes to latitude and longitude pairs, to get an idea of where your users are located.

Geographical coordinates can easily be visualized on a map, and you can easily do distance calculations and other effective GIS analysis that could be very useful for your business and your users.

So how do we convert ZIP codes to coordinates? We can use the Nominatim Search API, provided by OSM. This API allows you to look up locations from textual attributes - including from ZIP codes. The endpoint we will use is: https://nominatim.openstreetmap.org/search?format=json&postalcode={ZIP_HERE}

At the end of the URL, we substitute the {ZIP_HERE} with a real ZIP code to do the lookup. More information on the Search API can be found here

To perform the lookup, we will use the requests library to do an HTTP GET request to the above URL.

Let's see some code. We'll look up the G42 9AY postcode - a random postcode in Glasgow, Scotland.

import requests
from pprint import pprint

BASE_URL = 'https://nominatim.openstreetmap.org/search?format=json'
postcode = 'G42 9AY'

response = requests.get(f"{BASE_URL}&postalcode={postcode}")
data = response.json()
pprint(data)

On lines 4 and 5, we define the BASE_URL and a postcode to work with. On line 6, we perform a GET request to the endpoint by appending the postalcode query parameter to the BASE_URL, and setting it to the postcode variable using an f-string.

On line 8, we store the response data into a Python dictionary using the response.json() method available on Response objects in the requests library. We have specified that we want JSON data via the format=json query parameter in the URL - note that you can also specify XML, GeoJSON and GeocodeJSON with this API. Finally, we pretty-print the response data. You should receive output similar to below.

[{'boundingbox': ['55.665234206593',
                  '55.985234206593',
                  '-4.413199587922',
                  '-4.093199587922'],
  'class': 'place',
  'display_name': 'Glasgow City, Scotland, G42 9AY, United Kingdom',
  'importance': 0.325,
  'lat': '55.82523420659273',
  'licence': 'Data © OpenStreetMap contributors, ODbL 1.0. '
             'https://osm.org/copyright',
  'lon': '-4.253199587921969',
  'place_id': 261283313,
  'type': 'postcode'}]

We get back a list with a single object, representing the UK area with postcode G42 9AY. There's some useful information here - a display_name with a readable name, and a boundingbox specifying the geometry of the postcode/ZIP-code area.

We are interested in the latitude and longitude of the ZIP code. We can get these through the lat and lon fields, with the code below (we'll also grab the bounding box!):

latitude = data[0].get('lat')
longitude = data[0].get('lon')
print(latitude, longitude)

>> 55.82523420659273 -4.253199587921969

bbox = data[0].get('boundingbox')
print(bbox)

>> ['55.665234206593', '55.985234206593', '-4.413199587922', '-4.093199587922']

We've successfully extracted the data we're interested in. Now, let's use the same method to grab a second point. We're going to look for ZIP Code 2106, and store its values in two variables - latitude2 and longitude2.

zipcode = '2601'

response = requests.get(f"{BASE_URL}&postalcode={zipcode}")
response.json()

This gives us back a list with many objects - many different countries have a ZIP code of 2601, including Austria, Australia, Hungary and Italy. We can use another query parameter to narrow this down to just locations in one particular country.

Let's get the data for Italy, only. We use the country={country} query parameter, appending it to the end of the URL.

response = requests.get(f"{BASE_URL}&postalcode={zipcode}&country=italia")
data = response.json()

latitude2 = data[0].get('lat')
longitude2 = data[0].get('lon')
print(latitude2, longitude2)

>> 45.33059985 9.774886999999994

Now, we have two points - one in Scotland, and one in Italy. Let's now look at mapping these points onto interactive web-based maps, using the Folium library!

Mapping the Points

We are going to use Folium to plot the two points in Scotland and Italy on an interactive map, using markers to denote the locations.

We have two latitudes and longitudes for two locations - let's pull these into tuples, for convenience.

# create tuples representing our location
location = float(latitude), float(longitude)
location2 = float(latitude2), float(longitude2)

Now we'll import folium and plot the two points on a map. We will centre the map at Amsterdam (very roughly, halfway between Scotland and Italy).

# Amsterdam's latitude/longitude as a tuple
amsterdam = (52.3676, 4.9041)

# create a Folium map centred at Amsterdam
m = folium.Map(location=amsterdam, zoom_start=4, width=800, height=400)

# add markers at the locations
folium.Marker(location, popup="The postcode brought me here").add_to(m)
folium.Marker(location2, popup="The postcode brought me here").add_to(m)

# refer to the map to display it in Jupyter/Colab notebooks
m

On line 5, we use the folium.Map() object to create the map, centred at Amsterdam and with the given zoom, width and height. We add the two markers by calling folium.Marker() and specifying each location as the point where the marker is placed. These markers are added to the map with the add_to(m) function.

This should show a map that looks like the one below.

We can see the two markers, one in Scotland, and one in Italy.

We can compute the midpoint slightly more accurately with some Python code. We'll take the two locations we have, and compute the midpoint of their latitude values, and the midpoint of their longitude values.

We will zip the two locations to add their respective latitudes and longitudes, and divide by two to get the midpoint.

# generator expression to compute midpoint of the two locations
# this works because both locations are of form: (lat, long)
# zipping them together allows us to iterate over both lats at once
midpoint_gen = ((x+y)/2 for x,y in zip(location, location2))

# convert generator to a tuple representing lat/longitude of the midpoint
midpoint = tuple(midpoint_gen)

print(location)
print(location2)
print(midpoint)

>> (55.82523420659273, -4.253199587921969)
>> (45.33059985, 9.774886999999994)
>> (50.57791702829637, 2.7608437060390125)

We can now centre the map on this midpoint - indeed, we can put a marker down to see where it is!

import folium

# create Folium map
m = folium.Map(location=midpoint, zoom_start=4, width=800, height=400)

# add marker at the locations
folium.Marker(location, popup="The postcode brought me here").add_to(m)
folium.Marker(location2, popup="The postcode brought me here").add_to(m)
folium.Marker(midpoint, popup="Middle!").add_to(m)

m

This should display a map similar to the below, with the new point plotted halfway between our two original points (somewhere in France).


We have plotted the points successfully. Now, let's look at how to find the distances between our points.

Calculating Distances with GeoPy

GeoPy is a cool library for useful geographical operations. One such operation is finding the distance between two points on the Earth's surface.

GeoPy has a distance module that allows computation of these distances. We will use the default distance method - this calculates geodetic distance between two points. We can calculate the distance between the Scottish point and the Italian point with the code below.

from geopy.distance import distance

km = distance(location, location2)
miles = distance(location, location2).miles

print("Distance between postcodes:")
print(f"{km}")
print(f"{miles} miles")

>> Distance between postcodes:
>> 1527.1533016462852 km
>> 948.929067773133 miles

The distance() function from the geopy.distance module calculates the geodetic distance. By default, we get the distance in kilometres, but we can also convert to other units - on line 4, we convert to miles.

After printing the output on line 10-12, we see that the distance between the points is 1527 kilometres, or 949 miles.

And that's it! We have successfully calculated the distance between our points using GeoPy.

Summary

In this post, we have learned the following.

  • How to get geographical coordinates (latitude, longitude) from ZIP codes using the OpenStreetMap Search API.
  • How to plot our points as markers on interactive maps, using Folium.
  • How to calculate (roughly) the midpoint between two points.
  • How to calculate the distance between two points

Check the associated video on YouTube for bonus content, where we demonstrate how to draw lines between markers on the map.

Also make sure to check out the notebook on Github, if you want to play around with the code.

If you enjoyed this post, please subscribe to our YouTube channel and follow us on Twitter to keep up with our new content!

Please also consider buying us a coffee, to encourage us to create more posts and videos!

;