This is my first look at geo location. Please forgive my long question. I hope it is clear.
I have a database holding records with lat & lng. I am trying to write a PHP class which defines four decimal coordinates that form a boundary box (to then retrieve all records within the box).
I only need to know which locations are within n km of the user location. I do not need the distances from the user to the locations.
I am building on the first part of this answer.
At the core of the above answer is:
10 km in a straight line is:
on the latitude is equal to ~1'(minute) on the longitude is equal to ~6'(minutes)
Using this as a basis, do some quick math and in your query add to the WHERE clause removing any locations that are outside the 'box' that is created by adding the buffer zone with the assumption of 1' lat & 6' long.
Mmmm, "do some quick math" says Patrick. I have failed to do the maths correctly after a few days.
My class is hanging on in there and seems to work for 10km. However, I am failing to make it work with any other distance.
First I moved to seconds, so 10km is:
on the latitude is equal to 60 seconds
on the longitude is equal to 360 seconds
This worked fine.
However, I then tried 5km is:
on the latitude is equal to 30 seconds
on the longitude is equal to 180 seconds
This did not work.
Can you explain why this approach does not work for 5km (and other distances I have tried)?
Below is my class code. Clearly the switch is rubbish. Any help appreciated.
class coords
{
/*
Calculate a bounding box from a decimal coordinate and distance.
The public vars are populated with minimum and maximum longitudes and latitudes which define the boundary.
*/
public $dec_lat_min;
public $dec_lat_max;
public $dec_lng_min;
public $dec_lng_max;
function init($dec_lat, $dec_lng, $dist)
{
// This switch is a terrible way to allow multiple distances.
// 10km = 1 min lat = 60 sec lat
// 10km = 6 min lng = 360 sec lng
// 5km = 30 sec lat
// 5km = 180 sec lng
// 1km = 6 sec lat
// 1km = 36 sec lat
// 500m = 3 sec lat
// 500m = 18 sec lat
switch($dist)
{
case 10: // 10km
$sec_diff_lat = 60;
$sec_diff_lng = 360;
break;
case 5: // 5km
$sec_diff_lat = 30;
$sec_diff_lng = 180;
break;
case 1: // 1km
$sec_diff_lat = 6;
$sec_diff_lng = 36;
break;
default: // 500m
$sec_diff_lat = 3;
$sec_diff_lng = 18;
break;
}
// Convert lat to DMS
$dms_lat = $this->dec2dms($dec_lat);
// Allow for western hemisphere (ie negative)
$dms_lat['hem'] == '-' ? $h = -1 : $h = 1;
// Populate min and max latitudes
$this->dec_lat_min = $this->dms2dec($dms_lat['deg'],$dms_lat['min'],$dms_lat['sec']+(-1 * $sec_diff_lat * $h),$dms_lat['hem']);
$this->dec_lat_max = $this->dms2dec($dms_lat['deg'],$dms_lat['min'],$dms_lat['sec']+($sec_diff_lat * $h),$dms_lat['hem']);
$dms_lng = $this->dec2dms($dec_lng);
$dms_lng['hem'] == '-' ? $h = -1 : $h = 1;
$this->dec_lng_min = $this->dms2dec($dms_lng['deg'],$dms_lng['min'],$dms_lng['sec']+(-1 * $sec_diff_lng * $h),$dms_lng['hem']);
$this->dec_lng_max = $this->dms2dec($dms_lng['deg'],$dms_lng['min'],$dms_lng['sec']+($sec_diff_lng * $h),$dms_lng['hem']);
}
function dec2dms($d)
{
$d = (string)$d;
// got dashes?
if ($d[0] == "-") {
$hem = '-';
$dVal = substr($d,1);
} else {
$hem = '';
$dVal = $d;
}
// degrees = degrees
$dVals = explode('.', $dVal);
$dmsDeg = $dVals[0];
// * 60 = mins
$dRemainder = ('0.'.$dVals[1]) * 60;
$dmsMinVals = explode('.', $dRemainder);
$dmsMin = $dmsMinVals[0];
// * 60 again = secs
$dMinRemainder = ('0.'.$dmsMinVals[1]) * 60;
$dmsSec = round($dMinRemainder);
return array("deg"=>$dmsDeg,"min"=>$dmsMin,"sec"=>$dmsSec, "hem"=>$hem);
}
function dms2dec($deg,$min,$sec,$hem)
{
// find decimal latitude
$d = $deg+((($min*60)+($sec))/3600);
$d = $hem.$d;
return $this->round100000($d);
}
function round100000($v)
{
return round($v * 100000) / 100000;
}
}
10 km in a straight line is: on the longitude is equal to ~6 minutes
The distance to longitude ratio is not a constant - it depends on the the user's latitude. You can use this function for the longitude difference:
function distanceToLng($distance,$latDeg) {
$latRad = $latDeg / 180 * M_PI;
$R = 6371;
return atan2(1*sin($distance/$R)*cos($latRad), cos($distance/$R) - sin($latRad)*sin($latRad)) * (180 / M_PI);
}
The formula is taken from this question.
You can calculate latitude difference from a given distance using something like this:
$lngDeg = $distanceInKm/6371 * 180 / M_PI
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With