Calculate (road travel) distance between postcodes/zipcodes python
Asked Answered
E

4

10

I have a csv file with start and end postcodes (UK equivalent of US zipcodes) and would like to compute simple distance, road travel distance and travel time between the two. I guess the way to go would be to use Google maps in one way or another. I first tried using some spreadhsheet and the following url http://maps.google.com/maps?saddr="&B2&"&daddr="&A2&" but

  1. I do not know how to retrieve the resulting distance from google maps
  2. I would like to know some more pythonic way to work this out
Enslave answered 25/5, 2017 at 8:57 Comment(0)
C
15

The main issue with finding a distance between 2 postcodes is that they aren't designed for it.

For the purposes of directing mail, the United Kingdom is divided by Royal Mail into postcode areas. -Wikipedia

A postcode by itself provides no useful information, so you are correct you need help from an external source. The Google maps service at http://maps.google.com is of no use, as it's not designed for you to retrieve information like this.


Option 1 - Google Maps API

The Google Maps API is feature packed and will provide you with a lot of options. The link above is to the Distance Matrix API, which will help with working out distances between 2 points. The results from this will be based on travel (so driving distance), this may or may not be what you want.

Example

Python 3

import urllib.request
import json

res = urllib.request.urlopen("https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=SE1%208XX&destinations=B2%205NY").read()
data = json.loads(res.decode())
print(data["rows"][0]["elements"][0]["distance"])
# {'text': '127 mi', 'value': 204914}

Note: Google Maps API is subject to usage limits.

Option 2 - Do it yourself with postcodes.io

postcodes.io has a nice API backed by a public data set. Example lookup. Results are in JSON which can be mapped to a Python dictionary using the json module. The downside here is it provides no way to check distance, so you will have to do it yourself using the Longitude and Latitude returned.

Example

Python 3

import urllib.request
import json

res = urllib.request.urlopen("http://api.postcodes.io/postcodes/SE18XX").read()
data = json.loads(res)
print(data["result"]["longitude"], data["result"]["latitude"])
# -0.116825494204512 51.5057668390097

Calculating distance

I don't want to get too much into this because it's a big topic and varies greatly depending on what you're trying to achieve, but a good starting point would be the Haversine Formula, which takes into account the curvature of the Earth. However, it assumes the Earth is a perfect sphere (which it's not).

The haversine formula determines the great-circle distance between two points on a sphere given their longitudes and latitudes. Important in navigation, it is a special case of a more general formula in spherical trigonometry, the law of haversines, that relates the sides and angles of spherical triangles.

Here is an example of it implemented in Python: https://mcmap.net/q/109993/-haversine-formula-in-python-bearing-and-distance-between-two-gps-points

Coleoptile answered 25/5, 2017 at 9:48 Comment(3)
Absolutely right. Geographic distance is not the same as Euclidean distance. For example, longitudinal distance is calculated as a function of the latitude. This makes sense when you think about travelling from the line of 1 degree East to the line 2 degrees East; at the equator this is well over 50 miles, but very close to the North or South poles it would be a tiny distance. The Haversine formula accounts for this. Also see Vincenty's Formulae for equations on the surface of the Earth.Atalante
@Coleoptile thanks! The python code on how to call Google API and read the JSON is exactly what I was looking for. However, your code did not work for me (python 3.5): data = json.loads(res) was giving me TypeError: the JSON object must be str, not 'bytes'. I now modified your answer to be data = json.loads(res.decode()) and it works as it was supposed to.Enslave
Need to thank you again: I ended up using postcodes.io as well. This answer has been twice useful!Enslave
T
25

The distance between postal codes can be obtained with the pgeocode library. Unlike the above response, it does not query a web API, and is therefore more suitable for processing large amounts of data,

 >>> import pgeocode

 >>> dist = pgeocode.GeoDistance('GB')
 >>> dist.query_postal_code('WC2N', 'EH53')
 536.5  # retured distance in km

More information about these postal codes, including latitude and longitude, can be queried with,

 >>> nomi = pgeocode.Nominatim('GB')
 >>> nomi.query_postal_code(['WC2N', 'EH53'])
   postal_code country code                                     place_name  \
 0        WC2N           GB                                         London   
 1        EH53           GB  Pumpherston, Mid Calder, East Calder, Oakbank   

   state_name state_code     county_name county_code community_name  \
 0    England        ENG  Greater London    11609024            NaN   
 1   Scotland        SCT    West Lothian         WLN            NaN   

   community_code  latitude  longitude  accuracy  
 0            NaN   51.5085  -0.125700       4.0  
 1            NaN   55.9082  -3.479025       4.0

This uses the GeoNames postal code dataset to get the GPS coordinates, then computes the Haversine (great circle) distance on those. Most countries are supported.

In the particular case of Great Britain, only the outward codes are included in the GB dataset, the full dataset is also available as GB_full but it is currently not supported in pgeocode.

Telemachus answered 8/11, 2018 at 23:15 Comment(2)
Would this only cover simple distance, or can it do road travel distance and travel time as well?Subjective
This is for geographical distances only.Telemachus
C
15

