<?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>sociacall.com &#187; location based search</title>
	<atom:link href="http://sociacall.com/blog/tag/location-based-search/feed/" rel="self" type="application/rss+xml" />
	<link>http://sociacall.com/blog</link>
	<description>Blogging about online privacy, personal data, social networking and communications</description>
	<lastBuildDate>Mon, 14 Nov 2011 14:17:55 +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>Global proximity box calculations</title>
		<link>http://sociacall.com/blog/2009/09/07/global-proximity-box-calculations/</link>
		<comments>http://sociacall.com/blog/2009/09/07/global-proximity-box-calculations/#comments</comments>
		<pubDate>Mon, 07 Sep 2009 17:44:36 +0000</pubDate>
		<dc:creator>Dwight Irving</dc:creator>
				<category><![CDATA[social networking]]></category>
		<category><![CDATA[boundary]]></category>
		<category><![CDATA[distance]]></category>
		<category><![CDATA[geocode]]></category>
		<category><![CDATA[great circle]]></category>
		<category><![CDATA[haversine]]></category>
		<category><![CDATA[latitude]]></category>
		<category><![CDATA[LBS]]></category>
		<category><![CDATA[location based search]]></category>
		<category><![CDATA[longitude]]></category>
		<category><![CDATA[meridian]]></category>
		<category><![CDATA[NACGEO]]></category>
		<category><![CDATA[proximity]]></category>

		<guid isPermaLink="false">http://sociacall.com/blog/?p=136</guid>
		<description><![CDATA[Use the Haversine equation to compute a latitude and longitude bounding box a given distance from a centerpoint.  This function includes a solution for the normal boundary conditions occurring at the poles and 0 and 180 meridians]]></description>
			<content:encoded><![CDATA[<p>By <a target="_self" title="Dwight Irving bio" mce_href="http://sociacall.com/blog/about/biography-dwight-irving/" href="http://sociacall.com/blog/about/biography-dwight-irving/">Dwight Irving</a><br mce_bogus="1"></p>
<p>Note:&nbsp; Updated on Dec 6, 2009 &#8211; Found a bug in the code.&nbsp; I&#8217;ve updated the code below and have a notation where the bug was.</p>
<p>A while ago, I needed to figure out how to search for database entries that were within a specified distance from a central point.&nbsp; The code was needed for the search function at <a target="_self" title="CrossroadsAngel.com" mce_href="http://crossroadsangel.com/" href="http://crossroadsangel.com/">CrossroadsAngel.com</a> where you can search for members by location.&nbsp; I found many pages that had a similar goal of calculating great circle distance between any two points on a sphere, but that&#8217;s not quite the same problem.&nbsp; I found a few web pages that described how to convert linear distance to delta latitude and longitude, but I didn&#8217;t find any complete code examples that worked across all border conditions.</p>
<p>The key equation for translating linear distance to latitude and longitude on a sphere is the Haversine formula.&nbsp; Latitude boundary conditions for this formula occur at the&nbsp; north and south poles and the equator.&nbsp; For longitude, the boundary conditions occur at the prime and 180th degree meridians. For distance, the boundary condition is the circumference of the Earth.</p>
<p>I made a simplifying assumption that the earth is a perfect sphere.&nbsp; To make the database search easier I used a rectangular bounding box rather than a circular one.&nbsp; The rectangle used encloses the circle and therefore increases the distance from the centerpoint at any angles other than 0, 90, 180 and 270 degrees (0, PI/2, PI, 3PI/2 radians).</p>
<p>I thought it would also be handy to be able to use either kilometers or miles in the function, so I&#8217;ve included a flag where miles are true and kilometers are false</p>
<p>In PHP the code is as follows:</p>
<p style=""><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">class diGeoBox<br />{</span></p>
<p style="padding-left: 30px;" mce_style="padding-left: 30px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">public $LatN;<br />public&nbsp; $LatS;<br />public&nbsp; $LonE;a<br />public&nbsp; $LonW;</span></p>
<p style="padding-left: 30px;" mce_style="padding-left: 30px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">public function __construct($Lat1, $Lon1, $Distance, $boolMiles)<br />{</span></p>
<p style="padding-left: 60px;" mce_style="padding-left: 60px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">$RadCalcConst=M_PI/180;<br />$InvRadCalcConst=180/M_PI;</span></p>
<p style="padding-left: 60px;" mce_style="padding-left: 60px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">// Convert degrees to radians<br />$Lat1 = $Lat1 * $RadCalcConst;<br />$Lon1Rad = $Lon1 * $RadCalcConst;</span></p>
<p style="padding-left: 60px;" mce_style="padding-left: 60px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">// Miles or kilometers?</span></p>
<p style="padding-left: 60px;" mce_style="padding-left: 60px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">if ($boolMiles)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $DistRad = $Distance * 2.52780586e-4; // 1/3956 radius of the earth in miles<br />else<br />&nbsp; &nbsp; &nbsp; $DistRad = $Distance * 1.57070574e-4; // inverse radius of the earth in km</span></p>
<p style="padding-left: 60px;" mce_style="padding-left: 60px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">if ($DistRad &gt;= 2*M_PI)<br />{</span></p>
<p style="padding-left: 90px;" mce_style="padding-left: 90px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">$this-&gt;LatN = 90;<br />$this-&gt;LatS = -90;<br />$this-&gt;LonE = 180;<br />$this-&gt;LonW = -180;<br />return;</span></p>
<p style="padding-left: 60px;" mce_style="padding-left: 60px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">}</span></p>
<p style="padding-left: 60px;" mce_style="padding-left: 60px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">//&nbsp;&nbsp;&nbsp;&nbsp; North Latitude boundary<br />$this-&gt;LatN = $Lat1 + $DistRad;<br />// Convert back to degrees<br />$this-&gt;LatN = $this-&gt;LatN * $InvRadCalcConst;<br />// check for wrapping<br />if ($this-&gt;LatN &gt; 90.0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $this-&gt;LatN = 90.0 &#8211; fmod($LatN,90.0);</span></p>
<p style="padding-left: 60px;" mce_style="padding-left: 60px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">// South Latitude<br />$this-&gt;LatS&nbsp; = $Lat1 &#8211; $DistRad;<br />// Convert back to degrees<br />$this-&gt;LatS = $this-&gt;LatS * $InvRadCalcConst;<br />// check for wrapping<br />if ($this-&gt;LatS &lt; -90.0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $this-&gt;LatS = -90.0;</span></p>
<p style="padding-left: 60px;" mce_style="padding-left: 60px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">// if at a pole, Longititude goes from -180 to 180<br />if ( abs($Lat1) == M_PI/2)<br />{</span></p>
<p style="padding-left: 90px;" mce_style="padding-left: 90px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">$this-&gt;LonE = 180;<br />$this-&gt;LonW = -180;</span></p>
<p style="padding-left: 60px;" mce_style="padding-left: 60px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">}<br />else<br />{</span></p>
<p style="padding-left: 90px;" mce_style="padding-left: 90px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">$tmp=sin($Lat1);<br />$tmp=$tmp*$tmp;</span></p>
<p style="padding-left: 90px;" mce_style="padding-left: 90px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">// calculate East Longitude boundary<br />$dLon = abs(atan2(sin($DistRad) * cos($Lat1), cos($DistRad)- $tmp));<br />$this-&gt;LonE = fmod( $Lon1Rad + $dLon +M_PI,2*M_PI )-M_PI;<br />// convert back to degrees<br />$this-&gt;LonE = $this-&gt;LonE * $InvRadCalcConst;</span><br /><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">//check to see if wrapped around 180 longitude<br /></span><span style="color: rgb(0, 0, 128);"> //**&nbsp; There was a bug here in the original version where <br />//**&nbsp; Lon1 used to be Lon1Rad<br /></span><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">if ($this-&gt;LonE &lt; $Lon1)<br />{</span></p>
<p style="padding-left: 120px;" mce_style="padding-left: 120px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">// LonW is an equal amount on the other side of $Lon1 as $LonE<br />$this-&gt;LonW = $Lon1-($this-&gt;LonE + 180);</span></p>
<p style="padding-left: 90px;" mce_style="padding-left: 90px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">}<br />else<br />{</span></p>
<p style="padding-left: 120px;" mce_style="padding-left: 120px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">// LonW is an equal amount on the other side of $Lon1 as $LonE<br />$this-&gt;LonW = 2*$Lon1 &#8211; $this-&gt;LonE;<br />// See if it wrapped<br />if ($this-&gt;LonW &lt; -180)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $this-&gt;LonW = $this-&gt;LonW + 360;</span></p>
<p style="padding-left: 90px;" mce_style="padding-left: 90px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">}</span></p>
<p style="padding-left: 60px;" mce_style="padding-left: 60px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">}</span></p>
<p style="padding-left: 30px;" mce_style="padding-left: 30px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">}&nbsp;&nbsp;&nbsp;&nbsp; //__construct()</span></p>
<p style=""><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //diGeoBox</span></p>
<p>Example php code using the diGeoBox function and tests the 180 degree meridian and the polar conditions is below.</p>
<p style="padding-left: 30px;" mce_style="padding-left: 30px;"><span mce_style="color: #000080;" style="color: rgb(0, 0, 128);">$BoundingBox = new diGeoBox( 0, -170, 15000, 1);<br />echo &#8216;LonE = &#8216;.$BoundingBox-&gt;LonE. &#8220;\n&#8221;;<br />echo &#8216;LonW = &#8216;.$BoundingBox-&gt;LonW. &#8220;\n&#8221;;<br />echo &#8216;LatN = &#8216;.$BoundingBox-&gt;LatN. &#8220;\n&#8221;;<br />echo &#8216;LatS = &#8216;.$BoundingBox-&gt;LatS. &#8220;\n&#8221;;</span></p>
<p style="">Using this function at <a target="_self" title="CrossroadsAngel.com" mce_href="http://crossroadsangel.com/" href="http://crossroadsangel.com/">CrossroadsAngel.com</a> requires that street address and zip codes be converted into latitude and longitude coordinates (geocoding).&nbsp; Because <a target="_self" title="CrossroadsAngel.com" mce_href="http://crossroadsangel.com/" href="http://crossroadsangel.com/">CrossroadsAngel.com</a> is a commercial website, and because I need to store converted locations in a local database for performance reasons, I am using the <a target="_self" title="NAC Geographic Products" mce_href="http://nacgeo.com/" href="http://nacgeo.com/">NAC Geographic Products</a> webservice for stored conversion, and Google Maps API for geocoding the &#8220;search center&#8221; location.&nbsp; The Google Maps API is accessed using JavaScript from the client browser and the converted coordinates are sent to the server for query execution.</p>
]]></content:encoded>
			<wfw:commentRss>http://sociacall.com/blog/2009/09/07/global-proximity-box-calculations/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

