这是我自己的解决方案,基于以下想法询问数学博士。我很高兴看到您的反馈。
首先免责声明。该解对于球体是正确的。地球不是球体,坐标系 (WGS 84) 也不假定它是球体。所以这只是一个近似值,我无法真正估计误差。此外,对于非常小的距离,通过假设所有东西都是共面的,也可能获得良好的近似值。我再次不知道距离必须有多“小”。
现在开始做生意。我将线的末端称为 A、B 和第三点 C。基本上,该算法是:
-
首先将坐标转换为笛卡尔坐标(原点位于地球中心)-例如这里.
-
使用以下 3 个向量积计算 T,即 AB 线上最接近 C 的点:
G = A x B
F=C×G
T=G×F
-
标准化 T 并乘以地球半径。
-
将 T 转换回经度\纬度。
-
计算 T 和 C 之间的距离 -例如这里.
如果您正在寻找 C 与 A 和 B 定义的大圆之间的距离,这些步骤就足够了。如果您像我一样对 C 与较短线段之间的距离感兴趣,则需要采取额外的步骤来验证T确实在这个段上。如果不是,那么最近的点必然是 A 端或 B 端之一 - 最简单的方法是检查哪一端。
一般来说,三个向量乘积背后的想法如下。第一个 (G) 给出了 A 和 B 的大圆平面(因此包含 A、B 和原点的平面)。第二个 (F) 给出了穿过 C 并垂直于 G 的大圆。然后 T 是 F 和 G 定义的大圆的交点,通过归一化并乘以 R 得到正确的长度。
下面是一些用于执行此操作的部分 Java 代码。
寻找大圆上最近的点。输入和输出是长度为 2 的数组。中间数组的长度为 3。
double[] nearestPointGreatCircle(double[] a, double[] b, double c[])
{
double[] a_ = toCartsian(a);
double[] b_ = toCartsian(b);
double[] c_ = toCartsian(c);
double[] G = vectorProduct(a_, b_);
double[] F = vectorProduct(c_, G);
double[] t = vectorProduct(G, F);
normalize(t);
multiplyByScalar(t, R_EARTH);
return fromCartsian(t);
}
寻找线段上最近的点:
double[] nearestPointSegment (double[] a, double[] b, double[] c)
{
double[] t= nearestPointGreatCircle(a,b,c);
if (onSegment(a,b,t))
return t;
return (distance(a,c) < distance(b,c)) ? a : c;
}
这是一种简单的方法,用于测试点 T(我们知道它与 A 和 B 在同一个大圆上)是否在该大圆的较短线段上。然而,还有更有效的方法可以做到这一点:
boolean onSegment (double[] a, double[] b, double[] t)
{
// should be return distance(a,t)+distance(b,t)==distance(a,b),
// but due to rounding errors, we use:
return Math.abs(distance(a,b)-distance(a,t)-distance(b,t)) < PRECISION;
}