iA


Find nearest store page using PHP, mySQL and Google Maps

by David. Average Reading Time: about 4 minutes.

This is a step by step guide for building a find nearest/store locator for free in PHP just like Postcode Anywhere.

It requires PHP and a mySQL database. That’s all.

You don’t need Google Maps, but it helped me populating the database, so I’ve included it for completeness. The example I’m using is UK based, but provided Google have a postcode/zipcode to lat/long convertor for your country, it should work nicely.

Background Maths

Before we start, it’s time to go back to school with some background maths.

Don’t be tempted into Pythagorean territory here, because the world isn’t flat and the distance between lines of longitude gets smaller as we get closer to the poles. Also with the curvature of the earth for distances over say 15 miles, the sides of the triangle go from straight into an arc – not good.

Trying a little harder, we get to the Great Circle Distance method.

The Great Circle Distance Formula using radians:

r * acos[sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(lon2 - lon1)]

Where r is the radius of the earth e.g. r = 3963.0 miles

or
Great Circle Distance arccos formula

As I’m lazy, I want to add this directly into a SQL query that checks against lat/long values for my stores in my database.

Converted to (my)SQL syntax using lat/long in degrees becomes:

SELECT 3963.0 * ACOS(   SIN( lat1*PI()/180 ) * SIN( lat2*PI()/180 ) + COS( lat1*PI()/180 ) * COS( lat2*PI()/180 )  *  COS( (lon2*PI()/180) - (lon1*PI()/180) )   )  AS distance;

Assuming mySQL uses double precision, we can be assured that the calculation is reasonably accurate. Although the arcos formula can suffer from rounding errors at small distances. But in my experience, it works well when compared to other distance calculators (e.g.GMap Pedometer and Daft Logic Google Maps Distance Calculator).

Building the scripts

This example is unashamedly simplifed. The PHP is procedural and basic. It is for demonstration only – apply you own favourite MVC framework when working this up.

Create a simple mySQL database table for stores

CREATE TABLE `stores` (
  `id` bigint(20) NOT NULL auto_increment,
  `store_name` varchar(300) NOT NULL,
  `postcode` varchar(10) NOT NULL,
  `latitude` double NOT NULL,
  `longitude` double NOT NULL,
  `postal_address` text NOT NULL
  PRIMARY KEY  (`id`)
);

In reality, you’d probably want to add columns for phone no, web address, email etc.

Add some data to the table

I’m using real locations of Apple computer stores in the UK…

INSERT INTO `stores` (`id`, `store_name`, `postcode`, `latitude`, `longitude`, `postal_address`,)
VALUES
(
	(1, 'KRCS Mac computer store', 'NG1 7QQ', 52.952493, -1.140725, 'Victoria Centre, Nottingham'),
	(2, 'Apple Store', 'M4 3AJ', 51.51277, -2.14308, 'U23, New Cannon Street Mall,  Manchester'),
	(3, 'Apple Store', 'S9 1EA', 53.41442, -1.41089, '80 High Street, Meadowhell,  Sheffield')
);

The lat/long values can be populated manually (e.g. go get them from Google Maps) or automatically using a function similar to this:

/*
 * Get LatLong From Postcode
 *
 * @access  public
 * @param   string $postcode
 * @param   string $country
 * @param   string $gmapApiKey
 * @return  array(statusCode, accuracy, latitude, longitude);
 */
function getLatLongFromPostcode($postcode, $country, $gmapApiKey)
{
	/* remove spaces from postcode */
	$postcode = urlencode(trim($postcode));

	/* connect to the google geocode service */
	$file = "http://maps.google.com/maps/geo?q=$postcode,$country&output=csv&key=$gmapApiKey";

	$contents = file_get_contents($file);
	return explode(',', $contents);
}

…but you’ll need a Google Maps API key

Example for the Apple Store in Regent Street:

$geocode = getLatLongFromPostcode ('W1B 2EL', 'uk', 'ABQIAAAA2xzlSb6lvuy1qNdW6D87dBSX4wCNHpcq6aPi4BCCV9JuIkX6UhQgj9ZmrEF5FYpCKxFE5wNmrj');

Build the search/results page

<form class="center" action="./find-a-store.php" method="get">
	<label for="postcode">Postcode or Town</label>
	<input id="postcode" name="postcode" type="text" value="" />
	<input name="submit" type="submit" value="Search »" />
</form>

