PHP中的全景图近似算法

5

我正在尝试使用PHP中的等距正投影近似公式计算两个纬度/经度坐标之间的距离,但是对于某些经度坐标,我的结果与哈弗赛恩公式(我知道是正确的)不同。

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;
}

距离似乎总是通过本初子午线计算,而不是最短距离。例如,从坐标(lat = 0,long = -180)到(lat = 0,long = 180)沿赤道行进,距离应为零。但实际上该函数返回了大约40030公里的赤道周长。问题似乎源自于$lngDelta的计算,但我能找到的所有实现,任何编程语言都使用相同的公式。我是否漏掉了一些重要细节,或者这个公式真的不能完全替代haversine(忽略明显的精度差异)?供参考,这是我使用的haversine实现:
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;
}   

$lngDelta = ($lngTo - $lngFrom) % 360; $lngDelta = ($lngTo - $lngFrom) % 360; - Aleksei Matiushkin
@mudasobwa,模360(或2 * pi)不起作用。需要进行反演。我使用$lngDelta = abs($lngTo - $lngFrom); $lngDelta = min($lngDelta, (2 * pi()) - $lngDelta);有一些有限的成功,但在极点仍然会产生相当大的误差(我可以接受),并且几乎将其速度降到了与haversine相同的水平(这使得使用等经纬度近似变得毫无意义)。 - Martijn
2个回答

5
我是一名有用的助手,可以为您翻译文本。
我不知道您从哪里获取了您的公式。下面的三个公式都可以计算两个坐标之间的距离。
等距圆柱投影
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;
}

编辑 修改了Equirectangular()函数以考虑评论。使用php的abs()函数将lng值变为绝对值。当lng2从负数变为正数时,它开始偏离Haversine算法。

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;
}

哈弗辛公式

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;
} 

球面余弦定理
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是最简单但不太准确的。对于另外两个,使用哪一个取决于涉及的距离。请参见此答案。 对于小距离(大约1米或更短),请使用Haversine。对于较大的距离,请使用球面余弦定理。

公式计算结果

0,-179至0,-179 Equirectangular 0公里 Haversine 0公里

0,-179至0,-159 Equirectangular 2224.526851公里 Haversine 2224.526851公里

0,-179至0,-139 Equirectangular 4449.053703公里 Haversine 4449.053703公里

0,-179至0,-119 Equirectangular 6673.580554公里 Haversine 6673.580554公里

0,-179至0,-99 Equirectangular 8898.107406公里 Haversine 8898.107406公里

0,-179到0,-79等距圆柱投影11122.634257公里Haversine 11122.634257公里

0,-179到0,-59等距圆柱投影13347.161109公里Haversine 13347.161109公里

0,-179到0,-39等距圆柱投影15571.68796公里Haversine 15571.68796公里

0,-179到0,-19等距圆柱投影17796.214811公里Haversine 17796.214811公里

0,-179到0,1等距圆柱投影19798.288978公里Haversine 20020.741663公里

0,-179到0,21等距圆柱投影17573.762126公里Haversine 17796.214811公里

0,-179到0,41等距圆柱投影15349.235275公里Haversine 15571.68796公里

0,-179到0,61等距圆柱投影13124.708423公里Haversine 13347.161109公里

0,-179 至 0,81 等距圆柱投影 10900.181572 公里 Haversine 11122.634257 公里

0,-179 至 0,101 等距圆柱投影 8675.654721 公里 Haversine 8898.107406 公里

0,-179 至 0,121 等距圆柱投影 6451.127869 公里 Haversine 6673.580554 公里

0,-179 至 0,141 等距圆柱投影 4226.601018 公里 Haversine 4449.053703 公里

0,-179 至 0,161 等距圆柱投影 2002.074166 公里 Haversine 2224.526851 公里


这些都有我在问题中描述的相同问题:Equirectangular(0,-179,0,179)返回39819.030640452,Haversine(0,-179,0,179)返回222.45268514219。显然,Equirectangular没有计算最短距离。SphericalLawOfCosines返回222.45268514218,几乎与Haversine相同。Equirectangular公式在跨越-180/180度经线时会出现问题。 - Martijn
@Martijn已经修改了Equirectangular()以考虑注释。 - david strachan

0

您可以重复使用latDelta,其中一个用法是将它们相加,另一个用法是将其减去。

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

有可用的 JavaScript。

已翻译...

$x = ($lngTo-$lngFrom) * cos(($latTo+$latFrom)/2);
$y = ($latTo-$latFrom)
$d = sqrt($x*$x + $y*$y) * EARTH_RADIUS;

我没有看到我的函数和那个页面上的公式之间有什么区别。(事实上,那个页面被用作参考)。 - Martijn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接