The main issue with finding a distance between 2 postcodes is that they aren't designed for it.

For the purposes of directing mail, the United Kingdom is divided by Royal Mail into postcode areas. -Wikipedia

A postcode by itself provides no useful information, so you are correct you need help from an external source. The Google maps service at http://maps.google.com is of no use, as it's not designed for you to retrieve information like this.


Option 1 - Google Maps API

The Google Maps API is feature packed and will provide you with a lot of options. The link above is to the Distance Matrix API, which will help with working out distances between 2 points. The results from this will be based on travel (so driving distance), this may or may not be what you want.

Example

Python 3

import urllib.request
import json

res = urllib.request.urlopen("https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=SE1%208XX&destinations=B2%205NY").read()
data = json.loads(res.decode())
print(data["rows"][0]["elements"][0]["distance"])
# {'text': '127 mi', 'value': 204914}

Note: Google Maps API is subject to usage limits.

Option 2 - Do it yourself with postcodes.io

postcodes.io has a nice API backed by a public data set. Example lookup. Results are in JSON which can be mapped to a Python dictionary using the json module. The downside here is it provides no way to check distance, so you will have to do it yourself using the Longitude and Latitude returned.

Example

Python 3

import urllib.request
import json

res = urllib.request.urlopen("http://api.postcodes.io/postcodes/SE18XX").read()
data = json.loads(res)
print(data["result"]["longitude"], data["result"]["latitude"])
# -0.116825494204512 51.5057668390097

Calculating distance

I don't want to get too much into this because it's a big topic and varies greatly depending on what you're trying to achieve, but a good starting point would be the Haversine Formula, which takes into account the curvature of the Earth. However, it assumes the Earth is a perfect sphere (which it's not).

The haversine formula determines the great-circle distance between two points on a sphere given their longitudes and latitudes. Important in navigation, it is a special case of a more general formula in spherical trigonometry, the law of haversines, that relates the sides and angles of spherical triangles.

Here is an example of it implemented in Python: https://mcmap.net/q/109993/-haversine-formula-in-python-bearing-and-distance-between-two-gps-points

Coleoptile answered 25/5, 2017 at 9:48 Comment(3)
Absolutely right. Geographic distance is not the same as Euclidean distance. For example, longitudinal distance is calculated as a function of the latitude. This makes sense when you think about travelling from the line of 1 degree East to the line 2 degrees East; at the equator this is well over 50 miles, but very close to the North or South poles it would be a tiny distance. The Haversine formula accounts for this. Also see Vincenty's Formulae for equations on the surface of the Earth.Atalante
@Coleoptile thanks! The python code on how to call Google API and read the JSON is exactly what I was looking for. However, your code did not work for me (python 3.5): data = json.loads(res) was giving me TypeError: the JSON object must be str, not 'bytes'. I now modified your answer to be data = json.loads(res.decode()) and it works as it was supposed to.Enslave
Need to thank you again: I ended up using postcodes.io as well. This answer has been twice useful!Enslave
S
4

This looks like the perfect resource for you (they provide lat and long values for each postcode in the UK, in various formats): https://www.freemaptools.com/download-uk-postcode-lat-lng.htm and in particular this CSV file (linked in the same page): https://www.freemaptools.com/download/full-postcodes/ukpostcodes.zip

Once you match geographical coordinates to each postcode you have (out of the scope of this question), say you'll have a table with 4 columns (i.e. 2 (lat, long) values per postcode). You can compute the distances using numpy. Here's an example:

import numpy as np
latlong = np.random.random((3,4))
# Dummy table containing 3 records, will look like this:
# array([[ 0.258906  ,  0.66073909,  0.25845113,  0.87433443],
#        [ 0.7657047 ,  0.48898144,  0.39812762,  0.66054291],
#        [ 0.2839561 ,  0.04679014,  0.40685189,  0.09550362]])
# The following will produce a numpy array with as many elements as your records
# (It's the Euclidean distance between the points)
distances = np.sqrt((latlong[:, 3] - latlong[:, 1])**2 + (latlong[:, 2] - latlong[:, 0])**2)
# and it look like this:
# array([ 0.21359582,  0.405643  ,  0.13219825])
Sylas answered 25/5, 2017 at 9:30 Comment(2)
Using the Euclidean distance is incorrect in this case, Haversine (great-circle) distance should be used instead.Telemachus
Agreed. This is an approximate approach.Sylas
M
1

The simplest way to calculate the distance between two UK postcodes is not to use latitude and longitude but to use easting and northing instead.

Once you have easting and northing you can just use Pythagoras's theorem to calculate the distance, making the maths much simpler.

  1. Get the easting and northing for the postcodes. You can use Open Postcode Geo for this.

  2. Use the below formula to find the distance:

sqrt(pow(abs(easting1 - easting2),2) + pow(abs(northing1 - northing1),2))

This example is from MySQL but you should be able to find similar functions in both Excel and Python:

  • sqrt(): Find the square root.
  • pow(): Raise to the power of.
  • abs(): Absolute value (ignore sign).
Managing answered 25/5, 2017 at 10:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.