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.
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.
Find your nearest Melodramatic Coffee branch
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
Example folder of text files
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
}
Download the following files:
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.
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. |