Calculating Distance in Miles from Latitude and Longitude

Finding good examples for Calculating Distance in Miles from Latitude and Longitude Using Mysql and PHP wasn’t as easy as I thought it would be. I thought I would compile the working quires and the functions that I wrote/found.

I found a very good paper on the topic at Scribd. I used one the the examples from this paper.

</embed>

Geo Distance Search with MySQL - Upload a Document to Scribd
 
Read this document on Scribd: Geo Distance Search with MySQL

 Here are a couple of PHP functions that I thought would be helpful.  Just a heads up, you will need and XML parser for the first function listed here(I have posted  the one I used for this function below).

getGeoCords

/**
 * getGeoCords()
 * uses geonames.org to get lat and long of postal codes
 * @param string $zip
 * @param string $country | use 2 letter , US
 * @author Jesse Baird <jebaird@gmail.com> 
 * @return array('lat','long')
 */
function getGeoCords($zip='',$country=''){
	
	if($zip!=''&&$country!=''){
		$url = 'http://ws.geonames.org/findNearbyPostalCodes?postalcode='.$zip.'&country='.$country.'&maxRows=1&style=short';
	$myFile = new XMLParser($url);
	//print_r($myFile);
	$xmlRoot = $myFile->data[0];    // GEO Element
	
	if (sizeof($xmlRoot) > 0 ) {
			$geoLocation = $xmlRoot['child'];
			if (sizeof($geoLocation) > 0) {
				$geoLocationProperties = $geoLocation[0]['child'];
			$long = $geoLocationProperties[4]['content'];
				$lat = $geoLocationProperties[3]['content'];
			return array('lat'=>$lat,'long'=>$long);
			}
			
	}else
		return false;
	}else
	return false;
	
}
useage
$cords=getGeoCords($zip,'US');

XMLParser Class

xml_url = $xml_url;
      // print($xml_url."
");
       $this->xml = xml_parser_create();
       xml_set_object($this->xml, $this);
       xml_set_element_handler($this->xml, 'startHandler', 'endHandler');
       xml_set_character_data_handler($this->xml, 'dataHandler');
       $this->parse($xml_url);
   }

   function parse($xml_url) {
   	//$bad_chr = array("x00" => "chr(0)", "x01" => "chr(1)", "x02" => "chr(2)", "x03" => "chr(3)", "x04" => "chr(4)", "x05" => "chr(5)", "x06" => "chr(6)", "x07" => "chr(7)", "x08" => "chr(8)", "x09" => "chr(9)", "x0a" => "chr(10)", "x0b" => "chr(11)", "x0c" => "chr(12)", "x0d" => "chr(13)", "x0e" => "chr(14)", "x0f" => "chr(15)", "x10" => "chr(16)", "x11" => "chr(17)", "x12" => "chr(18)", "x13" => "chr(19)", "x14" => "chr(20)", "x15" => "chr(21)", "x16" => "chr(22)", "x17" => "chr(23)", "x18" => "chr(24)", "x19" => "chr(25)", "x1a" => "chr(26)", "x1b" => "chr(27)", "x1c" => "chr(28)", "x1d" => "chr(29)", "x1e" => "chr(30)", "x1f" => "chr(31)");
   	
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $xml_url);
        curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
        //$store = curl_exec ($ch);
        $data = curl_exec ($ch);
      // print_r(sizeof($data)."
");
        curl_close ($ch);

      $parse = xml_parse($this->xml, $data, sizeof($data));
//$parse = xml_parse($xml_parser, strtr($data, $bad_chr),sizeof($data));
       if (!$parse) {
     	 
           die(sprintf("XML error: %s at line %d",
               xml_error_string(xml_get_error_code($this->xml)),
                   xml_get_current_line_number($this->xml)));
                   xml_parser_free($this->xml
                 );
       }
       return true;
   }

   function startHandler($parser, $name, $attributes) {
       $data['name'] = $name;
       if ($attributes) { $data['attributes'] = $attributes; }
       $this->data[] = $data;
   }

   function dataHandler($parser, $data) {
       if ($data = trim($data)) {
           $index = count($this->data) - 1;
           // begin multi-line bug fix (use the .= operator)
           $this->data[$index]['content'] .= $data;
           // end multi-line bug fix
       }
   }

   function endHandler($parser, $name) {
       if (count($this->data) > 1) {
           $data = array_pop($this->data);
           $index = count($this->data) - 1;
           $this->data[$index]['child'][] = $data;
       }
   }
}
?>

getDistanceSQL

I wrote the php for this function to make life easy on me.  It is very flexible and allows you to use it on any table.

/**
 * getDistanceSQL()
 *
 * @param mixed $lat
 * @param mixed $long
 * @param string $sql_lat_col | place_lat -- alows you to use it on any table
 * @param string $sql_long_col place_long
 * @return
 */
function getDistanceSQL($lat,$long,$sql_lat_col="",$sql_long_col=""){
	/*
	SELECT *, 3956 * 2 * ASIN(SQRT(
POWER(SIN((41.648207 - abs(tournament_lat)) * pi()/180 / 2), 2) + COS(41.648207 * pi()/180 ) * COS(abs(tournament_lat) * pi()/180) * POWER(SIN((-91.541579-tournament_long) * pi()/180 / 2), 2) )) as distance From tournament
	*/
	return ', 3956 * 2 * ASIN(SQRT(
POWER(SIN(('.$lat.' - abs('.$sql_lat_col.')) * pi()/180 / 2), 2) + COS('.$lat.' * pi()/180 ) * COS(abs('.$sql_lat_col.') * pi()/180) * POWER(SIN(('.$long.'-'.$sql_long_col.') * pi()/180 / 2), 2) )) as distance';
	
}
useage
$sql="SELECT *";
//check to see if user is using this option
if($cords['lat']!='')
$sql.=getDistanceSQL($cords['lat'],$cords['long'],'tournament_lat','tournament_long');
$sql.=" FROM tournament ".$whereClause;
//having clause
if(is_numeric($r['miles'])){
	if($r['miles']>199)

//using the HAVING because WHERE you will end up with an error
$sql.='HAVING distance < '.$r['miles'];
else
$sql.='HAVING distance > '.$r['miles'];

}
$sql.= " ORDER BY tournament_date ASC";
//you end up with something like this
SELECT *, 3956 * 2 * ASIN(SQRT(
POWER(SIN((42.524071 - abs(tournament_lat)) * pi()/180 / 2), 2) +
COS(42.524071 * pi()/180 ) * COS(abs(tournament_lat) * pi()/180) *
POWER(SIN((-92.449725-tournament_long) * pi()/180 / 2), 2) )) as
distance FROM tournament WHERE tournament_date between '1199167200' AND
'1228024800' AND 1 HAVING distance > 25 ORDER BY tournament_date ASC