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

Language agnostic 如何计算球体上从点到线段的距离?,language-agnostic,geometry,gis,geospatial,distance,Language Agnostic,Geometry,Gis,Geospatial,Distance,我在地球上有一条线段(大圆部分)。线段由其端点的坐标定义。显然,两点定义了两条线段,所以假设我对较短的线段感兴趣 我得到了第三个点,我正在寻找线和点之间的(最短)距离 所有坐标均以经度\纬度(WGS 84)表示 如何计算距离 任何一种合理的编程语言的解决方案都可以 试试看,问问数学博士。您仍然需要将经度/纬度转换为球坐标和地球半径的比例,但这似乎是一个好的方向。球体上两点之间的最短距离是穿过两点的大圆的较小一侧。我相信你已经知道了。这里有一个类似的问题,可以帮助您对其进行数学建模 老实说,我不确

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

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

所有坐标均以经度\纬度(WGS 84)表示

如何计算距离


任何一种合理的编程语言的解决方案都可以

试试看,问问数学博士。您仍然需要将经度/纬度转换为球坐标和地球半径的比例,但这似乎是一个好的方向。

球体上两点之间的最短距离是穿过两点的大圆的较小一侧。我相信你已经知道了。这里有一个类似的问题,可以帮助您对其进行数学建模


老实说,我不确定你得到这个编码示例的可能性有多大。

我现在基本上在寻找相同的东西,只是严格地说,我不关心大圆的一段,而只是想要到整圆上任何一点的距离

我目前正在调查两个链接:

提到“交叉轨道距离”,这基本上似乎是你正在寻找的

此外,在PostGIS邮件列表上的以下线程中,尝试似乎是(1)使用用于二维平面上直线距离的相同公式(使用PostGIS的“直线定位点”)确定大圆上最近的点,然后(2)计算该点与球体上第三点之间的距离。我不知道数学上的步骤(1)是否正确,但我会感到惊讶

最后,我刚刚看到以下链接在“相关”下:


以下是我自己的解决方案,基于中的想法。我很高兴看到你的反馈

先声明。此解决方案适用于球体。地球不是一个球体,坐标系(WGS 84)并不认为它是一个球体。所以这只是一个近似值,我不能真正估计误差。另外,对于非常小的距离,假设所有物体都是共面的,也可能得到很好的近似值。我也不知道距离有多小

