Driving distance

I have this program to be able to enter an address and then it return the list of closest branches. It’s working you can see it working at:

http://s262833979.onlinehome.us/Dan/postcode/search.php

My question is how can I change my code from the Havsersine formula to driving distance? this works great but for what I’m trying accomplish “how the crow flies” distance wont work. I’d like it to show how many kilometers driving distance instead of how many kilometers in a straight line, and it list the closest locations by driving distance.

[code]<?

  • The curl class
    */
    class curl {

    /**

    • COnstructor
      */
      function curl() {

    }

    function init_curl($ch,$url,$postfields=null,$follow=null,$cookie=null,$referer=null) {

      // Set url
      curl_setopt($ch, CURLOPT_URL, $url);
      
      // Enable Post
      if($postfields) {
      	curl_setopt ($ch, CURLOPT_POST, 1);
      	curl_setopt ($ch, CURLOPT_POSTFIELDS, $postfields);
      }
      
      if($follow) {
      curl_setopt ($ch, CURLOPT_FOLLOWLOCATION, 1 ); 		
      }
      		
      if($referer) {
      curl_setopt($ch, CURLOPT_REFERER, $referer);
      }
      				
      //Enable SSL
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
      curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
      
    
      curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)');
      
      //Return results as string
      curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
      		
      return $ch;
    

    } // end function

    /*
    Grabs a page
    */

    function get_page($options) {

    //Set options
    foreach($options AS $key=>$value) {
    $$key = $value;
    }

      $ch = curl_init();		
      $ch = $this->init_curl($ch,$url,$postfields,$follow,$cookie);
      $page = curl_exec($ch);
      curl_close($ch);
      return $page;
    

    }

} // end class

/**

  • A simple wrapper for db functions

*/
class db_custom {

/**
 * Constructor
 *
 * A simple wrapper for database functions
 *
 */	
function db_custom()	{

// database configuration
$host = "";
$user = "";
$pass = "";
$db = "";

// open database connection
$connection = mysql_connect($host, $user, $pass) or die ("Unable to connect!");
// select database
mysql_select_db($db) or die ("Unable to select database!");


}

###############################################
## Updates an array of fields and values
## and reurn the resulting ID
###############################################
function quick_update($database,$fields,$values,$wherearray) {
//Variables
$num_array = count($values);
//Format NULL
$values = str_replace("'NULL'","NULL",$values);
//Write query
$query = "UPDATE `$database` SET ";
foreach ($fields AS $key=>$value) {
$count++;
$query .= " $fields[$key] = '$values[$key]'";
if ($count <> $num_array) { $query .= ","; }
}
//Create where
foreach ($wherearray AS $key=>$value) {
$counterv++;
	$query_chk .= "$key = '" . trim($value) . "'";
	
		if ($counterv != count($wherearray)) {
		$query_chk .= " AND ";
		}
}

$query .= " WHERE $query_chk";
$query = str_replace("'`","",$query);
$query = str_replace("`'","",$query);


$result = mysql_query($query) or die ("Error in query: $query. " . mysql_error());
$rows = mysql_affected_rows();
return $rows;
}


######################################################
# Execute row
# runs query and gets row back
######################################################
function executeRow($query__ER,$type=null) {
$result__ER = mysql_query($query__ER) or die ("Error in query: $query__ER. " . mysql_error());
	if($result__ER != 1) {
		if($type!="array") {
		$row__ER = mysql_fetch_object($result__ER);
		} else {
		$row__ER = mysql_fetch_assoc($result__ER);
		}
	return $row__ER;		
	}
}

######################################################
# Connects to the database and returns the 
# results in an array
######################################################

function executeQuery($query,$func=null,$type="") {

	//Get the table name from the query
	preg_match("/SELECT(.*)FROM( )([A-z_]+)/i",$query,$matches);
	$table_name = $matches[3];
	
$result = mysql_query($query) or die ("Error in query: $query. " . mysql_error());
$rows = mysql_num_rows($result);
$columns = mysql_fetch_assoc($result);

	if ($rows > 0) { // Only proceed if we have a result
	
	mysql_data_seek($result,0);
		while ($row=mysql_fetch_array($result)) {
		
			foreach ($columns As $key=>$value) {
			
						
						//Run any extra functions that have been sent over
						if(is_array($func)) {			
							foreach ($func AS $Fkey=>$Fvalue) {						
							$row[$key] = $this->$Fvalue($row[$key],$key,$table_name);				
							} // end FE			
						} // end IF
						
							if($type == "object") {
							//echo $key . "  "  . $row[$key] . "\n";
							$tmp->$key = $row[$key];				
							} else {
							$tmp[$key] = $row[$key];
							}
								
			}// end for each
		
				$results[] = $tmp;
				unset($tmp);
		} //end while 
			
	$final_result['result'] = $results;
	$final_result['rows'] = mysql_num_rows($result);		
	} else {
	$final_result['rows'] = 0;		
	}// end if

return $final_result;

} // end function

}

