Calculate Distance Between UK Postcodes Without a Database

In this article we will demonstrate how you can perform distance calculations between UK postcodes without the need for a database. A typical scenario for this functionality is when a centre post code, provided by a user, is compared against a set list of known locations. This could be for example when a chain of retail outlets wants to let visitors to their website know which outlets are closest to them. The typical solution involves sending a request to the web server or web service where a back end database is used to look-up the location of the user's postcode and return the latitude/longitude location.

This is required because the database needs to sort each UK postcode together with latitude and longitude requires approximately 1.7 million rows and therefore a large amount of storage space.

Performing this look-up on the client side is not practical because the local database size and amount of data to be downloaded is too big and impacts the speed and performance.

Moving away from use of databases, but still with a web server element, we have conceived a method to perform this look-up with only Javascript and text files on the web server. We will fist provide a live example, then go on to describe how it works. Finally, we will provide instructions on how you can implement this example on your own web server with the intention that you can adapt it to your own needs.

Live Example: Melodramatic Coffee Company

In this example, Melodramatic Coffee Company has 6 branches in the UK. The search will allow the user to input a postcode and find the nearest branch to them.

Melodramatic Coffee Company Logo

Find your nearest Melodramatic Coffee branch


How it Works

The full list of postcodes are put into CSV format ... [postcode],[latitude],[longitude] .These 1.7m (approx.) rows are then grouped by the first 3 characters into separate text files. For example:

  • AB1.txt
  • AB2.txt
  • AB3.txt
  • ...
  • E10.txt
  • E11.txt

The number of rows in these text files will vary, but the maximum is SW1 with around 13,000 rows, which is 600kb in size

PCDB Folder Example

Example folder of text files

PCDB File Example

Example contents of text file

When a look-up is required for any postcode, the first 3 characters are identified and this will indicate the correct text file to load. This text file is downloaded using a Javascript AJAX request, parsed into a string array, and then searched until the row with the specific postcode is found. At that point the corresponding latitude and longitude will be available to use.


//List of branches
var branches = [
["Maidenhead","SL6 8AD"],
["Glasgow","G12 8DR"],
["Leeds","LS1 5AS"],
["Manchester","M1 3LA"],
["London North","NW1 8NH"],
["Belfast","BT1 4QN"]
];

//Number of results to show
var int_showtop=10;
//Path to PCDB folder on webserver
var urlprefix="/downloads/";

var output;
var branches;

function ftn_search(pctofind)
{
if (pctofind=="")
{
return;
}
//if there are any markers on the map, remove them...
if (Markers)
{
for (i in Markers)
{
Markers[i].setMap(null);
}
}
output=document.getElementById("div_output");
output.innerHTML="Searching...";

branches.unshift([pctofind.toUpperCase(),pctofind.toUpperCase()]);
for(i=0; i<branches.length; i++)
{
if(typeof(branches[i][2]) == "undefined")
{
ftn_getlatlngforpc(branches[i][1].toUpperCase(),i);
}
}
ftn_testifallprocessed();
}

function ftn_getlatlngforpc(str_input,ipassed)
{
var xmlhttp = new XMLHttpRequest();
//strip out spaces...
str_input=str_input.replace(" ","");
//get first 3 chars...
var str_firstthree=str_input.substring(0,3);

var url = urlprefix+"pcdb/"+str_firstthree+".txt";

xmlhttp.onreadystatechange = function()
{
var str_returnvalue="";
if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
{
var arr=xmlhttp.responseText.split("\n")
for(var i = 0; i < arr.length; i++)
{
if (arr[i].split(",")[0]==str_input)
{
str_returnvalue= arr[i].split(",")[1]+","+arr[i].split(",")[2];
branches[ipassed][2]=str_returnvalue;
ftn_testifallprocessed();
}
}
//Not Found
if (str_returnvalue=="")
{
if (ipassed==0)
{
delete branches[ipassed][2];
output.innerHTML="Postcode Not Found";
//pop the first element
branches.shift();
}
else
{
branches[ipassed][2]="0,0";
ftn_testifallprocessed();
}
}
}
}
xmlhttp.open("GET", url, true);
xmlhttp.send();
}

function ftn_testifallprocessed()
{
var bool_check=true;
//check until all 2nd elements are defined and have a lat/lng
for(i=0; i<branches.length; i++)
{
if(typeof(branches[i][2]) == "undefined")
{
bool_check=false;
}
}
if (bool_check)
{
for(i=1; i<branches.length; i++)
{
//find distance between this and the base point
var d=distance(branches[0][2].split(",")[0],branches[0][2].split(",")[1],branches[i][2].split(",")[0],branches[i][2].split(",")[1],"K");
branches[i][3]=d;
}

if (document.getElementById("cb_showmap").checked)
{
ftn_showmap(branches);
}

//pop the first element
branches.shift();
branches.sort(compareThirdColumn);

output.innerHTML="Closest Branch:";
output.innerHTML+="<br/>";

for(i=0; i<branches.length && i<int_showtop; i++)
{
output.innerHTML+=(i+1) + ") " + branches[i][0] + " ("+branches[i][3].toFixed(1)+" km)";
output.innerHTML+="<br/>";
}
}

function compareThirdColumn(a, b)
{
if (a[3] === b[3])
{return 0;}else {return (a[3] < b[3]) ? -1 : 1;}}
}

function distance(lat1, lon1, lat2, lon2, unit) {
var radlat1 = Math.PI * lat1/180
var radlat2 = Math.PI * lat2/180
var radlon1 = Math.PI * lon1/180
var radlon2 = Math.PI * lon2/180
var theta = lon1-lon2
var radtheta = Math.PI * theta/180
var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
dist = Math.acos(dist)
dist = dist * 180/Math.PI
dist = dist * 60 * 1.1515
if (unit=="K") { dist = dist * 1.609344 }
if (unit=="N") { dist = dist * 0.8684 }
return dist
}

How to Implement

Download the following files:

pcdb.zip

pcdb-example.zip

Extract these ZIP files and place the pcdb folder inside the folder where index.htm is, then upload to your webserver. Browse to index.htm to see the working example.

Relevant Links

Leaflet Maps

Version History

Version Date Description
Version 1.0 22/05/2015 Page Created
Version 1.1 19/12/2015 Bug fix - Postcode wasnt found due to a space. Needed to strip space out before comparing

Comments For This Page

Cool trick! Should put it behind your own server and let users call it as an API
On 6th March 2018

Add your own comment below and let others know what you think: