如何计算球体上从点到线段的距离?

dap*_*hez 26 language-agnostic gis geometry distance geospatial

我在地球上有一个线段(大圆圈部分).线段由其末端的坐标定义.显然,两个点定义了两个线段,所以假设我对较短的线段感兴趣.

我得到了第三点,我正在寻找线和点之间的(最短)距离.

所有坐标均以经度\纬度(WGS 84)给出.

我该如何计算距离?

任何合理的编程语言都可以使用解决方案.

dap*_*hez 19

这是我自己的解决方案,基于问Math博士的想法.我很乐意看到您的反馈.

首先免责声明.这种解决方案适用于球体.地球不是一个球体,坐标系统(WGS 84)并不认为它是一个球体.所以这只是一个近似值,我无法估计是错误.此外,对于非常小的距离,通过假设所有东西都只是一个共面,它也可能得到很好的近似.我再一次不知道距离必须"小".

现在去做生意.我将调用线A,B和第三点C的末端.基本上,算法是:

  1. 首先将坐标转换为笛卡尔坐标(原点位于地球中心) - 例如此处.
  2. 使用以下3个矢量积计算T,AB线上最接近C的点:

    G = A x B

    F = C x G.

    T = G×F

  3. 归一化T并乘以地球半径.

  4. 将T转换回经度\纬度.
  5. 计算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);
}
Run Code Online (Sandbox Code Playgroud)

找到段上最近的点:

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;
} 
Run Code Online (Sandbox Code Playgroud)

这是一种简单的测试方法,如果我们知道的点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;
   }    
Run Code Online (Sandbox Code Playgroud)

  • 在我看来,行"返回(距离(a,c)<距离(b,c))?a:c;" 应该是"返回(距离(a,c)<距离(b,c))?a:b;" (3认同)
  • 我希望有简单数字的例子 (2认同)