<?php
	if (isset($_REQUEST['postcode']))
	{
		$geocode = geocodePostcode ($_REQUEST['postcode'], $country, $gmapApiKey);

		$storeResults = array();

		if ($geocode[0] == '200')
		{
			$latitude = $geocode[2];
			$longitude = $geocode[3];

			// Print heading
			$htmlOutput .= "
				<div class=\"results\">
				<h2>Nearest stores to <em>$postcode</em></h2>
				<ol>";

			// Build the spherical geometry SQL string
			$earthRadius = '3963.0'; // In miles

			$sql = "
				SELECT
					ROUND(
						$earthRadius * ACOS(
							SIN( $latitude*PI()/180 ) * SIN( latitude*PI()/180 )
							+ COS( $latitude*PI()/180 ) * COS( latitude*PI()/180 )  *  COS( (longitude*PI()/180) - ($longitude*PI()/180) )   )
					, 1)
					AS distance,
					store_name,
					postcode,
					postal_address,
					latitude,
					longitude
				FROM
					stores
				ORDER BY
					distance ASC
				LIMIT 0, 10";

			// Search the database for the nearest agents
			$result = db_query($sql);
			while ($row = mysql_fetch_array($result))
			{
				array_push($storeResults, $row);
			}			

			foreach ($storeResults as $store)
			{
				// Print listings as an unordered list (structured: ol > li > h3 + div)
				$htmlOutput .= "
	<li>
<h3><em>{$store[store_name]} <em>{$store[distance]} miles.</em></em></h3>
<div><em>" . str_replace("n", ', ', $store['postal_address']) ."</em></div></li>";
			}
			$htmlOutput .= '
			</ol></div>';

			echo $htmlOutput;
		}
		else
		{
			echo "Problem with postcode."
		}

	} // if isset()
?>

By the way, properly cleanse $_REQUEST['postcode'] to prevent XSS attacks. The code above is for demo only.

Reference

Update

If you find this useful, but are still having trouble, here’s an excellent article — Creating a Store Locator with PHP, MySQL & Google Maps from Google.

10 comments on ‘Find nearest store page using PHP, mySQL and Google Maps’

  1. Thorsten says:

    Hi, for me as a relative beginner with php and mysql it would be very helpful if you could provide a complete ready to use download of your script with all necessary lines of code (db connect etc.). So it would be even easier to reproduce your nice work.

    All the best

  2. Is the gmaps api now fully geocoding UK postcodes. Last time i looked it was only doing partial postcode areas, for example a request for W1B 2EL would return coords for the centre of the W1B 2xx area.

  3. Andrew Paget says:

    Hi

    This lloks quite interesting. Do you have a sample page that includes the MySQL query and the search form that I could take a look at?

    Thank you

  4. Brandon says:

    Hey,

    I dont know if im missing something or not, but the build the search results page section definitely isnt complete?

    Maybe its because you say its basic and to provide your own MVC framework, but i dont actually know what one of those is.

    Can you in anyway provide a complete working demo which can be adjusted to work?

  5. David says:

    @Thorsten, @Andrew Paget, @Brandon:
    Thanks for the interest. I’ve added a little to the PHP code sample. It now includes the MySQL query and execution.

    Also, here’s an excellent alternative article excellent article — Creating a Store Locator with PHP, MySQL & Google Maps from Google.

    @Jamie Thompson:
    Sorry, not sure about that one I’m afraid.

  6. Jamie says:

    Hi your SQL statement:

    SELECT ROUND( 3963.0 * ACOS( SIN( *PI()/180 ) * SIN( latitude*PI()/180 ) + COS( *PI()/180 ) * COS( latitude*PI()/180 ) * COS( (longitude*PI()/180) – (*PI()/180) ) ) , 1) AS distance, store_name, postcode, postal_address, latitude, longitude FROM stores WHERE postcode = NR11HU ORDER BY distance ASC LIMIT 0, 10

    Causes the following error:
    You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘*PI()/180 ) * SIN( latitude*PI()/180 ) + COS( *PI()/180 ) * COS( latitude’

    Any advice?

  7. David says:

    @Jamie: Yes. The postcode needs to be in single quotes. See line 35 of the listing above – it’s been updated.

  8. Saikat says:

    I agree with Jamie, Google discards last 2 digits of postcode following a complaint from Royal Mail. Full postcode to latitude longitude data is Royal Mail proprietory and they sell it for £2000/year!

  9. David says:

    A full UK postcode location file has been leaked as a CSV. As Google’s postcode lookup isn’t that accurate, I’d recommend trying it out.

    For more details check the Guardian blog by @charlesarthur

  10. kevin davies says:

    Postcode.org.uk currently is geared for postcode to postcode distance calculations in the UK – similar travel distance calculators for other countries will be added in the near future.postcode distance calculator

Leave a Reply