How to search by nearest UK postcode in PHP
Often clients ask me to create a function where by a visitor can come onto their website, there they can enter in their postcode, the idea is to then display the closest location based on locations in a database.
Aim: To display details of a predefined location that is closest to the visitor based on the postcode they enter.
To achieve this you must cross reference their postcode and your locations with a numerical value (such as coordinates).
This means we require a database of postcodes and such numerical values.
This gives us a few choices:
- Royal Mail’s PAF – This is very costly, I’d not recommend this.
- Jibble.org’s postcodes.zip – Not been updated since 2004, may no longer be online.
- easypeasy.com’s Postcode database – Also has lots of information on how to use such a database
- freethepostcode.org’s Database – Kinda crappy site, with limited info, but has a database.
- New Popular Edition Maps – A very popular postcode database
- kubelabs.com ‘s database – Fairly new, notable.
- Google Maps UK – If all else fails a bit of API magic and this may just do the trick. (Also see: Map Maker)
For some of you Google Maps might be the most appropriate option, however for me, my client is particularly fussy and only wants to display the details of the closest location, not the details of all the locations. Using Google Maps would require plotting all the locations on the map before you even enter the postcode.
Having said all this, a quick browse through the “External Links” on the UK Postcodes Wikipedia entry, and I quickly found an article by Paul Jenkins entitled UK Post Code Distance Calculation in PHP, which is fantastic, you can even download it here (uk_postcode_calc.zip).
After a short examination it seems this does exactly what it says on the tin, and simply calculates the distance.
However, with a quick google for php distance calculation, you can quickly find that there are more refined equivalents of the distance calculation. I thought it might be a good idea to use one of those instead.
After a bit of tweaking, here’s what I came up with in the end:
function distance($lat1, $lon1, $lat2, $lon2, $u=’1′) {
$u=strtolower($u);
if ($u == ‘k’) { $u=1.609344; } // kilometers
elseif ($u == ‘n’) { $u=0.8684; } // nautical miles
elseif ($u == ‘m’) { $u=1; } // statute miles (default)$d=sin(deg2rad($lat1))*sin(deg2rad($lat2))+cos(deg2rad($lat1))*cos(deg2rad($lat2))*cos(deg2rad($lon1-$lon2));
$d=rad2deg(acos($d));
$d=$d*60*1.1515;$d=($d*$u); // apply unit
$d=round($d); // optional
return $d;
}
So, that’s the hard parts done (database and maths), next is simply a case of using this information to “find the closest” from the postcode we input to an array of postcodes we supply…
To find the “closest” postcode, effectively what we’re trying to do is find the “shortest” distance between the postcodes, or, simply the smallest number in the results, assuming we put the results into an array with the key as the postcode and the distance as the value.
All we have to do is create a simple script that will find the smallest number in a given array, then return the appropriate key. Simple!
function closest ($needle,$haystack) {
if (!$needle || !$haystack) { return; }
if (!is_array($haystack)) { return; }$smallest=min($haystack); //smallest value
foreach ($haystack as $key => $val) {
if ($val == $smallest) { return $key; }
}
}
The above script does exactly what we want, using the “min” function we can quickly work out what we need to return.
The only task left is to bind all this together, we need to create two functions that will:
- Get the distance using the postcode to get the longitude and latitude from the database.
- Create an array with the postcodes as the keys, and the distance as the values.
Very simple!
Function 1, Postcode Distance
function postcode_distance ($from,$to) {
// Settings for if you have a different database structure
$table=’postcodes_uk’;
$lat=’lat’;
$lon=’lon’;
$postcode=’postcode’;// This is a check to ensure we have a database connection
if (!@mysql_query(‘SELECT 0’)) { return; }// Simple regex to grab the first part of the postcode
preg_match(‘/[A-Z]{1,2}[0-9R][0-9A-Z]?/’,strtoupper($from),$match);
$one=$match[0];
preg_match(‘/[A-Z]{1,2}[0-9R][0-9A-Z]?/’,strtoupper($to),$match);
$two=$match[0];$sql = “SELECT `$lat`, `$lon` FROM `$table` WHERE `$postcode`=’$one'”;
$query = mysql_query($sql);
$one = mysql_fetch_row($query);$sql = “SELECT `$lat`, `$lon` FROM `$table` WHERE `$postcode`=’$two'”;
$query = mysql_query($sql);
$two = mysql_fetch_row($query);$distance = distance($one[0], $one[1], $two[0], $two[1]);
// For debug only…
//echo “The distance between postcode: $from and postcode: $to is $distance miles\n”;return $distance;
}
Function 2, Postcode Closest
function postcode_closest ($needle,$haystack) {
if (!$needle || !$haystack) { return; }
if (!is_array($haystack)) { return; }foreach ($haystack as $postcode) {
$results[$postcode]=postcode_distance($needle,$postcode);
}
return closest($needle,$results);
}
So, with that done, place the 4 above functions into a file such as “postcode.php”, ready for use in the real world…
Test case:
<?php
include_once(‘postcode.php’);
if ($_POST) {
include_once(‘db.php’);
$postcodes=array(‘TF9 9BA’,’ST4 3NP’);
$input=strtoupper($_POST[‘postcode’]);
$closest=postcode_closest($input,$postcodes);
}if (isset($closest)) {
echo “The closest postcode is: $closest”;
}
?><form action=”” method=”post”>
Postcode: <input name=”postcode” maxlength=”9″ /><br />
<input type=”submit” />
</form>
You can download this script here: postcode_search.phps
Note: In the above test case, I have a “db.php” file which contains my database details and starts a database connection. I suggest you do the same.
Ensure you have your database populated, you should be able to use Paul Jenkins’s UK Postcode csv, allowing you to use your own table structure.
Well, that’s all folks, I can now use this script to provide any locations that match the “closest” postcode.
Enjoy!
Warning: Declaration of Social_Walker_Comment::start_lvl(&$output, $depth, $args) should be compatible with Walker_Comment::start_lvl(&$output, $depth = 0, $args = Array) in /Users/wade/Sites/hm2k.org/wp-content/plugins/social/lib/social/walker/comment.php on line 18
Warning: Declaration of Social_Walker_Comment::end_lvl(&$output, $depth, $args) should be compatible with Walker_Comment::end_lvl(&$output, $depth = 0, $args = Array) in /Users/wade/Sites/hm2k.org/wp-content/plugins/social/lib/social/walker/comment.php on line 42
Hi,
Thought I’d let you know that Google now allows Geocoding of UK postcodes via their API. This means that you can quite easily build nearest store finder without a database of UK postcodes. If you’ll allow the plug, I have written a tutorial on the whole thing over at my site: http://aciddrop.com/2007/12/17/quick-easy-and-free-nearest-store-postcodezip-finder/. Perhaps you would consider including it in your list of choices?
Cheers,
Leon
Nice work people!
nice to see some actual ways to implement this, i have tried working with google maps, but getting something which is automated is a real pain.Thanks to one of the links above i have a decent postcode list to work with.
Thanks for this but are you you able to provide this as a download aas my php is very limited?
Thanks in advance.
Thank you for this write up, it has been extremely useful!
This looks great, exactly what i am after!
I am a newbie to PHP so was wondering if you could help me?
I have created my Database and was wondering how i import the CSV you specified into the table? For some reason when i import it, nothing happens.
One more thing, how would i make the search open a new page once the query has been processed?
[…] know the basics. I managed to find hopefully something similar to what they are after on this site: HM2K.com ? How to search by nearest UK postcode in PHP but am not sure how to implement. Can anyone shed any light on how to go about this? I have tried […]
LS61DH:53.8139004149156:-1.57013758343434
LS61DJ:53.8147992884429:-1.57012838559871
LS61DL:53.813911262566:-1.57317538719233
LS61DN:53.8147992884429:-1.57012838559871
LS61DP:53.8166024690639:-1.57162898755877
LS61DQ:53.8156981618341:-1.57011918726391
LS61DR:53.8139004149156:-1.57013758343434
LS61DS:53.8130069745156:-1.57166564990514
LS61DT:53.8130123885475:-1.57318451953679
LS61DU:53.813911262566:-1.57317538719233
LS61DW:53.8148155316019:-1.57468518947338
LS61DX:53.812113514393:-1.57319365138567
LS61DY:53.8130123885475:-1.57318451953679
It’s about 6 months old, so still pretty much up to date.
It has 2.3 million rows, and is 72 MB uncompressed.
If anyone would like a copy, the price is 80 pounds. My email is [email protected]
What an increadibly useful article. I can’t thank you enough for this! I was about to tell my client that they would have to look elsewhere but now I can not only provide the functionality, but ask them what list they want to use (and tell them that they dont have to spend £££’s buying the post office’s list)!
I will ensure that any info that I find during ‘code tweaking’ will be posted here.
Once again, thanks for such a fantastic tutorial.
I dont like royal mail!
Hey,
I have just tried out the distance function at the top. I copied and pasted the code then modified it with the variable names I originally used. However it seems that the distance between DT11 and DT11 is just over 5 miles. Any idea why?
Please email me if you have an idea.
Regards,
Richard
hmm scrap that last message – I simply didnt round it. rounding to 2 decimal places made it 0. However the fact that it requires rounding is odd – it should be 0 without rounding, shouldnt it?
hi guys,
I can’t download the code, if i click on ‘postcode_search.phps’ link the whole page gets refreshed. Could any of you tell me how can i download the script.
Many Thanks
hi hm2k,
thank you very much about this sharing, this is actually what I need, it help me understand how this work, and your code is very helpful 😀
thank “Leon Chevalier”, too.
Thank you all guy 🙂
This is great script but i am little bit confuse in miles search please any body help me how i can input latitude longitude and with in miles ….
That pjenkins.co.uk doesn’t exist any more. Is there anywhere else I can download it from? 🙁
None of those links are good enough any more – should be updated with more up to date resources. Postcodes change every week, but these are anything but 5 years old.
any other links where I can find a working script
I have been looking at this for a while, trying to work the maths out, I have a new postcode database provided by the UK’s Ordanace Survey OpenData set CodePoint Open (http://www.ordnancesurvey.co.uk/oswebsite/produts/code-point-open/)