How to build your own UK nearest search using PostCoder Web SOAP

Introduction

We are noticing an increasing demand from customers for a "Find my Nearest" function, particularly when associated with maps.

Providing a "Find my Nearest" function with a map of a location helps your customers to find out where the nearest suppliers of your products are located, is more professional and let's be honest it looks cool to have a map on your web page!

This article outlines how to incorporate a "Find my Nearest" function with maps into your web site or application using the PostCoder Web SOAP service. Code snippets are given in PHP, using the nuSOAP library which is distributed with the PostCoder Web SOAP examples. The database interface is MySQL, but could easily be adapted for other databases.

The example code provided with PostCoder Web SOAP is available in PHP (4 and 5), ASP.NET (C#, VB), ASP, Perl, Java and ColdFusion. The principles in this article can be used with any of the example languages, but here we'll use PHP. In order to finish up with a fully working set of pages, you should create all your pages in the same directory as the PostCoder Web SOAP PHP examples, because there are some dependent style, script and image files included with the examples.

Note: Style and accessibility code has been omitted for clarity.

Build Your Address Database

First of all, you need to set up a (or augment an existing) database which contains details of all the suppliers which you want your customers to be able to search on.

The fields highlighted are those which will be populated by the PostCoder Web SOAP service.

Grid references are needed to calculate the distance between postcodes, and the latitude and longitude values are use for hooking into mapping systems such as Google Maps.

MySQL Table Editor

Figure 1 - Example database

The code required to populate the database is beyond the scope of this article, but for convenience here is the SQL to create the schema for the MySQL database illustrated above:

CREATE TABLE `suppliers` (
  `SupplierID` varchar(36) NOT NULL,
  `SupplierName` varchar(40) NOT NULL,
  `Telephone` varchar(25) default NULL,
  `EmailAddress` varchar(255) default NULL,
  `WebSite` varchar(255) default NULL,
  `Premise` text,
  `Dependent_street` varchar(80) default NULL,
  `Street` varchar(80) default NULL,
  `Double_dependent_locality` varchar(35) default NULL,
  `Dependent_locality` varchar(35) default NULL,
  `Post_town` varchar(30) default NULL,
  `Postcode` varchar(12) default NULL,
  `GridEasting` int(11) default '0',
  `GridNorthing` int(11) default '0',
  `Latitude` varchar(16) default NULL,
  `Longitude` varchar(16) default NULL,
  PRIMARY KEY  (`SupplierID`),
  KEY `GridNorthIndex` (`GridNorthing`),
  KEY `GridEastIndex` (`GridEasting`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

Populate your database

To populate your database with addresses and/or grid references there are two options:

Add addresses (optional)

If you have a relatively small number of records, then use the PostCoder Web SOAP getThrfareAddresses function to lookup the address information for each of your suppliers and add your own premise information. Alternatively use the PostCoder Web SOAP getPremiseList function to lookup the full address for each of your suppliers including the premise information. You only need to lookup addresses if you wish to supply address details to users of your "Find my Nearest" search and you do not already have such address information available. You may on the other hand just provide web site addresses, where the target web site contains address details for the supplier and/or an online purchasing system.

The PostCoder Web SOAP examples contain a fully working example of the getThrfareAddresses and getPremiseList functions. However, to populate your database you will only need the server side code such as (for the PHP language) the getThrfareAddressesExample.php and getPremiseListExample.php pages.

Add Geographic Info

If you have a relatively small number of records, then use the PostCoder Web SOAP getGrids function to lookup the geographic information for each of your suppliers.

Aside from the usual code required to set up SOAP (see later in this article for example code "initiate_SOAP_Service") and check for errors, one line of code is required to retrieve the geographic info for a postcode:

$result = $proxy->getGrids($postcode,$identifier,$username,$password);

Then the following code fragment can be used to extract the geographic info from the SOAP payload into your own database (note: you will need to insert the username and password for your database):

<?php
$ACLnumRecords = $result['number_results'];
if (!empty($ACLnumRecords) && $ACLnumRecords != "0")
{
//connect to the database
$db=mysql_connect ("localhost", "username", "password") or exitScript();

//-select the database to use
$mydb=mysql_select_db("nearest");
//-update the customer record
	$update_customer = "UPDATE suppliers SET
GridEasting = '$result[grideast]',
GridNorthing = '$result[gridnorth]',
Latitude = '$result[latitude_erts89]',
Longitude = '$result[longitude_erts89]',
WHERE SupplierID = '$Supplier_ID'";
//-run  the query
$result=mysql_query($update_customer);
}
?>

You will notice that we have also loaded the latitude and longitude co-ordinates, in this case based on the ETRS89 datum. The values correspond to the latitude and longitude values used in Global Positioning Systems in the UK and Europe. Note that many online mapping systems such as Google Maps and Multimap use GPS maps and hence the ETRS89 latitude and longitude co-ordinates should be used for lookups on these systems.

The PHP exitScript function called in the "$db=mysql_connect ("localhost", "username", "password") or exitScript();" line is a generic error handling function called when the server-side script should be terminated and only a simple error message returned:

/*********************************************************************
* exitScript($message)
* Exit script, but in a tidy fashion. Uses default message unless over-ridden
*********************************************************************/
function exitScript($message='Unable to conduct Nearest search, please report to site owner')
{
	showMessage($message);
	toggleFormButton();
	exit("

We will make further use of this function later on in this article.

Search for nearest supplier

Once your database has been primed with the grid references, the database can be queried for the nearest suppliers to a given postcode.

For the purposes of this example, we are going to use two pages - a client-side (html) page which contains the basic GUI code, and a server-side (php) page which handles the communication with the PostCoder Web SOAP service and the database.

Client-Side Page

The client-side page contains two forms, one hidden form used to communicate with the server-side page:

<!-- Hidden form to allow easy communication with server-side page -->
<form name="PostCoderForm" method="post" action="NearestExample.php" target="nearest_result">
<input type="hidden" name="postcode" />
<input type="hidden" name="radius" />
<input type="hidden" name="limit_results" />
<input type="hidden" name="identifier" value="Example - Find My Nearest" />
</form>

And another form used to collect the user's Postcode, the radius to limit the search to and the number of results to return:
<form name="NearestForm">
	<fieldset>
	<legend>Nearest Search</legend>
	<div style="float:left;">
		<div class="row">
			<label>Postcode:</label>
			<input maxlength="10" tabindex="1" type="text" name="pcode" size="9" onkeypress="if ((window.event) && (window.event.keyCode==13)) findaddress(this.value)" />
			<input id="searchbutton" tabindex="4" type="button" class="button" value="Find my Nearest"
					onclick="findaddress(this.form.pcode.value,this.form.radius.value,this.form.limit_results.value)" />
		</div>
    		<div class="row">
			<label>Radius (miles):</label>
			<input maxlength="4" tabindex="2" type="text" name="radius" size="4" value="1"/>
 		</div>
 		<div class="row">
			<label>Max Items:</label>
			<input maxlength="4" tabindex="3" type="text" name="limit_results" size="4" value="10"/>
		</div>
		<div class="row">
			<div align="center" id="message" class="message"> </div>
		</div>
		<iframe src="blank.html" class="nearestframe" id="nearest_result" name="nearest_result" width="340" height="340">
		</iframe>
	</div>
	</fieldset>
</form>

Note that we have included an element called "message" to handle error messages received from the server-side page:

		<div class="row">
			<div align="center" id="message" class="message"> </div>
		</div>
Nearest Search 1

Figure 2 - Client Side Page

On a production site, you might want to make it easier for the user to choose by offering options for radius and number of results on drop-downs with suitable bands.

Note that within this form there is an iframe called "nearest_results" which receives the results of the call to the server-side page - which would be a list of suppliers and associated information as retrieved from your database, in ascending order of distance from the supplied Postcode.

The javascript function "findaddress" and associated helper functions "showMessage" and "toggleFormButton" need to be included in you client-side page (between <script type="text/javascript"></script> tags):

function findaddress(pcode, radius, limit_results){
	if (pcode != ''){
	    // clear the message if any
		showMessage(' ');
		// disable the button
		toggleFormButton(true);
		// submit the form and data
	    document.PostCoderForm.postcode.value = pcode;
	    document.PostCoderForm.radius.value = radius;
	    document.PostCoderForm.limit_results.value = limit_results;
		document.PostCoderForm.submit();
	}
}

The "findaddress" function loads the "PostCoderForm" form with the parameters for the SOAP request and triggers the submission of the form.

function showMessage(str){
	if(document.getElementById){
		document.getElementById('message').innerHTML = str;
	}
	else if (document.all){
    		document.all['message'].innerHTML = str;
	}
}

The "showMessage" function is used to display any error/warning messages in the "message" slot we cunningly included in the form earlier.

function toggleFormButton(status) {
	var theform = document.NearestForm;
	if (status == true)
			theform.searchbutton.value = 'Searching...';
		else
			theform.searchbutton.value = 'Find my Nearest';
}

The "toggleFormButton" function provides feedback to the user that the search is in progress by changing the text on the "Find My Neareast" button.

Server-side Page

The server-side page (which we called "NearestExample.php" in the client-side page) accepts the following parameters from the client-side page, and returns either a list of matching records from your database, or a suitable error message:

The following php code is used to input these parameters from the client-side page:

// Get parameters from client-side page
$identifier = $_POST['identifier'];
$postcode = $_POST['postcode'];
$radius = $_POST['radius'];
$limit_results = $_POST['limit_results'];

The next step is to initialise the SOAP service. This is fairly standard code for setting up a SOAP service using nuSOAP:

// Enter Your PostCoder Web SOAP Username and Password Below.
$username = 'MyPostCoderWebSOAPUsername';
$password = 'MyPostCoderWebSOAPPassword';
initiate_SOAP_Service($proxy);
................
.................

/*********************************************************************
* initiate_SOAP_Service()
* Sets up the SOAP service.
*********************************************************************/
function initiate_SOAP_Service(&$proxy)
{
/* Setting the $useWSDLcache variable below to TRUE may improve the
overall	speed of the service by creating a cached version of the
WSDL document on your server. To use this feature, the user on your
server that runs this script (apache, httpd, etc) must have write access in the
directory this script lies. */
$useWSDLcache = TRUE;

	// Get the WSDL Document from the PostCoder Web SOAP server.
	$wsdlPath = "http://www.postcoderwebsoap.co.uk/websoap/websoap.php?wsdl";

	// Pull in the NuSOAP code
	require('nusoap/lib/nusoap.php');

	if ($useWSDLcache){
		// include the nusoap wsdl cache classes
		require('nusoap/lib/class.wsdlcache.php');

		// use a wsdl cache
		$cache = new wsdlcache(".",604800); //1 week
		$wsdl = $cache->get($wsdlPath);
		if (is_null($wsdl)){
			$wsdl = new wsdl($wsdlPath);
			$cache->put($wsdl);
		}

		// create a soap client from WSDL cache
		$client = new soap_client($wsdl,TRUE);
	}
	else {
		// Create the client from WSDL document.
		$client = new soap_client($wsdlPath,'wsdl');
	}

	// Check for an error
	if ($err = $client->getError()) exitScript();

	// get proxy client.
	$proxy = $client->getproxy();

	// Check for an error
	if ($err = $proxy->getError()) exitScript();
}

Next, after the call to initiate_SOAP_Service($proxy);, we add some php code to lookup the grid references for the Postcode using the PostCoder Web SOAP service and if successful search the database for the nearest items and generate the results for the client-side page:

// Find grids for input postcode
	if (getGrids($proxy, $postcode, $identifier, $username, $password, $result)) {
		// Get the list of Nearest Items
		getNearestItems($result['grideast'],$result['gridnorth'], $radius, $limit_results);
	}
toggleFormButton();

Note that we use the $proxy object returned from the "initiate_SOAP_Service" function. And as this is the last part of the main body of this script, we call a function which resets the client side form button.

The getGrids function is fairly simple, most of the code being error handling. The $proxy object is called to handle the call the SOAP service, being passed the postcode, identifier (of the client-side page, useful for statistics), and username/password for the SOAP service. The result passed back is an array of values including grid references and latitude/longitude co-ordinates.

/*********************************************************************
* getGrids()
* Get the Grid References and other geo data for a given postcode
*********************************************************************/
function getGrids($proxy, $postcode, $identifier, $username, $password, &$result)
{
	// call the SOAP method for getGrids.
	$result = $proxy->getGrids($postcode,$identifier,$username,$password);
	if ($proxy->fault) { // Check for a fault
	    exitScript();
	} elseif ($err = $proxy->getError()) { // Check for errors
	    exitScript();
	} elseif ($result['error_code'] != '0'){ // No SOAP error, but some other error
	    exitScript();
	} elseif ($result['retcode'] == '3'){ // Invalid Postcode
	    exitScript('Invalid Postcode ' . $postcode . ' - please check and try again');
	} elseif ($result['retcode'] == '4'){ // No Postcode found in search term
	    exitScript('Please input a valid UK Postcode');
	} elseif (empty($result['grideast'])) {
		exitScript('Grid references for ' . $postcode . ' not available, please try another Postcode');
    } else {
		return TRUE;
    }
}

The getNearestItems function takes the grid references, radius and limit_results parameters, queries the database for the matching items and assembles the results into the client-side page:

/*********************************************************************
* getNearestItems()
* Find nearest items in database
*********************************************************************/

function getNearestItems($grideast, $gridnorth, $radius_mi, $limit_results)
/* Note the output from this function is purely in the form of HTML
   to the client page at this stage in the development process */
{
$hostname="localhost";
$dbname="MyDatabaseName";
$dbusername="MyDatabaseUserName";
$dbpassword="MyDatabasePassword";

$k_to_mi = 0.621371192; // Kilometres to miles factor
$tenm_to_mi = $k_to_mi/100; // Ten Metre to miles factor
$mi_to_k = 1.609344; // Miles to Kilometres factor
$radius_km=$radius_mi*$mi_to_k; // Radius in Km
$radius_tenm=$radius_km*100; // Radius in 10m units (which is the same as Grid Refs)

//connect to the database
$db=mysql_connect ($hostname, $dbusername, $dbpassword) or exitScript();

//-select the database to use
$mydb=mysql_select_db($dbname) or exitScript();

//-find the nearest items
$nearest_query = "SELECT SupplierID, SupplierName, Premise, Dependent_street, Street, Double_dependent_locality,
				Dependent_locality, Post_Town, Postcode, Telephone, EmailAddress, Website,
				(SQRT(POW(($grideast - GridEasting),2)+POW(($gridnorth - GridNorthing),2))) AS distance,
				Latitude, Longitude
				FROM suppliers
				HAVING distance <= $radius_tenm
				ORDER BY distance
				LIMIT $limit_results";

$result=mysql_query($nearest_query);
if (($result) && (mysql_num_rows($result) > 0)) {
		//-loop through result set
		while($row=mysql_fetch_array($result)){

			// distance in Miles
			$miles = sprintf("%01.2f",$tenm_to_mi*$row['distance']);

	  		echo '<p class="resulttext"> Distance - <span style="color: red">approx '.$miles.' miles</span><br>';
	  		if (!empty($row['SupplierName'])) echo $row['SupplierName'] .'<br>';
	  		if (!empty($row['Premise'])) echo $row['Premise'] . ' ';
	  		if (!empty($row['Dependent_street'])) echo $row['Dependent_street'].'<br>';
	  		if (!empty($row['Street'])) echo $row['Street'].'<br>';
	  		if (!empty($row['Double_dependent_locality'])) echo $row['Double_dependent_locality'].'<br>';
	  		if (!empty($row['Dependent_locality'])) echo $row['Dependent_locality'].'<br>';
	  		if (!empty($row['Post_Town'])) echo $row['Post_Town'].'<br>';
	  		if (!empty($row['Postcode'])) echo $row['Postcode'].'<br>';
			if (!empty($row['Telephone'])) echo 'Tel: ' . $row['Telephone'].'<br>';
			if (!empty($row['EmailAddress']))
echo 'Email: <a href="mailto:' . $row['EmailAddress'] . '" "target="_blank">' . $row['EmailAddress'] . '</a><br>';
			if (!empty($row['Website']))
				echo 'Web: <a href="http://' . $row['Website']. '" target="_blank">' . $row['Website'] . '</a><br>';
	  	}
}
else { // Problem with query syntax or (more likely) no results found
	showMessage('No results found within radius of ' . $radius_mi . ' mile(s), please try a larger radius');
}
}

Most of the code should be self-explanatory because it is straightforward mySQL database access code. The SQL query which is used to extract the relevant data:

//-find the nearest items
$nearest_query = "SELECT SupplierID, SupplierName, Premise, Dependent_street, Street, Double_dependent_locality,
			Dependent_locality, Post_Town, Postcode,
			(SQRT(POW(($grideast - GridEasting),2)+POW(($gridnorth - GridNorthing),2))) AS distance	FROM suppliers
			HAVING distance <= $radius_tenm
			ORDER BY distance
			LIMIT $limit_results";

$result=mysql_query($nearest_query);

SELECTs the relevant columns FROM the "suppliers" database. The distance of each customer from the input postcode is calculated using Pythagoras' Theorem which in slightly plainer English is:

Square Root of ((grideast - GridEasting)2 + ($gridnorth – GridNorthing)2)

yielding a result in 10 metre units. This result is then compared with the radius in ten metre units in the "HAVING distance <= $radius_tenm" clause, limiting the results to those suppliers within the desired radius.

The LIMIT $limit_results" clause limits the number of results returned to the client-side page.

The results of the SQL query can be accessed with as simple loop:

if (($result) && (mysql_num_rows($result) > 0)) {
		//-loop through result set
		while($row=mysql_fetch_array($result)){
		// Output results to client side page
		.....................
		.....................
		}
}
else { // Problem with query syntax or (more likely) no results found
	showMessage('No results found within radius of ' . $radius_mi . ' mile(s), please try a larger radius');
}

Again, a typical way of accessing the results of a MySQL query in PHP.

The code within the loop which outputs the results to the client-side page is as follows.

First set up the string for the distance in miles and then generate the HTML code for it:


	$miles = sprintf("%01.2f",$tenm_to_mi*$row['distance']);  // distance in Miles
	echo '<p class="resulttext"> Distance - <span style="color: red">approx '.$miles.' miles</span><br>';

Then set up the strings for the other database fields and generate the HTML code for them:


		  	if (!empty($row['SupplierName'])) echo $row['SupplierName'] .'<br>';
	  		if (!empty($row['Premise'])) echo $row['Premise'] . ' ';
	  		if (!empty($row['Dependent_street'])) echo $row['Dependent_street'].'<br>';
	  		if (!empty($row['Street'])) echo $row['Street'].'<br>';
	  		if (!empty($row['Double_dependent_locality'])) echo $row['Double_dependent_locality'].'<br>';
	  		if (!empty($row['Dependent_locality'])) echo $row['Dependent_locality'].'<br>';
	  		if (!empty($row['Post_Town'])) echo $row['Post_Town'].'<br>';
	  		if (!empty($row['Postcode'])) echo $row['Postcode'].'<br>';
			if (!empty($row['Telephone'])) echo 'Tel: ' . $row['Telephone'].'<br>';
			if (!empty($row['EmailAddress']))
echo 'Email: <a href="mailto:' . $row['EmailAddress'] . '" "target="_blank">' . $row['EmailAddress'] . '</a><br>';
			if (!empty($row['Website']))
				echo 'Web: <a href="http://' . $row['Website']. '" target="_blank">' . $row['Website'] . '</a><br>';

Note that code has been inserted to turn the EmailAddress and Website fields into clickable links.
Next we need to add a couple of helper functions which have been called from the server-side code we have already added:
/*********************************************************************
* toggleFormButton()
* Toggle the search button in the client-side form
*********************************************************************/

function toggleFormButton()
{
?>
<script type="text/javascript">
	// reset the search button.
	parent.toggleFormButton(false);
</script>
<?php
}
/*********************************************************************
* showMessage($message)
* Show message in the client-side form
*********************************************************************/

function showMessage($message)
{
?>
<script type="text/javascript">
	parent.showMessage("<?php echo $message; ?>");
</script>
<?php
}

You should now have a set of pages which will display something like this, depending on how you style your page:

Nearest Search 2

Figure 3 - The Results of a nearest search

Would you like a Google Map with that?

We deliberately included the latitude and longitude co-ordinates in our database so that we had a hook into a mapping system. If you would like to add a link to a Google Map (links to other mapping systems will be similar), simply add an iframe to receive the map in your client-side code:

<iframe src="blank.html" class="mapframe" id="map_result" name="map_result" width="340" height="340"></iframe>

And add the following code in your server-side page, after the 'Website' field (or wherever you would prefer it to be!):

if ($row['Longitude'] != '' && $row['Latitude'] != '') {
echo '<a href="google_map.php?long='.$row['Longitude']. '&lat='.$row['Latitude'].'" target="map_result">Show Map</a><br>';
	}

This calls another server-side php script "google_map.php which handles the call to the Google Maps API:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<style type="text/css">
body, html{
	padding : 0px;	margin : 0px;
	background : #FFF;
}
#map {
	width: 340px;
	height: 340px;
}
</style>

<!-- Enter your Google Maps Key by replacing KEY with your key in the line below -->
<script src="http://maps.google.com/maps?file=api&v=1&key=KEY" type="text/javascript"></script>

</head>
  <body>
  <?php
  	$nlong = $_GET['long'];
  	$nlat = $_GET['lat'];
  ?>
    <div id="map"></div>
    <script type="text/javascript">
    <!--
    window.onload = showMap;
    function showMap()
	{
    	var map = new GMap(document.getElementById("map"));
    	map.addControl(new GSmallMapControl());
    	map.addControl(new GMapTypeControl());
map.centerAndZoom(new GPoint(<?php echo $nlong ?>, <?php echo $nlat ?>), 3);
map.addOverlay(new GMarker(new GPoint(<?php echo $nlong ?>, <?php echo $nlat ?>)));
    }
    -->
    </script>
  </body>
</html>

Make sure you enter your Google Maps Key (see http://code.google.com/apis/maps/signup.html if you don't have one) at the indicated place in the above code.

You should now have a set of pages which will display something like this, depending on how you style your page:

Nearest Search 3

Figure 4 - results of nearest search with a side order of maps

Conclusion

This article explained how to do the following:

With the knowledge gained from this article, developers should be able to adapt the code presented to their specific needs.

For details on the PostCoder Web SOAP service and to sign-up for a completely free trial with full access to example source code and technical support, visit Allies Computing Ltd. Phone/Fax: +44(0)1508 494488 / +44(0)1508 494481

Full source code for client-side page

Here is the full source code for the client-side page "NearestExample.html", including styles:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"  >
<head>
<!--
********************************************************************************
	Released under the GNU General Public License

	The following copyright announcement is in compliance
	to section 2c of the GNU General Public License, and
	thus can not be removed, or can only be modified
	appropriately.

	Please leave this comment intact together with the
	following copyright announcement.

	Copyright(c) 2008 Allies Computing Ltd

	The authors provide no warranty.

	PostCoder Web SOAP - Nearest Search Example version 1.01
	By 	Allies Computing Ltd - www.allies-computing.co.uk
	Last Modified 08/10/2008

********************************************************************************
This page and its associated server-side page NearestExample.php
demonstrate how to retrieve grid reference information from the
PostCoder Web SOAP Service and search for the nearest items in a MySQL database

Insert a valid Postcoder Web Nearest user name and password into
findMyNearestExample.php, and insert a valid Google maps ID into
google_mapExample.php page before use.
********************************************************************************
-->
	<title>PostCoder Web SOAP - Nearest Example</title>
<style type="text/css">
<!--
 body, html{
	font : 11px, verdana, sans-serif;
	color				:#000080;
	background : #FFF;
}

div.main {
	width: 720px;
	padding: 5px;
}

div.row {
	clear				:both;
	padding-top			:10px;
}

iframe.nearestframe, iframe.mapframe{
	width				:340px;
	height				:340px;
	border				:1px solid #CDCDCD;
}

iframe.nearestframe{
	float				:left;
	scrolling			:auto;
	frameborder			:1;
}
iframe.mapframe{
	float				:right;
	margin-left			:10px;
}

label {
	float				:left;
	width				:10em;
	text-align			:right;
	padding				:5px 5px 0px 0px;
	font-weight			:bold;
}

fieldset {
	border				:1px solid #000080;
	padding				:10px;
}

legend {
	padding				:2px 5px;
	border				:1px solid #000080;
	text-align			:right;
}

input{
	padding				:2px;
	border				:inset 1px #330066;
}

input.button{
	font : 11px, verdana, sans-serif;
	background-color	:#E5E5E5;
	border				:inset 1px #000080;
	width				:110px;
}

div.message{
	padding				:1px;
	margin				:0px;
	font-weight			:bold;
	color				:#0000FF;
}
-->
</style>
<script type="text/javascript">
function findaddress(pcode, radius, limit_results){
	if (pcode != ''){
	    // clear the message if any
		showMessage(' ');
		// disable the button
		toggleFormButton(true);
		// submit the form and data
	    document.PostCoderForm.postcode.value = pcode;
	    document.PostCoderForm.radius.value = radius;
	    document.PostCoderForm.limit_results.value = limit_results;
		document.PostCoderForm.submit();
	}
}
function showMessage(str){
	if(document.getElementById){
		document.getElementById('message').innerHTML = str;
	}
	else if (document.all){
    		document.all['message'].innerHTML = str;
	}
}

function toggleFormButton(status) {
	var theform = document.NearestForm;
	if (status == true)
			theform.searchbutton.value = 'Searching...';
		else
			theform.searchbutton.value = 'Find my Nearest';
}
function clearMapFrame() {
	map_result.document.body.innerHTML = "";
}
</script>
</head>

<body>
<!-- Hidden form to allow easy communication with server-side page -->
<form name="PostCoderForm" method="post" action="NearestExample.php" target="nearest_result">
<input type="hidden" name="postcode" />
<input type="hidden" name="radius" />
<input type="hidden" name="limit_results" />
<input type="hidden" name="identifier" value="Example - Find My Nearest" />
</form>

<div class="main">
<form name="NearestForm">
	<fieldset>
	<legend>Nearest Search</legend>
	<div style="float:left;">
		<div class="row">
			<label>Postcode:</label>
				<input maxlength="10" tabindex="1" type="text" name="pcode" size="9" onkeypress="if ((window.event) && (window.event.keyCode==13))findaddress(this.value)" />
				<input id="searchbutton" tabindex="4" type="button" class="button" value="Find my Nearest"
							onclick="findaddress(this.form.pcode.value,this.form.radius.value,this.form.limit_results.value)" />
    	</div>
    	<div class="row">
			<label>Radius (miles):</label>
				<input maxlength="4" tabindex="2" type="text" name="radius" size="4" value="1"/>
    	</div>
    	<div class="row">
			<label>Max Items:</label>
				<input maxlength="4" tabindex="3" type="text" name="limit_results" size="4" value="10"/>
    	</div>
		<div class="row">
			<div align="center" id="message" class="message"> </div>
		<div>
		<iframe src="blank.html" class="nearestframe" id="nearest_result" name="nearest_result">
		</iframe>
		<iframe src="blank.html" class="mapframe" id="map_result" name="map_result"></iframe>
	</div>
	</fieldset>
</form>
</div>
</body>
</html>

Full source code for server-side page

Here is the full source code for the server-side page "NearestExample.php".

(Note: Please ensure you enter your PostCoder Web SOAP username and password, and also the hostname, database name, username and password for your MySQL database in this code before use.)

<?php
/*	*************************************************************
	Released under the GNU General Public License

	The following copyright announcement is in compliance
	to section 2c of the GNU General Public License, and
	thus can not be removed, or can only be modified
	appropriately.

	Please leave this comment intact together with the
	following copyright announcement.

	Copyright(c) 2008 Allies Computing Ltd

	The authors provide no warranty.

	PostCoder Web SOAP - Nearest Search Example version 1.01
	By 	Allies Computing Ltd - www.allies-computing.co.uk

	Last Modified 08/10/2008
	*************************************************************
	This PHP script demonstrates how to retrieve grid reference
	information from the PostCoder Web SOAP Service and search
	for the nearest items in a MySQL database
	*************************************************************
*/
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"  >
<head>
	<title>PostCoder Web SOAP Nearest Example</title>
<style type="text/css">
.resulttext {
	font	:11px, verdana, sans-serif;
}
</style>
</head>
<body>
<?php


// Enter Your PostCoder Web SOAP Username and Password Below.
$username = 'MyPostCoderWebSOAPUsername';
$password = 'MyPostCoderWebSOAPPassword';

// Get parameters from client-side page
$identifier = $_POST['identifier'];
$postcode 	= $_POST['postcode'];
$radius = $_POST['radius'];
$limit_results	= $_POST['limit_results'];

$proxy='';
$result='';

if (!empty($postcode)){
	// Set up the SOAP service
	initiate_SOAP_Service($proxy);
	// Find grids for input postcode
	if (getGrids($proxy, $postcode, $identifier, $username, $password,
					$result)) {
		// Get the list of Nearest Items
		getNearestItems($result['grideast'],$result['gridnorth'], $radius, $limit_results);
	}
	else showmessage("Please enter a postcode");
}

toggleFormButton();
clearMapFrame();

/*********************************************************************
* End of main logic, function defintions follow
*********************************************************************/

/*********************************************************************
* initiate_SOAP_Service()
* Sets up the SOAP service.
*********************************************************************/
function initiate_SOAP_Service(&$proxy)
{
/* Setting the $useWSDLcache variable below to TRUE may improve the
overall	speed of the service by creating a cached version of the
WSDL document on your server. To use this feature, the user on your
server that runs this script (apache, httpd, etc) must have write access in the
directory this script lies. */
$useWSDLcache = TRUE;

	// Get the WSDL Document from the PostCoder Web SOAP server.
	$wsdlPath = "http://www.postcoderwebsoap.co.uk/websoap/websoap.php?wsdl";

	// Pull in the NuSOAP code
	require('nusoap/lib/nusoap.php');

	if ($useWSDLcache){
		// include the nusoap wsdl cache classes
		require('nusoap/lib/class.wsdlcache.php');

		// use a wsdl cache
		$cache = new wsdlcache(".",604800); //1 week
		$wsdl = $cache->get($wsdlPath);
		if (is_null($wsdl)){
			$wsdl = new wsdl($wsdlPath);
			$cache->put($wsdl);
		}

		// create a soap client from WSDL cache
		$client = new soap_client($wsdl,TRUE);
	}
	else {
		// Create the client from WSDL document.
		$client = new soap_client($wsdlPath,'wsdl');
	}

	// Check for an error
	if ($err = $client->getError()) exitScript();

	// get proxy client.
	$proxy = $client->getproxy();

	// Check for an error
	if ($err = $proxy->getError()) exitScript();
}

/*********************************************************************
* getGrids()
* Get the Grid References and other geo data for a given postcode
*********************************************************************/
function getGrids($proxy, $postcode, $identifier, $username, $password, &$result)
{
	// call the SOAP method for getGrids.
	$result = $proxy->getGrids($postcode,$identifier,$username,$password);
	if ($proxy->fault) { // Check for a fault
	    exitScript();
	} elseif ($err = $proxy->getError()) { // Check for errors
	    exitScript();
	} elseif ($result['error_code'] != '0'){ // No SOAP error, but some other error
	    exitScript();
	} elseif ($result['retcode'] == '3'){ // Invalid Postcode
	    exitScript('Invalid Postcode ' . $postcode . ' - please check and try again');
	} elseif ($result['retcode'] == '4'){ // No Postcode found in search term
	    exitScript('Please input a valid UK Postcode');
	} elseif (empty($result['grideast'])) {
	    exitScript('Grid references for ' . $postcode . ' not available, please try another Postcode');
    } else {
		return TRUE;
    }
}

/*********************************************************************
* getNearestItems()
* Find nearest items in database
*********************************************************************/

function getNearestItems($grideast, $gridnorth, $radius_mi, $limit_results)
/* Note the output from this function is purely in the form of HTML
   to the client page at this stage in the development process */
{

// Enter the hostname for your database server, database name, username and password
$hostname="localhost";
$dbname="MyDatabaseName";
$dbusername="MyDatabaseUserName";
$dbpassword="MyDatabasePassword";

$k_to_mi = 0.621371192; // Kilometres to miles factor
$tenm_to_mi = $k_to_mi/100; // Ten Metre to miles factor
$mi_to_k = 1.609344; // Miles to Kilometres factor
$radius_km=$radius_mi*$mi_to_k; // Radius in Km
$radius_tenm=$radius_km*100; // Radius in 10m units (which is the same as Grid Refs)

//connect to the database
$db=mysql_connect ($hostname, $dbusername, $dbpassword) or exitScript();

//-select the database to use
$mydb=mysql_select_db($dbname) or exitScript();

//-find the nearest items
$nearest_query = "SELECT SupplierID, SupplierName, Premise, Dependent_street, Street, Double_dependent_locality,
				Dependent_locality, Post_Town, Postcode, Telephone, EmailAddress, Website,
				(SQRT(POW(($grideast - GridEasting),2)+POW(($gridnorth - GridNorthing),2))) AS distance,
				Latitude, Longitude
				FROM suppliers
				HAVING distance <= $radius_tenm
				ORDER BY distance
				LIMIT $limit_results";

$result=mysql_query($nearest_query);
if (($result) && (mysql_num_rows($result) > 0)) {
		//-loop through result set
		while($row=mysql_fetch_array($result)){

			// distance in Miles
			$miles = sprintf("%01.2f",$tenm_to_mi*$row['distance']);
	  		echo '<p class="resulttext"> Distance - <span style="color: red">approx '.$miles.' miles</span><br>';

	  		if (!empty($row['SupplierName'])) echo $row['SupplierName'] .'<br>';
	  		if (!empty($row['Premise'])) echo $row['Premise'] . ' ';
	  		if (!empty($row['Dependent_street']))
	  			echo $row['Dependent_street'].'<br>';
	  		if (!empty($row['Street']))
	  			echo $row['Street'].'<br>';
	  		if (!empty($row['Double_dependent_locality']))
	  			echo $row['Double_dependent_locality'].'<br>';
	  		if (!empty($row['Dependent_locality']))
	  			echo $row['Dependent_locality'].'<br>';
	  		if (!empty($row['Post_Town']))
	  			echo $row['Post_Town'].'<br>';
	  		if (!empty($row['Postcode']))
				echo $row['Postcode'].'<br>';
			if (!empty($row['Telephone']))
				echo 'Tel: ' . $row['Telephone'].'<br>';
			if (!empty($row['EmailAddress']))
				echo 'Email: <a href="mailto:' . $row['EmailAddress'] . '" "target="_blank">' . $row['EmailAddress'] . '</a><br>';
			if (!empty($row['Website']))
				echo 'Web: <a href="http://' . $row['Website']. '" target="_blank">' . $row['Website'] . '</a><br>';
			// Display Map Link
			if (!empty($row['Latitude']) && !empty($row['Longitude']))
				echo '<a href="google_map.php?long=' . $row['Longitude'] . '&lat=' . $row['Latitude'] . '" target="map_result">Show Map</a><br>';
	  	}
}
else { // Problem with query syntax or (more likely) no results found
	showMessage('No results found within radius of ' . $radius_mi . ' mile(s), please try a larger radius');
}
}

/*********************************************************************
* toggleFormButton()
* Toggle the search button in the client-side form
*********************************************************************/

function toggleFormButton()
{
?>
<script type="text/javascript">
	// reset the search button.
	parent.toggleFormButton(false);
</script>
<?php
}
/*********************************************************************
* showMessage($message)
* Show message in the client-side form
*********************************************************************/

function showMessage($message)
{
?>
<script type="text/javascript">
	parent.showMessage("<?php echo $message; ?>");
</script>
<?php
}

/*********************************************************************
* clearMapFrame()
* Clear map frame in the client-side form
*********************************************************************/

function clearMapFrame()
{
?>
<script type="text/javascript">
	parent.clearMapFrame();
</script>
<?php
}

/*********************************************************************
* exitScript($message)
* Exit script, but in a tidy fashion.
*********************************************************************/

function exitScript($message='Unable to conduct Nearest search, please report to site owner')
{
	showMessage($message);
	toggleFormButton();
	clearMapFrame();
	exit("</body></html>");
}

?>
</body>
</html>