5

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;
}   
Martijn
  • 3,696
  • 2
  • 38
  • 64
  • `$lngDelta = ($lngTo - $lngFrom) % 360;`? – Aleksei Matiushkin Dec 01 '14 at 15:11
  • @mudasobwa, modulo 360 (or rather 2 * pi) wouldn't work. Inversion is needed somewhere. I have some limited success using `$lngDelta = abs($lngTo - $lngFrom); $lngDelta = min($lngDelta, (2 * pi()) - $lngDelta);`, but this still produces rather large errors at the poles (which I can life with) and pretty much slows it down to roughly the speed as haversine (which makes using equirectangular approximation pointless). – Martijn Dec 01 '14 at 15:17

2 Answers2

5

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

Community
  • 1
  • 1
david strachan
  • 7,174
  • 2
  • 23
  • 33
  • These have the same problem I describe in my question: `Equirectangular(0,-179,0,179)` returns 39819.030640452, `Haversine(0,-179,0,179)` returns 222.45268514219. Clearly Equirectangular is not calculating the shortest distance. `SphericalLawOfCosines` returns 222.45268514218, nearly the same as Haversine. The Equirectangular formula has trouble with distances crossing the -180/180 degree longitude meridian. – Martijn Dec 02 '14 at 06:34
  • @Martijn have modified `Equirectangular()`to consider comment. – david strachan Dec 02 '14 at 17:47
0

You reuse latDelta one of the uses have them added, the other subtracted.

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

have working javascript.

translated ..

$x = ($lngTo-$lngFrom) * cos(($latTo+$latFrom)/2);
$y = ($latTo-$latFrom)
$d = sqrt($x*$x + $y*$y) * EARTH_RADIUS;
exussum
  • 18,275
  • 8
  • 32
  • 65
  • I'm not seeing the difference between my function and the formula on that page. (in fact, that page was used as reference). – Martijn Dec 01 '14 at 15:26