从Lat / Lng点到Minor Arc段

时间:2015-09-24 21:46:26

标签: matlab gps gis distance latitude-longitude

我需要计算从lat / lng GPS点P到2个其他lat / lng GPS点A和B描述的线段的最短距离。

'跨赛道距离'帮助我计算P和A和B描述的大圆之间的最短距离。

但是,这不是我想要的。我需要P和A-B的之间的距离,而不是整个大圆。

我使用了http://www.movable-type.co.uk/scripts/latlong.html

中的以下实现
Formula:    dxt = asin( sin(δ13) ⋅ sin(θ13−θ12) ) ⋅ R
where:
δ13 is (angular) distance from start point to third point
θ13 is (initial) bearing from start point to third point
θ12 is (initial) bearing from start point to end point
R is the earth’s radius

以下图片希望能够展示我想要解决的问题: Cross-Track distance Correct Cross-Track distance Incorrect

在第一张图片中,由绿色线指示的跨轨距离是正确的,实际上是到线段AB的最短距离。

在第二张图片中显示了跨轨道距离的问题,在这种情况下,我希望最短距离为简单距离AP,但是跨轨距离给出了由红色<指示的距离/ strong>行。

如何更改算法以将此考虑在内,或检查X点是否在AB内。是否可以通过计算方式完成此操作?或者迭代是唯一可能的(昂贵的)解决方案? (沿着AB取N个点并计算从P到所有这些点的最小距离)

为简单起见,图像中的所有线条都是笔直的。实际上,这些是大圆上的小弧

7 个答案:

答案 0 :(得分:18)

首先,一些命名法:
我们的弧线从p1到p2。
我们的第三点是p3。
与大圆相交的虚点是p4。
p1由lat1,lon1定义; p2 by lat2,lon2;等。
dis12是从p1到p2的距离;等。
bear12是从p1到p2的轴承;等。
dxt是跨轨道距离。
dxa是跨弧距离,我们的目标!

请注意,交叉跟踪公式依赖于相对方位bear13-bear12

我们有3个案件要处理。

案例1:相对方位是钝的。所以,dxa = dis13。

Case 1

案例2.1:相对方位是尖锐的,并且p4落在我们的弧上。 所以,dxa = dxt。

Case 2.1

案例2.2:相对方位是尖锐的,并且p4超出了我们的弧度。 所以,dxa = dis23

enter image description here

算法:

步骤1:如果相对方位是钝角,则dxa = dis13
完成!
步骤2:如果相对轴承是急性的:
    2.1:查找dxt。
    2.3:查找dis12。
    2.4:查找dis14。
    2.4:如果dis14&gt; dis12,则dxa = dis23。
      完成!
  2.5:如果我们到达此处,则dxa = abs(dxt)

MATLAB代码:

function [ dxa ] = crossarc( lat1,lon1,lat2,lon2,lat3,lon3 )
%// CROSSARC Calculates the shortest distance in meters 
%// between an arc (defined by p1 and p2) and a third point, p3.
%// Input lat1,lon1,lat2,lon2,lat3,lon3 in degrees.
    lat1=deg2rad(lat1); lat2=deg2rad(lat2); lat3=deg2rad(lat3);
    lon1=deg2rad(lon1); lon2=deg2rad(lon2); lon3=deg2rad(lon3);

    R=6371000; %// Earth's radius in meters
    %// Prerequisites for the formulas
    bear12 = bear(lat1,lon1,lat2,lon2);
    bear13 = bear(lat1,lon1,lat3,lon3);
    dis13 = dis(lat1,lon1,lat3,lon3);

    %// Is relative bearing obtuse?
    if abs(bear13-bear12)>(pi/2)
        dxa=dis13;
    else
        %// Find the cross-track distance.
        dxt = asin( sin(dis13/R)* sin(bear13 - bear12) ) * R;

        %// Is p4 beyond the arc?
        dis12 = dis(lat1,lon1,lat2,lon2);
        dis14 = acos( cos(dis13/R) / cos(dxt/R) ) * R;
        if dis14>dis12
            dxa=dis(lat2,lon2,lat3,lon3);
        else
            dxa=abs(dxt);
        end   
    end
end

function [ d ] = dis( latA, lonA, latB, lonB )
%DIS Finds the distance between two lat/lon points.
R=6371000;
d = acos( sin(latA)*sin(latB) + cos(latA)*cos(latB)*cos(lonB-lonA) ) * R;
end

function [ b ] = bear( latA,lonA,latB,lonB )
%BEAR Finds the bearing from one lat/lon point to another.
b=atan2( sin(lonB-lonA)*cos(latB) , ...
    cos(latA)*sin(latB) - sin(latA)*cos(latB)*cos(lonB-lonA) );
end

示例输出:展示所有案例。见下面的地图。

>> crossarc(-10.1,-55.5,-15.2,-45.1,-10.5,-62.5)
ans =
   7.6709e+05
>> crossarc(40.5,60.5,50.5,80.5,51,69)
ans =
   4.7961e+05
>> crossarc(21.72,35.61,23.65,40.7,25,42)
ans =
   1.9971e+05

