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 postcode, 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 coordinates.

This is required because the database needs to store each UK postcode together with its latitude and longitude, which 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 the amount of data to be downloaded are too large, impacting speed and performance.

Moving away from the use of databases, but still with a web server element, we have conceived a method to perform this look-up using only JavaScript and text files on the web server.

We will first 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:

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].replace(" ","")	==str_input)
				{
					str_returnvalue= arr[i].split(",")[1]+","+arr[i].split(",")[2];
					if (branches[ipassed])
					{
						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)
		{
			document.getElementById('map-canvas').style.display = "block";
			ftn_showmap(branches);
		}
		else
		{
			document.getElementById('map-canvas').style.display = "none";
		}	
		
		//pop the first element
		branches.shift();
		branches.sort(compareThirdColumn);
				
		output.innerHTML="Closest Branch:";
		output.innerHTML+="
"; 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+="
"; } } 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 wasn't 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 Comment

There's no need to create an account or provide unnecessary details. Just your comment.