现在谈正事。我将调用线A、B和第三点C的端点。基本上,算法是:

  • 首先将坐标转换为笛卡尔坐标(原点位于地球中心)-
  • 使用以下3个向量积计算T,即直线AB上距离C最近的点:

    G=A×B

    F=cxg

    T=gxf

  • 标准化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;
    } 
    
    double[]nearestPointSegment(double[]a、double[]b、double[]c)
    {
    双[]t=最接近的点大圆(a,b,c);
    if(第(a、b、t)段)
    返回t;
    返回(距离(a,c)<距离(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;
       }    
    
    布尔onSegment(double[]a,double[]b,double[]t)
    {
    //应该是返回距离(a,t)+距离(b,t)=距离(a,b),
    //但由于舍入误差,我们使用:
    返回Math.abs(距离(a,b)-距离(a,t)-距离(b,t))<精度;
    }    
    
    这是公认答案的完整代码,如ideone fiddle(已找到):

    最近的节点是:50.17493121381319,19.05846668493702

    但我对这些数据有疑问:

    double [] a = {52.00118, 17.53933};
    double [] b = {52.00278, 17.54008};
    //point
    double [] c = {52.008308, 17.542927};
    
    最近的节点是:52.00834987257176,17.542691313436357,这是错误的


    我认为两点指定的线不是一个闭合段。

    如果有人需要它,这是loleksy答案移植到c#

    private static double_eQuatorialEarthRadius=6378.1370D;
    私有静态双精度_d2r=(Math.PI/180D);
    专用静态双精度=0.1;
    //哈弗森算法
    //资料来源:http://stackoverflow.com/questions/365826/calculate-distance-between-2-gps-coordinates
    专用静态双Haversinenm(双lat1、双long1、双lat2、双long2){
    返回(1000D*HaversineInKM(lat1、long1、lat2、long2));
    }
    专用静态双HaversineInKM(双lat1、双long1、双lat2、双long2){
    双dlong=(long2-long1)*\u d2r;
    双dlat=(lat2-lat1)*\u d2r;
    双a=Math.Pow(Math.Sin(dlat/2D),2D)+Math.Cos(lat1*\u d2r)*Math.Cos(lat2*\u d2r)
    *Math.Pow(Math.Sin(dlong/2D),2D);
    
    //line
    double [] a = {50.174315,19.054743};
    double [] b = {50.176019,19.065042};
    //point
    double [] c = {50.184373,19.054657};
    
    double [] a = {52.00118, 17.53933};
    double [] b = {52.00278, 17.54008};
    //point
    double [] c = {52.008308, 17.542927};
    
            private static double _eQuatorialEarthRadius = 6378.1370D;
            private static double _d2r = (Math.PI / 180D);
            private static double PRECISION = 0.1;
    
            // Haversine Algorithm
            // source: http://stackoverflow.com/questions/365826/calculate-distance-between-2-gps-coordinates
    
            private static double HaversineInM(double lat1, double long1, double lat2, double long2) {
                return  (1000D * HaversineInKM(lat1, long1, lat2, long2));
            }
    
            private static double HaversineInKM(double lat1, double long1, double lat2, double long2) {
                double dlong = (long2 - long1) * _d2r;
                double dlat = (lat2 - lat1) * _d2r;
                double a = Math.Pow(Math.Sin(dlat / 2D), 2D) + Math.Cos(lat1 * _d2r) * Math.Cos(lat2 * _d2r)
                        * Math.Pow(Math.Sin(dlong / 2D), 2D);
                double c = 2D * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1D - a));
                double d = _eQuatorialEarthRadius * c;
                return d;
            }
    
            // Distance between a point and a line
            static double pointLineDistanceGEO(double[] a, double[] b, double[] c)
            {
    
                double[] nearestNode = nearestPointGreatCircle(a, b, c);
                double result = HaversineInKM(c[0], c[1], nearestNode[0], nearestNode[1]);
    
                return result;
            }
    
            // source: http://stackoverflow.com/questions/1299567/how-to-calculate-distance-from-a-point-to-a-line-segment-on-a-sphere
            private static 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);
    
                return fromCartsian(multiplyByScalar(normalize(t), _eQuatorialEarthRadius));
            }
    
            private static double[] nearestPointSegment (double[] a, double[] b, double[] c)
            {
               double[] t= nearestPointGreatCircle(a,b,c);
               if (onSegment(a,b,t))
                 return t;
               return (HaversineInKM(a[0], a[1], c[0], c[1]) < HaversineInKM(b[0], b[1], c[0], c[1])) ? a : b;
            }
    
             private static bool 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(HaversineInKM(a[0], a[1], b[0], b[1])-HaversineInKM(a[0], a[1], t[0], t[1])-HaversineInKM(b[0], b[1], t[0], t[1])) < PRECISION;
               }
    
    
            // source: http://stackoverflow.com/questions/1185408/converting-from-longitude-latitude-to-cartesian-coordinates
            private static double[] toCartsian(double[] coord) {
                double[] result = new double[3];
                result[0] = _eQuatorialEarthRadius * Math.Cos(deg2rad(coord[0])) * Math.Cos(deg2rad(coord[1]));
                result[1] = _eQuatorialEarthRadius * Math.Cos(deg2rad(coord[0])) * Math.Sin(deg2rad(coord[1]));
                result[2] = _eQuatorialEarthRadius * Math.Sin(deg2rad(coord[0]));
                return result;
            }
    
            private static double[] fromCartsian(double[] coord){
                double[] result = new double[2];
                result[0] = rad2deg(Math.Asin(coord[2] / _eQuatorialEarthRadius));
                result[1] = rad2deg(Math.Atan2(coord[1], coord[0]));
    
                return result;
            }
    
    
            // Basic functions
            private static double[] vectorProduct (double[] a, double[] b){
                double[] result = new double[3];
                result[0] = a[1] * b[2] - a[2] * b[1];
                result[1] = a[2] * b[0] - a[0] * b[2];
                result[2] = a[0] * b[1] - a[1] * b[0];
    
                return result;
            }
    
            private static double[] normalize(double[] t) {
                double length = Math.Sqrt((t[0] * t[0]) + (t[1] * t[1]) + (t[2] * t[2]));
                double[] result = new double[3];
                result[0] = t[0]/length;
                result[1] = t[1]/length;
                result[2] = t[2]/length;
                return result;
            }
    
            private static double[] multiplyByScalar(double[] normalize, double k) {
                double[] result = new double[3];
                result[0] = normalize[0]*k;
                result[1] = normalize[1]*k;
                result[2] = normalize[2]*k;
                return result;
            }
    
    Location a;
    Location b;
    Location x;
    
    double ax = a.distanceTo(x);
    double alfa = (Math.abs(a.bearingTo(b) - a.bearingTo(x))) / 180
                * Math.PI;
    double distance = Math.sin(alfa) * ax;