Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Equirectangular approximation in PHP

I'm trying to calculate the distance between two latitude/longitude coordinates using the equirectangular approximation formula in PHP, but I get different results than the haversine formula (which I know is correct) for certain longitude coordinates.

define('EARTH_RADIUS', 6371);

function equirectangularRad($latFrom, $lngFrom, $latTo, $lngTo) {
    $latDelta = $latTo - $latFrom;
    $lngDelta = $lngTo - $lngFrom;

    $x = $lngDelta * cos(($latFrom + $latTo) * .5);
    $radius = sqrt(($x * $x) + ($latDelta * $latDelta));

    return $radius * EARTH_RADIUS;
}

Distances always seem to be calculated crossing the prime meridian instead of the shortest distance. i.e. from coordinate (lat=0, long=-180) to (lat=0, long=180) along the equator, the distance should be zero. Instead the function returns the circumference of the earth along the equator; roughly 40030 kilometer.

The problem seems to stem from the calculation of $lngDelta, but all implementations I can find, for any programming language, uses this same formula. Am I missing some important detail or is this formula truely not a drop-in replacement for haversine (ignoring the obvious accuracy difference)?

For reference; this is the haversine implementation I use:

function haversineRad($latFrom, $lngFrom, $latTo, $lngTo) {
    $latDelta = $latTo - $latFrom;
    $lngDelta = $lngTo - $lngFrom;              

    $latSin = sin($latDelta * .5);
    $lngSin = sin($lngDelta * .5);
    $radius = 2. * asin(sqrt(($latSin * $latSin) + cos($latFrom) * cos($latTo) * ($lngSin * $lngSin)));

    return $radius * EARTH_RADIUS;
}   
like image 982
Martijn Avatar asked Dec 01 '14 14:12

Martijn


1 Answers

I don't know where you got your formulas. The 3 formulas below all calculate distance between 2 coordinates.

Equirectangular

function Equirectangular($lat1,$lng1,$lat2,$lng2){
$x = deg2rad($lng2-$lng1) * cos(deg2rad($lat1+$lat2)/2);
$y = deg2rad($lat1-$lat2);
$R = 6372.8; // gives d in km
$distance = sqrt($x*$x + $y*$y) * $R;
return $distance;
}

EDIT Modified Equirectangular() to consider comment. Have made the lng values absolute using php abs() function. It starts to drift from Haversine when lng2 passes from negative to positive.

function Equirectangular($lat1,$lng1,$lat2,$lng2){
$lng1 = abs($lng1);
$lng2 = abs($lng2);
$alpha = $lng2-$lng1;
$x = deg2rad($alpha) * cos(deg2rad($lat1+$lat2)/2);
$y = deg2rad($lat1-$lat2);
$R = 6372.8; // gives d in km
$distance = sqrt($x*$x + $y*$y) * $R;
return $distance;
}

Haversine

function Haversine($lat1,$lng1,$lat2,$lng2) {
  $deltaLat = $lat2 - $lat1 ;
  $deltaLng = $lng2 - $lng1 ;
  $earthRadius = 6372.8; // 3959 in miles.
  $alpha = $deltaLat/2;
  $beta = $deltaLng/2;
  $a = sin(deg2rad($alpha)) * sin(deg2rad($alpha)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin(deg2rad($beta)) * sin(deg2rad($beta)) ;
  $c = 2 * atan2(sqrt($a), sqrt(1-$a));
  $distance = $earthRadius * $c;
  return $distance;
} 

SphericalLawOfCosines

function SphericalLawOfCosines($lat1,$lng1,$lat2,$lng2) {
 $lat1 = deg2rad($lat1);
 $lat2 = deg2rad($lat2);
 $deltaLng = deg2rad($lng2-$lng1);
 $R = 6372.8; // gives d in km
$d = acos( sin($lat1)*sin($lat2) + cos($lat1)*cos($lat2) * cos($deltaLng) ) * $R;
return $d;
}

Equirectangular is the simplest but less accurate. For the other 2 The one to use depends on the distances involved.See this Answer. For small distances (on the order of 1 meter or less) use Haversine. For larger distances use Spherical Law of Cosines.

Results from formulas

0,-179 to 0,-179 Equirectangular 0 km Haversine 0 km

0,-179 to 0,-159 Equirectangular 2224.526851 km Haversine 2224.526851 km

0,-179 to 0,-139 Equirectangular 4449.053703 km Haversine 4449.053703 km

0,-179 to 0,-119 Equirectangular 6673.580554 km Haversine 6673.580554 km

0,-179 to 0,-99 Equirectangular 8898.107406 km Haversine 8898.107406 km

0,-179 to 0,-79 Equirectangular 11122.634257 km Haversine 11122.634257 km

0,-179 to 0,-59 Equirectangular 13347.161109 km Haversine 13347.161109 km

0,-179 to 0,-39 Equirectangular 15571.68796 km Haversine 15571.68796 km

0,-179 to 0,-19 Equirectangular 17796.214811 km Haversine 17796.214811 km

0,-179 to 0,1 Equirectangular 19798.288978 km Haversine 20020.741663 km

0,-179 to 0,21 Equirectangular 17573.762126 km Haversine 17796.214811 km

0,-179 to 0,41 Equirectangular 15349.235275 km Haversine 15571.68796 km

0,-179 to 0,61 Equirectangular 13124.708423 km Haversine 13347.161109 km

0,-179 to 0,81 Equirectangular 10900.181572 km Haversine 11122.634257 km

0,-179 to 0,101 Equirectangular 8675.654721 km Haversine 8898.107406 km

0,-179 to 0,121 Equirectangular 6451.127869 km Haversine 6673.580554 km

0,-179 to 0,141 Equirectangular 4226.601018 km Haversine 4449.053703 km

0,-179 to 0,161 Equirectangular 2002.074166 km Haversine 2224.526851 km

like image 175
david strachan Avatar answered Sep 21 '22 15:09

david strachan