/**

  • A postcode finder class

*/
class postcode_finder {

/**
* Constructor
*
*/
function postcode_finder($array) {

	if(is_array($array)) {
		foreach($array AS $key=>$value) {
		$this->$key = $value;
		}
	}	

	//DO stuff

}


/**
* Returns a lat / long of a given postcode
*
*/	
function get_lat_long($postcode,$domain=null) {
	
	if(!$domain) {
	$domain = "com";
	}

	$url = "http://maps.google." . $domain . "/maps/geo?q=" . urlencode($postcode) . "&output=json&key=ABQIAAAAWjc0ZH2RENLxziofASg9ABQH987j_SlqISv1l93HS7ksPkvN9xRAXjKLSj-Yj2Xw7I6gP3RHQb4UQg";
					
	$json = $this->curl->get_page(array("url"=>$url));
					
	$store_data = json_decode(str_replace("&quot;","\"",htmlentities($json))); //Take care of accents
							
	$lng = $store_data->Placemark[0]->Point->coordinates[0];			
	$lat = $store_data->Placemark[0]->Point->coordinates[1];
	
		//Return
		if($lng && $lat) {

			return array('lat'=>$lat,
						 'lng'=>$lng
						 );
					 
		} else {
		
			return false;
		
		}

}


/**
* Get a list of our stores, sorted by distance to this postcode
*
*/	
function get_stores_list($postcode) {

	//If it's a UK postcode then format correctly
	$postcode = $this->checkPostcode($postcode);

	$latlng = $this->get_lat_long($postcode);
	
	if(!$latlng) { //Unrecognised postcode
	return false;
	}
	
	$latitude = $latlng['lat'];
	$longitude = $latlng['lng'];		

// print_r($latlng);

		$query = "SELECT *,
					(((acos(sin((".$latitude."*pi()/180)) * sin((`lat`*pi()/180))
					+cos((".$latitude."*pi()/180)) * cos((`lat`*pi()/180)) 
					* cos(((".$longitude."- `lng`)*pi()/180))))*180/pi())*60*1.1515) * 1.609344
					as distance 
						FROM `postloc`
						ORDER BY distance ASC
						LIMIT 0,10
				";
		$stores = $this->db->executeQuery($query);
		$stores = $stores['result'];			
		
		return $stores;


}


/**
* Checks whether supplied postcode is a valid UK postcode
*/
function checkPostcode($toCheck) {

  $orig = $toCheck;

  // Permitted letters depend upon their position in the postcode.
  $alpha1 = "[abcdefghijklmnoprstuwyz]";                          // Character 1
  $alpha2 = "[abcdefghklmnopqrstuvwxy]";                          // Character 2
  $alpha3 = "[abcdefghjkstuw]";                                   // Character 3
  $alpha4 = "[abehmnprvwxy]";                                     // Character 4
  $alpha5 = "[abdefghjlnpqrstuwxyz]";                             // Character 5
  
  // Expression for postcodes: AN NAA, ANN NAA, AAN NAA, and AANN NAA
  $pcexp[0] = '^('.$alpha1.'{1}'.$alpha2.'{0,1}[0-9]{1,2})([0-9]{1}'.$alpha5.'{2})$';

  // Expression for postcodes: ANA NAA
  $pcexp[1] =  '^('.$alpha1.'{1}[0-9]{1}'.$alpha3.'{1})([0-9]{1}'.$alpha5.'{2})$';

  // Expression for postcodes: AANA NAA
  $pcexp[2] =  '^('.$alpha1.'{1}'.$alpha2.'[0-9]{1}'.$alpha4.')([0-9]{1}'.$alpha5.'{2})$';
  
  // Exception for the special postcode GIR 0AA
  $pcexp[3] =  '^(gir)(0aa)$';
  
  // Standard BFPO numbers
  $pcexp[4] = '^(bfpo)([0-9]{1,4})$';
  
  // c/o BFPO numbers
  $pcexp[5] = '^(bfpo)(c\/o[0-9]{1,3})$';

  // Load up the string to check, converting into lowercase and removing spaces
  $postcode = strtolower($toCheck);
  $postcode = str_replace (' ', '', $postcode);

  // Assume we are not going to find a valid postcode
  $valid = false;
  
  // Check the string against the six types of postcodes
  foreach ($pcexp as $regexp) {
  
	if (ereg($regexp,$postcode, $matches)) {
	  
	  // Load new postcode back into the form element  
	  $toCheck = strtoupper ($matches[1] . ' ' . $matches [2]);
	  
	  // Take account of the special BFPO c/o format
	  $toCheck = ereg_replace ('C\/O', 'c/o ', $toCheck);
	  
	  // Remember that we have found that the code is valid and break from loop
	  $valid = true;
	  break;
	}
  }
		
  // Return with the reformatted valid postcode in uppercase if the postcode was 
  // valid
  if ($valid){
  return $toCheck;
  } else {
  $this->non_standard_postcode = true;
  return $orig;
  };
  
}	

}