地图上的相同输出!:

演示案例1:

Case 1 on map

演示案例2.1:

Case 2.1 on map

演示案例2.2:

Case 2.2 on map

感谢:http://www.movable-type.co.uk/scripts/latlong.html
对于公式
并且:http://www.darrinward.com/lat-long/?id=1788764
用于生成地图图像。

答案 1 :(得分:1)

对于100-1000m的球形问题,很容易转换为 笛卡尔空间,使用等角矩形投影 然后继续学校数学:
使用“线段距离”功能,易于实现。 该功能使用(有时返回)线A,B上的投影点X的相对前/后位置。值是

    如果投影点在线段内,则在区间[0,1]中
  • 如果X在A之前,则为负,
  • 如果在B.之外,则为> 1 如果相对位置在0,1之间,则采用正常距离,如果在开始点和线端点的较短距离之外,则为A,B。

这种/或非常相似的笛卡尔实现的一个例子是Shortest distance between a point and a line segment

答案 2 :(得分:1)

并添加Sga实现的python翻译:

    def bear(latA, lonA, latB, lonB):
        # BEAR Finds the bearing from one lat / lon point to another.
        return math.atan2(
            math.sin(lonB - lonA) * math.cos(latB),
            math.cos(latA) * math.sin(latB) - math.sin(latA) * math.cos(latB) * math.cos(lonB - lonA)
        )


    def pointToLineDistance(lon1, lat1, lon2, lat2, lon3, lat3):
        lat1 = math.radians(lat1)
        lat2 = math.radians(lat2)
        lat3 = math.radians(lat3)
        lon1 = math.radians(lon1)
        lon2 = math.radians(lon2)
        lon3 = math.radians(lon3)
        R = 6378137

        bear12 = bear(lat1, lon1, lat2, lon2)
        bear13 = bear(lat1, lon1, lat3, lon3)
        dis13 = distance( (lat1, lon1), (lat3, lon3)).meters

        # Is relative bearing obtuse?
        if math.fabs(bear13 - bear12) > (math.pi / 2):
            return dis13

        # Find the cross-track distance.
        dxt = math.asin(math.sin(dis13 / R) * math.sin(bear13 - bear12)) * R

        # Is p4 beyond the arc?
        dis12 = distance((lat1, lon1), (lat2, lon2)).meters
        dis14 = math.acos(math.cos(dis13 / R) / math.cos(dxt / R)) * R
        if dis14 > dis12:
            return distance((lat2, lon2), (lat3, lon3)).meters
        return math.fabs(dxt)

答案 3 :(得分:0)

/**
 * Calculates the euclidean distance from a point to a line segment.
 *
 * @param v     the point
 * @param a     start of line segment
 * @param b     end of line segment 
 * @return      an array of 2 doubles:
 *              [0] distance from v to the closest point of line segment [a,b],
 *              [1] segment coeficient of the closest point of the segment.
 *              Coeficient values < 0 mean the closest point is a.
 *              Coeficient values > 1 mean the closest point is b.
 *              Coeficient values between 0 and 1 mean how far along the segment the closest point is.
 *
 * @author         Afonso Santos
 */
public static
double[]
distanceToSegment( final R3 v, final R3 a, final R3 b )
{
    double[] results    = new double[2] ;

    final R3     ab_    = b.sub( a ) ;
    final double ab     = ab_.modulus( ) ;

    final R3     av_    = v.sub( a ) ;
    final double av     = av_.modulus( ) ;

    if (ab == 0.0)                       // a and b coincide
    {
        results[0] = av ;                // Distance
        results[1] = 0.0 ;               // Segment coeficient.
    }
    else
    {
        final double avScaProjAb  = av_.dot(ab_) / ab ;
        final double abCoeficient = results[1] = avScaProjAb / ab ;

        if (abCoeficient <= 0.0)                 // Point is before start of the segment ?
            results[0] = av ;                    // Use distance to start of segment.
        else if (abCoeficient >= 1.0)            // Point is past the end of the segment ?
            results[0] = v.sub( b ).modulus() ;    // Use distance to end of segment.
        else                                       // Point is within the segment's start/end perpendicular boundaries.
        {
            if (avScaProjAb >= av)                    // Test to avoid machine float representation epsilon rounding errors that would result in expection on sqrt.
                results[0] = 0.0 ;                    // a, b and v are colinear.
            else
                results[0] = Math.sqrt( av * av - avScaProjAb * avScaProjAb ) ;        // Perpendicular distance from point to segment.
        }
    }

    return results ;
}

上述方法需要笛卡尔3D空间参数,并且您要求使用lat / lon参数。要进行转换,请使用

/**
 * Calculate 3D vector (from center of earth).
 * 
 * @param latDeg    latitude (degrees)
 * @param lonDeg    longitude (degrees)
 * @param eleMtr    elevation (meters)
 * @return          3D cartesian vector (from center of earth).
 * 
 * @author          Afonso Santos
 */
