Find closest longitude and latitude in array?
Asked Answered
I

4

20

I have a longitude and latitude as a string in PHP like below

49.648881
-103.575312

And I want to take that and look in an array of values to find the closest one. The array looks like

array(
'0'=>array('item1','otheritem1details....','55.645645','-42.5323'),
'1'=>array('item1','otheritem1details....','100.645645','-402.5323')
);

I want to return the array that has the closest long and lad. In this case it would be the first one (and yes I know -400 is not a a possible value).

Is there any quick and easy way to do this? I tried array searching but that didn't work.

Difference code

function distance($lat1, $lon1, $lat2, $lon2, $unit) { 

  $theta = $lon1 - $lon2; 
  $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta)); 
  $dist = acos($dist); 
  $dist = rad2deg($dist); 
  $miles = $dist * 60 * 1.1515;
  $unit = strtoupper($unit);

  if ($unit == "K") {
    return ($miles * 1.609344); 
  } else if ($unit == "N") {
      return ($miles * 0.8684);
    } else {
        return $miles;
      }
}
Interrex answered 6/3, 2012 at 17:50 Comment(4)
Can you please provide your code where you get the distance between two lat/long pairs like function distance($lat1, $long1, $lat2, $long2) { ...?Undershoot
That makes no sense, it's simple math? 55 - 49 = 6, 55-100 = 45 6 is smaller then 45.Interrex
Do you actually know how to calculate the distance between two points on the globe?Undershoot
I added the difference code as requested.Interrex
U
35

You need to map the distance of each item to the reference point first.

Then you sort the map and then you can tell which has the lowest (or highest if you reverse the search) distance:

$ref = array(49.648881, -103.575312);

$items = array(
    '0' => array('item1','otheritem1details....','55.645645','-42.5323'),
    '1' => array('item1','otheritem1details....','100.645645','-402.5323')
);

$distances = array_map(function($item) use($ref) {
    $a = array_slice($item, -2);
    return distance($a, $ref);
}, $items);

asort($distances);

echo 'Closest item is: ', var_dump($items[key($distances)]);

Output:

Closest item is: array(4) {
  [0]=>
  string(5) "item1"
  [1]=>
  string(21) "otheritem1details...."
  [2]=>
  string(9) "55.645645"
  [3]=>
  string(8) "-42.5323"
}

Take care you have the right order of lat and long.

The distance function (only the header slightly changed and units have been dropped):

function distance($a, $b)
{
    list($lat1, $lon1) = $a;
    list($lat2, $lon2) = $b;

    $theta = $lon1 - $lon2;
    $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
    $dist = acos($dist);
    $dist = rad2deg($dist);
    $miles = $dist * 60 * 1.1515;
    return $miles;
}
Undershoot answered 6/3, 2012 at 18:8 Comment(1)
Brilliant! Great answer!Zarger
L
10

Rather using the law of cosines for distance, you can use flat earth approximation. The flat earth equations reduce the number of trig functions in the calculation. The Δlat, Δlon is the difference between your reference point and the test point.

This formula would not be accurate for long distance navigation (thousands of miles) but for this particular problem, you aren't really interested in accurate distance, but who is the closest point to me. This is a simpler formulation that should give you that.

x = Δlon * cos(lat)   // lat/lon are in radians!
y = Δlat
distance = R * sqrt( x² + y² )  // R is radius of the earth; 
                                // typical value is 6371 km

Reference: http://www.movable-type.co.uk/scripts/latlong.html

Distance code

function distanceMeters($lat1, $lon1, $lat2, $lon2) { 
  $x = deg2rad( $lon1 - $lon2 ) * Math.cos( deg2rad( ($lat1+$lat2) /2 ) );
  $y = deg2rad( $lat1 - $lat2 ); 
  $dist = 6371000.0 * Math.sqrt( $x*$x + $y*$y );

  return $dist;
}

function deg2rad(degrees) {
  var pi = Math.PI;
  return degrees * (pi/180);
}
Lederhosen answered 6/3, 2012 at 20:54 Comment(2)
LOL nvm, I just read your function name is distanceMeters... Ignore me.Interrex
If you're not concerned with the actual distance (i.e. just sorting), you can also drop R * from the last step as it's a constant multiplication for all entriesRizzi
A
1

There is no quick and easy way to do that. You have to iterate through all elements and calculate distance between them and starting point, save the result and repeat, saving the result only if it is lower then previous.

Arturoartus answered 6/3, 2012 at 17:56 Comment(0)
E
-1

Iterate through the array, comparing values to what you have. If the value is smaller than your currently stored value (or if you don't have a currently stored value), store that value instead, otherwise throw it away.

$closest = null;
foreach($array as $key => $value){
    $distance = //compare distance here;
    if ($closest === null || $closest > $distance) {
        $closest = $distance;
    };
};

Of course, this will be made more difficult by the fact that latitude and longitude are on a sphere, and longitudes 179 and -179 are closer than 90 and 179.

Exam answered 6/3, 2012 at 17:59 Comment(2)
I have updated your answer significantly, adding null as the default value instead of empty string, explicit strict check for being null, stated clearly that 179 and -179 are about longitudes.Octo
And that's why I'm a front end developer, and not a PHP guru. Thank you. :)Exam

© 2022 - 2024 — McMap. All rights reserved.