如何检查坐标(纬度和经度)是否在 PHP 中的其他两个坐标之间?

How to check if a coordinate (latitude and longitude) is in between two other coordinates in PHP?

假设我有两个坐标 ab,它们都由纬度和经度组成。如何使用 PHP 检查第三个坐标 c 是否在坐标 ab 之间? 换句话说:当我用一条线连接 ab 时,我怎么知道坐标 c 是否正好在那条线上?

我已经找到了使用 Python 的叉积和点积解决简单 X 和 Y 坐标的答案,请参见此处:

但是在 PHP 中使用带有很多小数位的坐标似乎更复杂,就像由于 the limited precision for floating point numbers in PHP.

导致的纬度和经度的情况一样

所以这是一个不适用于上述解决方案的基本示例:

// Copied from the Python solution
public function isBetweenPoints($aLat, $aLng, $bLat, $bLng, $cLat, $cLng) {

    $crossProduct = ($cLat - $aLat) * ($bLng - $aLng) - ($cLng - $aLng) * ($bLat - $aLat);
    if (abs($crossProduct) < PHP_FLOAT_EPSILON) {
        return false;
    }

    $dotProduct = ($cLng - $aLng) * ($bLng - $aLng) + ($cLat - $aLat)*($bLat - $aLat);
    if ($dotProduct < 0) {
        return false;
    }

    $squaredLength = ($bLng - $aLng)*($bLng - $aLng) + ($bLat - $aLat)*($bLat - $aLat);

    return !($dotProduct > $squaredLength);
}

$aLat = 48.14723956724038;
$aLng = 11.514418017613934;
$bLat = 48.14722882951992;
$bLng = 11.51386548255687;
$cLat = 48.147645056105120;
$cLng = 11.514326333999636;

// Returns true, even if the point c is not between a and b
isBetweenPoints($aLat, $aLng, $bLat, $bLng, $cLat, $cLng);

为了更好的理解上面例子中的isBetweenPoints应该是return false,这里有一张对应的图片,上面的$latLngA$latLngB用绿线表示在底部,$latLngC 通过顶部的标记:

这个问题归结为给定两个点(唯一定义一条线)如何确定第三个点是否在同一条线上,即(a)确定这些点是否共线以及(b)确定点是否c 的 x 和 y 坐标介于其他两点的 x 和 y 坐标之间。

两点定义直线:

(y - y1) = (y2 - y1)(x2-x1)(x-x1)

如果当您替换该点的 x 和 y 时等式成立,则该点与两个点共线,例如,假设每个点都是具有属性 x 和 y 的对象:

function pointOnLineSegment($point1, $point2, $point3) {
   return abs(($point3->y-$point1->y) - (($point2->y - $point1->y)/($point2->x - $point1->x)*($point3->x - $point1->x))) < 0.001 
        && $point3->x > $point1->x 
        && $point3->x < $point2->x 
        && $point3->y > $point1->y
        && $point3->y < $point2->y; 

}

在你的例子中,x 和 y 可以是经度和纬度(或者相反,这并不重要)。

我在这里使用 0.001,但选择实际上取决于你的精度 PHP_EPSILON 绝对太低了,不能依赖。

现在当然这只适用于平面几何,因此在地图上投影需要正确地将点投影到同一测地线上(我认为墨卡托投影就是这样做的)

我认为只有你的第一个条件是错误的,它必须是 > 而不是 <

根据参考资料 python 如果它大于 epsilon 那么它就是假的。

   <?// Copied from the Python solution
 function isBetweenPoints($aLat, $aLng, $bLat, $bLng, $cLat, $cLng) {

    $crossProduct = ($cLat - $aLat) * ($bLng - $aLng) - ($cLng - $aLng) * ($bLat - $aLat);
       echo "cross_product: ". $crossProduct; //debug 
       if (abs($crossProduct) > PHP_FLOAT_EPSILON) {   // i would rethink epsilon as you will never get TRUE ;-) 
       return false;
    }

    $dotProduct = ($cLng - $aLng) * ($bLng - $aLng) + ($cLat - $aLat)*($bLat - $aLat);
    echo "<br>dotProduct: ". $dotProduct; //debug
    if ($dotProduct < 0) {
        return false;
    }

    $squaredLength = ($bLng - $aLng)*($bLng - $aLng) + ($bLat - $aLat)*($bLat - $aLat);
    echo "<br>SquaredLength: " . $squaredLength; //debug
    return !($dotProduct > $squaredLength);
}

$aLat = 1;
$aLng = 1;

$bLat = 3;
$bLng = 3;

$cLat = 2;
$cLng = 2;

//will give true

if (isBetweenPoints($aLat, $aLng, $bLat, $bLng, $cLat, $cLng) ==TRUE)
{echo "<br>Point IS between the other points<p>";}
else
{echo "<br>Point IS NOT between the other points<p>";}
  

$aLat = 1;
$aLng = 1;

$bLat = 3;
$bLng = 3;

$cLat = 2;
$cLng = 2.1;

//will give false

echo"<br>";

if (isBetweenPoints($aLat, $aLng, $bLat, $bLng, $cLat, $cLng) ==TRUE)
{echo "<br>POINT IS between the other points";}
else
{echo "<br>POINT IS NOT between the other points";}

?>