<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Pixels from the Edge &#187; Google API</title>
	<atom:link href="http://pixelsfromtheedge.com/tag/google-api/feed/" rel="self" type="application/rss+xml" />
	<link>http://pixelsfromtheedge.com</link>
	<description>Creative // Technology // Digital // Interactive // Mobile // Advertising</description>
	<lastBuildDate>Fri, 02 Mar 2012 14:39:12 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Geocoding and Distancing and Latitude and Longitude and umm Stuff</title>
		<link>http://pixelsfromtheedge.com/2009/04/geo-coding-and-distancing-and-latitude/</link>
		<comments>http://pixelsfromtheedge.com/2009/04/geo-coding-and-distancing-and-latitude/#comments</comments>
		<pubDate>Thu, 23 Apr 2009 19:01:00 +0000</pubDate>
		<dc:creator>Richie</dc:creator>
				<category><![CDATA[Geocoding]]></category>
		<category><![CDATA[Google API]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.detroitdigitalrevolution.com/clients/me/blog/?p=22</guid>
		<description><![CDATA[Okay this is complicated stuff.  Working on putting together a new site for a client, I have a table of locations with longitude and latitude fields for every row (I actually had a tab delimited file, but I sucked it into the db).  For the functionality on the web front end a user needs to search by distance, e.g. find all locations within 10 miles of a zip code.  Um okay, so how the hell do I do this?
Well first of I need to get the latitude and longitude for a zip code.  For that I very quickly found&#8230;]]></description>
			<content:encoded><![CDATA[<p>Okay this is complicated stuff.  Working on putting together a new site for a client, I have a table of locations with longitude and latitude fields for every row (I actually had a tab delimited file, but I sucked it into the db).  For the functionality on the web front end a user needs to search by distance, e.g. find all locations within 10 miles of a zip code.  Um okay, so how the hell do I do this?</p>
<p>Well first of I need to get the latitude and longitude for a zip code.  For that I very quickly found the <a href="http://code.google.com/apis/maps/documentation/geocoding/" target=_new>Google Geocode API</a> and threw together the following:</p>
<pre class="brush: php;">
&lt;?php
$result=file_get_contents("http://maps.google.com/maps/geo?48220&amp;sensor=false&amp;gl=us&amp;output=xml&amp;key=yourgooglekey);
$data=simplexml_load_string($result);
$coord=$data->Response->Placemark->Point->coordinates;
$coord=explode(",",$coord);
$longitude=$coord[1];
$latitude=$coord[0];
?&gt;
</pre>
<p>It took like 10 minutes, and that included signing up for the API key and reading the documentation, shit this geocoding stuff is easy, I should be done by lunch…</p>
<p>Yeah okay so I have two sets of coordinates, great but errr now what?  So I find the <a href="http://en.wikipedia.org/wiki/Haversine_formula" target=_new>Haversine</a> formula, but alas not really being any good at math I don’t understand it, but it doesn’t really matter because I know how to convert it to do the math in PHP.  Cool so I got the Haversine function in PHP now but how do I query it against the data in my database?  I’d read some stuff about geographical libraries for databases and it seemed super complicated, like the <a href="http://dev.mysql.com/tech-resources/articles/4.1/gis-with-mysql.html" target=_new>GIS and Spatial extensions for MySQL</a>.  So I figure I’ll use a two step approach and do a simple query to pull out a square block of locations and then apply the math.  i.e. if I am searching for within a 10 mile radius of a location I’ll do a search on -10 miles and +10 miles, but as the distance is circular I would be getting results in the four corners of that square block that are greater than the 10 miles, and I would then apply the Haversine function and be left only with those results that where truly within the 10 mile radius.</p>
<p>By sheer luck I found <a href="http://blog.peoplesdns.com/archives/24" target=_new>this post</a>.  It was insane, I didn’t realize you could apply the Haversine formula directly in SQL, I ran that query in my db and bam it worked instantly (well actually there’s a typo syntax error ‘< =’ -blank space between the less than and equals character - in the where clause).  Obviously I had to switch out a couple of things so it played nice with my database, but it was instant success, I tried out some different latitude and longitude coordinates and it worked every time:</p>
<p class="reverse">
SELECT Name,Address,Address2,City,State,Zip,Phone,URL,Latitude,Longitude,<br />    acos(SIN( PI()* 42.4584036 /180 )*SIN( PI()*latitude/180 ))+(cos(PI()* 42.4584036 /180)*COS( PI()*latitude/180) *COS(PI()*longitude/180-PI()* -83.1380955 /180))* 3963.191 AS distance<br />   FROM tbl_retailers<br />   WHERE active=1<br />    AND 3963.191 * ACOS( (SIN(PI()* 42.4584036 /180)*SIN(PI() * latitude/180)) +<br />    (COS(PI()* 42.4584036 /180)*cos(PI()*latitude/180)*COS(PI() * longitude/180-PI()* -83.1380955 /180))<br />    ) <= 100    ORDER BY 3963.191 * ACOS(     (SIN(PI()* 42.4584036 /180)*SIN(PI()*latitude/180)) +     (COS(PI()* 42.4584036 /180)*cos(PI()*latitude/180)*COS(PI() * longitude/180-PI()* -83.1380955 /180))    )
</p>
<p>Wow so this was it, everything I needed.  Oh wait, except that I needed to display the distance in miles.  But the guy who blogged this was some kind of superman and he talks about this on <a href="http://blog.peoplesdns.com/archives/34" target=_new>another post</a>.  Really I only needed the rad() and distance() functions (because distance is dependent on rad), but I decided that they’ll probably all come in useful one day, so I took them wrapped them in a class called GeoLoc.  The class doesn’t need to be instantiated into an object so all methods can be called as follows</p>
<pre class="brush: php;">
&lt;?php
GeoLoc::distance($lat1, $lon1, $lat2, $lon2, $units);
?&gt;
</pre>
<p>Then decided I could abstract in the SQL statement by passing in all necessary variables, and I added it to the class:</p>
<pre class="brush: php;">
&lt;?php
GeoLoc::prepareGetByDistanceSQL($lat,$long,$distance_in_miles,$table_name,$fields);
?&gt;
</pre>
<p>I then understood that the measuring unit the query is using is the same as in the distance() method so I abstracted it out into its own method.  At which point I noticed that the numbers didn’t match exactly – for miles the SQL had 3963.191 whereas the distance function only had 3963.1.  I decided that the SQL query had the more granular number and when I reviewed its original blog post it was across the board for all three measuring units so I updated all the numbers.  I then figured I might as well abstract in the Google Geocode zip code lookup.</p>
<p>This is what I was left with (functions I&#8217;m not using removed); I give all credit to joeldg:</p>
<pre class="brush: php;">
&lt;?php
/*
Geographical Location functions
ALL CREDIT GOES TO http://blog.peoplesdns.com/
Thanks to these two blog posts:

http://blog.peoplesdns.com/archives/24

http://blog.peoplesdns.com/archives/34

This API:

http://code.google.com/apis/maps/documentation/geocoding/

*/
class GeoLoc{

const GOOGLE_API_KEY="yourkeygoeshere";
const GOOGLE_API_URL="http://maps.google.com/maps/geo";

/*
queries the google api for zip code
returns long and lat coordinates
*/
function getZipcodeCoordinates($zip_code){
  $result=file_get_contents(self::GOOGLE_API_URL."?q=".$zip_code."&#038;sensor=false&#038;gl=us&#038;output=xml&#038;key=".self::GOOGLE_API_KEY);
  $data=simplexml_load_string($result);
  $coord=$data-&gt;Response-&gt;Placemark-&gt;Point-&gt;coordinates;
  $coord=explode(",",$coord);
  $r-&gt;longitude=$coord[0];
  $r-&gt;latitude=$coord[1];
  return $r;
}
function rad($v){
  return ($v*M_PI/180);
}
/*
distance between two points using sherical law of cosines
cos c = cos a cos b + sin a sin b cos C
*/
function distance($lat1, $lon1, $lat2, $lon2, $units = 'miles'){
  $lat1 = self::rad($lat1); $lon1 = self::rad($lon1);
  $lat2 = self::rad($lat2); $lon2 = self::rad($lon2);
  $r=self::convertMeasuringUnit($units);
  return acos(sin($lat1)*sin($lat2) + cos($lat1)*cos($lat2)*cos($lon2-$lon1)) * $r;
}
/*
gets the measuring unit
*/
function convertMeasuringUnit($units){
  switch ($units){
    case "miles": $r = 3963.191; break;;
    case "nmiles": $r = 3441.596; break;;
    case "kilo": $r = 6378.137; break;;
  }
  return $r;
}
/*
returns sql to query a table for rows by distance (miles,nmiles,kilo)
table must contain fields named latitude and longitude
*/
function prepareGetByDistanceSQL($long,$lat,$distance,$table_name,$fields,$units="miles"){
  $cUnit=self::convertMeasuringUnit($units);
  $sql="
    SELECT ".implode($fields,",").",
    acos(SIN( PI()* ".$lat." /180 )*SIN( PI()*latitude/180 ))+(cos(PI()* ".$lat." /180)*COS( PI()*latitude/180) *COS(PI()*longitude/180-PI()* ".$long." /180))* ".$cUnit." AS distance
    FROM ".$table_name."
    WHERE active=1
      AND ".$cUnit." * ACOS( (SIN(PI()* ".$lat." /180)*SIN(PI() * latitude/180)) +
    (COS(PI()* ".$lat." /180)*cos(PI()*latitude/180)*COS(PI() * longitude/180-PI()* ".$long." /180))
    ) &lt;= ".$distance."
    ORDER BY ".$cUnit." * ACOS(
    (SIN(PI()* ".$lat." /180)*SIN(PI()*latitude/180)) +
    (COS(PI()* ".$lat." /180)*Cos(PI()*latitude/180)*COS(PI() * longitude/180-PI()* ".$long." /180))
   )
  ";
  return $sql;
}
}
?&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://pixelsfromtheedge.com/2009/04/geo-coding-and-distancing-and-latitude/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