//If we have a post
if($_GET[‘postcode’]) {
//Start database class
$db = new db_custom();
$curl = new curl();
$finder = new postcode_finder(array(‘db’=>$db,
‘curl’=>$curl));
$stores = $finder->get_stores_list($_GET[‘postcode’]);
}

if($_GET[‘fill’]) {

$db = new db_custom();
$curl = new curl();
$finder = new postcode_finder(array('db'=>$db,
						  'curl'=>$curl));						  
$finder->setup();

}

?>

<title>Book a service call</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />

<!-- rounded corner css -->
<link rel="stylesheet" type="text/css" href="/wp-content/themes/leon/libs/nifty_corners/niftyCorners.css" />
<link href="css/style.css" rel="stylesheet" type="text/css" />
<!-- favicon -->	
<link rel="shortcut icon" href="/wp-content/themes/leon/images/favicon.ico" >


<link href="http://mu.com/wp-content/themes/leon/style.css" type="text/css" rel="stylesheet" />

<!--
Aciddrop Theme Created by Leon Chevalier @ Aciddrop (http://www.aciddrop.com/)
-->
<style type="text/css">
</style>
<div style="padding-top: 0pt; padding-bottom: 0pt;" class="roundedByNifty" id="content"><b style="background-color: rgb(223, 223, 223);" class="artop"><b style="border-color: rgb(204, 204, 204); background-color: rgb(138, 199, 239);" class="re1"></b><b style="border-color: rgb(204, 204, 204); background-color: rgb(138, 199, 239);" class="re2"></b><b style="border-color: rgb(204, 204, 204); background-color: rgb(138, 199, 239);" class="re3"></b><b style="border-color: rgb(204, 204, 204); background-color: rgb(138, 199, 239);" class="re4"></b></b>


	<div id="content_inner">

		<div id="header">
			
			<a href="/"></a>
			<p class="site_description"></p>
	  </div>


		<div class="posts_content">

	
		<div id="post_wrapper">
	
		
			<div class="post_title page">
				<h2><a href="http://mu.com/2007/12/09/about/" title="Store finder"></a></h2>
			</div>
			<div class="spacer_small"></div>
			<div class="comment_line"></div>
			
			<br>
			<br>
																						  
			<form action="" method="get">
				<fieldset>
				<legend>Enter customers postal code or city and province <span class="style1">(example E1E 1E1, Guelph, Ontario) </span></legend>					
				
					<p align="center">
					  <input name="postcode" type="text" value="<?= $_GET['postcode'] ?>" size="75"> 
					  <input type="submit" value="Search">
	            </p>
				</fieldset>
			</form>
			
			<? if ($_GET['fill']) { ?>
						
			<h2 style="background-color:padding:2px">Database filled</h2>								
			
			<? } ?>
			
			<? if ($_GET['postcode']) { ?>
			
				<? if ($stores) { ?>
									
				<h2 style="background-color:padding:2px">Closest branch's to customer are:</h2>
				
					<? foreach($stores AS $store) { ?>
						
				    <div class="branch"><span class="style3"><?= $store['name'] ?></span> - <span style="background-color:#FFFFCC"><?= number_format($store['distance'],2) ?></span> kilometers from customer</div>
							<div class="info"><?= $store['address'] ?><br>
							<?= $store['city'] ?><br>
							<?= $store['province'] ?><br>
							<?= $store['postcode'] ?>
							</div>
							<? } ?>
				
				<? } else { ?>
				
				<h2 style="background-color:padding:2px">That loaction  was not recognized</h2>					
				
				
				<div align="center"><span class="style2">
			    <? } ?>	
				  
				  
			    <? } ?>	
			    </span></div>
		</div>	
		</div>		
		
	  <div class="posts_content style2"></div>
	</div>	
</div>
</div>


<div class="spacer_small style2">
  <div align="center"> </div>
</div>

</div>
[/code]

Hi elite311,

In reality your program is not figuring a pinpoint distance, but a distance from a zip code, which is a little more general area than “how the crow flies” from 123 Somewhere Dr to 123 Somestore Dr.

If you want to figure driving distance from an address, I suggest using a Google Maps integration, if you want to figure an actual driving distance from a zip code, it’s still going to be a general driving distance, but the best way I can think to do it is to hard code it.

Simply go through the list of zip codes and look into Google Maps for driving time from each zip code to the store that is closest for that zip code since using your current system you can find that information out rather quickly.

Then create a new database table (or append a column or two to an existing table where appropriate) to hold the information for which store is closest based on which zip code is entered, and the drive time to that store.

I am thinking, a table for drive time, with columns for zip code, closest store id, and drive time to that store.

Then, based on the zip code entered, you could easily output the closest store (closest drive time, not closest how the crow flies) and the general drive time to that store from their zip code.

Hope this helps.

Robert

Thanks for the quick reply.

I’m not sure I understand what you mean when you say

Simply go through the list of zip codes and look into Google Maps for driving time from each zip code to the store that is closest for that zip code since using your current system you can find that information out rather quickly

The system is basically for call reps, when a customer calls they give their postal code or city and province and the rep puts it in and it returns the closest branch to that location. We have branches right across Canada so I’m not sure how I can hard code that because each call could be from a different place. The distance doesn’t have to be 100% accurate, the problem right now is:

say I have a branch in city A and city B and the customer calls in the reps puts the postal code or city in, the system tells them the branch in city A is closer because it’s right across the river (say 20kms) but to drive there it would take 250kms in actuality the branch in city B is closer because it only takes 35kms to drive there. The rep will get the wrong info because right now the system sees 20kms in a straight line and puts it first so they will direct the person to the wrong spot.

That’s where I’m stumped I just need a general driving distance not 100% accurate, I’ve been reading about the Google maps API but I can’t figure out how to use it to accomplish this.

What I meant was that using your current system, if you enter a zip code it will tell you the “closest” store, and you could then go over to Google Maps and look up driving distance from that zip code to that store address, although, given the case that the system is giving you inaccurate results sometimes, that wouldn’t be the best idea. Also, if it’s a Canada-wide business, obviously you don’t want to sit down and record that information for every zip code in Canada, whew, nightmare…lol.

In that case I would say your best bet is to look into Google Maps. If the rep is the person using the site, and not customers, why not just have them open another tab in their browser and use Google Maps?

They can input the persons full address or zip code, and then search for your business name, and Google Maps will show them every store within proximity to the address, sorted by shortest driving time (not necessarily distance, as Google Maps takes speed limits into consideration as well, so time takes precedence over distance).

Then your rep could confidently tell the customer "The closest store to that address / zip code is: ".

If it has to be integrated into your website, then simply look into the Google Maps API, as that is going to be the easiest fix for you here.

Regards,

Robert

Exactly way to many postal codes LOL

I’d love for them to just use Google maps, currently they are using that. The problem is they mess it up constantly still, that’s the purpose of this tool as they have to remember so many locations it gets messed up a lot and I don’t like having bad customer service.

My problem is the Google maps API is a java function, I’m not very good with java (even though it’s similar to PHP) I’ve just never used it really. So I’m having a hard time adapting this system to use the Google maps API to give me the driving distance. When I started I thought the haversine formula would be ok to use for this but I have since found the glitch’s I was speaking about in my last post.

I was hoping someone here has more experience with the Google maps API and could help me either implement it into my code or get me started in the right direction.

Understood, but if they use Google Maps, it should be a pretty hard thing to get wrong.

They need to use Get Directions, and for the from address, just type in the customer zip code.

Then in the to address, just type the name of your business.

The result will be a map covered in pin points with a list on the left hand side of each location and it’s distance, sorted automatically by shortest driving time.

If they can’t operate that, it might be time to upgrade your help! :o

Currently, when you go to http://maps.google.com, you are greeted with the search bar, and directly under it on the left is a link that says “Get directions.”

Upon clicking that, they just type a zip code into A and your business name into B and click “GET DIRECTIONS.”

Maybe we’re just tech savvy but I’d definitely remember these issues when I went around to hand out raises! ;D

I agree 100% I have no issues using Google maps but this is something that no matter how much I push they mess up constantly.

This is just the start of the tool as well, eventually once I get this part working properly I will build on it. Sort of a complete “triage” tool with all the information they require in one spot easy to use in a way that can’t be messed up. Which gives me more of an ability to manage performance in the long run.

Thanks for you advice on this, I guess I’ll just have to keep plugging away and see where I can get. Hopefully someone on this board will be able to assist me.

Thanks again.

Sponsor our Newsletter | Privacy Policy | Terms of Service