public static
R3
cartesian( final double latDeg, final double lonDeg, final double eleMtr )
{
    return versor( latDeg, lonDeg ).scalar( EARTHMEANRADIUS_MTR + eleMtr ) ;
}

对于剩余的3D / R3代码或如何计算到路径/路径/轨道检查的距离 https://sourceforge.net/projects/geokarambola/

答案 4 :(得分:0)

wdickerson答案中添加Java版本:

public static double pointToLineDistance(double lon1, double lat1, double lon2, double lat2, double lon3, double lat3) {
    lat1 = Math.toRadians(lat1);
    lat2 = Math.toRadians(lat2);
    lat3 = Math.toRadians(lat3);
    lon1 = Math.toRadians(lon1);
    lon2 = Math.toRadians(lon2);
    lon3 = Math.toRadians(lon3);

    // Earth's radius in meters
    double R = 6371000;

    // Prerequisites for the formulas
    double bear12 = bear(lat1, lon1, lat2, lon2);
    double bear13 = bear(lat1, lon1, lat3, lon3);
    double dis13 = dis(lat1, lon1, lat3, lon3);

    // Is relative bearing obtuse?
    if (Math.abs(bear13 - bear12) > (Math.PI / 2))
        return dis13;

    // Find the cross-track distance.
    double dxt = Math.asin(Math.sin(dis13 / R) * Math.sin(bear13 - bear12)) * R;

    // Is p4 beyond the arc?
    double dis12 = dis(lat1, lon1, lat2, lon2);
    double dis14 = Math.acos(Math.cos(dis13 / R) / Math.cos(dxt / R)) * R;
    if (dis14 > dis12)
        return dis(lat2, lon2, lat3, lon3);
    return Math.abs(dxt);
}

private static double dis(double latA, double lonA, double latB, double lonB) {
    double R = 6371000;
    return Math.acos(Math.sin(latA) * Math.sin(latB) + Math.cos(latA) * Math.cos(latB) * Math.cos(lonB - lonA)) * R;
}

private static double bear(double latA, double lonA, double latB, double lonB) {
    // BEAR Finds the bearing from one lat / lon point to another.
    return Math.atan2(Math.sin(lonB - lonA) * Math.cos(latB), Math.cos(latA) * Math.sin(latB) - Math.sin(latA) * Math.cos(latB) * Math.cos(lonB - lonA));
}

答案 5 :(得分:0)

添加wdickerson实现的ObjectiveC转换:

record.value()

答案 6 :(得分:0)

添加一个 python+numpy 实现(现在您可以将您的经度和纬度作为数组传递并同时计算所有距离而无需循环)。

def _angularSeparation(long1, lat1, long2, lat2):
    """All radians
    """
    t1 = np.sin(lat2/2.0 - lat1/2.0)**2
    t2 = np.cos(lat1)*np.cos(lat2)*np.sin(long2/2.0 - long1/2.0)**2
    _sum = t1 + t2

    if np.size(_sum) == 1:
        if _sum < 0.0:
            _sum = 0.0
    else:
        _sum = np.where(_sum < 0.0, 0.0, _sum)

    return 2.0*np.arcsin(np.sqrt(_sum))


def bear(latA, lonA, latB, lonB):
    """All radians
    """
    # BEAR Finds the bearing from one lat / lon point to another.
    result = np.arctan2(np.sin(lonB - lonA) * np.cos(latB),
                        np.cos(latA) * np.sin(latB) - np.sin(latA) * np.cos(latB) * np.cos(lonB - lonA)
                        )

    return result


def pointToLineDistance(lon1, lat1, lon2, lat2, lon3, lat3):
    """All radians
    points 1 and 2 define an arc segment,
    this finds the distance of point 3 to the arc segment. 
    """

    result = lon1*0
    needed = np.ones(result.size, dtype=bool)

    bear12 = bear(lat1, lon1, lat2, lon2)
    bear13 = bear(lat1, lon1, lat3, lon3)
    dis13 = _angularSeparation(lon1, lat1, lon3, lat3)

    # Is relative bearing obtuse?
    diff = np.abs(bear13 - bear12)
    if np.size(diff) == 1:
        if diff > np.pi:
            diff = 2*np.pi - diff
        if diff > (np.pi / 2):
            return dis13
    else:
        solved = np.where(diff > (np.pi / 2))[0]
        result[solved] = dis13[solved]
        needed[solved] = 0
    
    # Find the cross-track distance.
    dxt = np.arcsin(np.sin(dis13) * np.sin(bear13 - bear12))

    # Is p4 beyond the arc?
    dis12 = _angularSeparation(lon1, lat1, lon2, lat2)
    dis14 = np.arccos(np.cos(dis13) / np.cos(dxt))
    if np.size(dis14) == 1:
        if dis14 > dis12:
            return _angularSeparation(lon2, lat2, lon3, lat3)
    else:
        solved = np.where(dis14 > dis12)[0]
        result[solved] = _angularSeparation(lon2[solved], lat2[solved], lon3[solved], lat3[solved])

    if np.size(lon1) == 1:
        return np.abs(dxt)
    else:
        result[needed] = np.abs(dxt[needed])
        return result