计算两个圆的交点?


29

我试图弄清楚如何在数学上得出给定中心Lat / Lon和每个点的半径的情况下地球表面上两个相交圆的共同点。

例如,给定:

  • 纬度/经度(37.673442,-90.234036)半径107.5海里
  • 纬度/经度(36.109997,-90.953669)半径145海里

我应该找到两个相交点,其中之一是(36.948,-088.158)。

在平坦的平面上解决这个问题很容易,但是我没有在诸如地球表面之类的不完美球体上求解方程的经验。


1
如果您所有的半径都这么小(不到几公里),那么地球在此范围内基本上是平坦的,那么您不妨选择一个准确,简单的投影并执行通常的欧几里得计算。确保将交点计算到三个以上的小数位后- 第三个小数位不精确度与您的任何一个半径一样大!
whuber

1
我应该添加单位,这些半径以NM为单位,因此距地球表面的距离仍然很小,但大于几公里。该比例如何影响失真?我试图找到一种精确度小于1nm的解决方案,因此它不必是非常精确的。谢谢!
2013年

这一切都很好,因为它表明您可以使用地球的球形模型-不需要更复杂的椭球模型。
whuber

@whuber这是否意味着可以将问题重述为:找到3个球的交点,其中一个球是地球,而另外两个球以它们各自的半径为中心?
柯克·库肯达尔

@Kirk是的,假设地球表面是球形的,那就是这样做的方法。经过一些初步计算,将其简化为3D中的Trilateration问题的特殊情况。(需要进行计算才能将沿球弧的距离转换为沿球弦的距离,这成为两个较小球体的半径。)
whuber

Answers:


21

一旦认识到这一点,在球面上就比在飞机上难得多

  1. 所讨论的点是三个球体的相互交点:一个在给定半径的x1位置(在地球表面上)下方居中的球体,一个在给定半径的x2位置(在地球表面上)下方居中的球体和地球本身,这是一个以给定半径的O =(0,0,0)为中心的球体。

  2. 前两个球体与地球表面的交点是一个圆,它定义了两个平面。因此,所有三个球体的相互交点位于这两个平面的交点上:直线

因此,问题被简化为使线与球相交,这很容易。


这是详细信息。输入是地球表面上的点P1 =(lat1,lon1)和P2 =(lat2,lon2),以及两个对应的半径r1和r2。

  1. 将(lat,lon)转换为(x,y,z)地心坐标。像往常一样,因为我们可以选择地球具有单位半径的度量单位,

    x = cos(lon) cos(lat)
    y = sin(lon) cos(lat)
    z = sin(lat).
    

    在此示例中,P1 =(-90.234036度,37.673442度)具有地心坐标x1 =(-0.00323306,-0.7915,0.61116)和P2 =(-90.953669度,36.109997度)具有地心坐标x2 =(-0.0134464,-0.807775 ,0.589337)。

  2. 将半径r1和r2(沿球体测量)转换为沿球体的角度。根据定义,一海里(NM)是1/60弧度(即pi / 180 * 1/60 = 0.0002908888弧度)。因此,作为角度,

    r1 = 107.5 / 60 Degree = 0.0312705 radian
    r2 = 145 / 60 Degree = 0.0421788 radian
    
  3. 围绕x1的半径r1 的测地线圆是地球表面与以cos(r1)* x1为中心的半径sin(r1)的欧几里得球的交点。

  4. 由围绕cos(r1)* x1的半径sin(r1)的球面与地球表面的交点确定的平面垂直于x1并穿过点cos(r1)x1,因此其等式为x.x1 = cos (r1)(“。”代表通常的点积);对于另一架飞机也是如此。在这两个平面的交点上将存在一个唯一点x0,这是x1和x2的线性组合。写x0 = a x1 + b * x2这两个平面方程是

    cos(r1) = x.x1 = (a*x1 + b*x2).x1 = a + b*(x2.x1)
    cos(r2) = x.x2 = (a*x1 + b*x2).x2 = a*(x1.x2) + b
    

    利用x2.x1 = x1.x2的事实(我将其写为q),解(如果存在)由下式给出

    a = (cos(r1) - cos(r2)*q) / (1 - q^2),
    b = (cos(r2) - cos(r1)*q) / (1 - q^2).
    

    在运行的示例中,我计算出a = 0.973503和b = 0.0260194。

    显然,我们需要q ^ 2!=1。这意味着x1和x2既不能是同一点,也不能是对映点。

  5. 现在,两个平面相交线上的所有其他点与x0相差一个矢量n的几倍,矢量n相互垂直于两个平面。叉积

    n = x1~Cross~x2
    

    如果n不为零,该作业是否执行:再次,这意味着x1和x2既不是重合的也不是完全相反的。(我们需要小心地计算叉积,因为当x1和x2彼此接近时,它涉及很多相减的减法。)在示例中,n =(0.0272194,-0.00631254,-0.00803124) 。

  6. 因此,我们寻找位于地球表面的x0 + t * n形式的两个点:即它们的长度等于1。等效地,它们的平方长度为1:

    1 = squared length = (x0 + t*n).(x0 + t*n) = x0.x0 + 2t*x0.n + t^2*n.n = x0.x0 + t^2*n.n
    

    x0.n的项消失了,因为x0(x1和x2的线性组合)垂直于n。两种解决方案很容易

    t = sqrt((1 - x0.x0)/n.n)
    

    及其负面影响。再次需要高精度,因为当x1和x2接近时,x0.x0 非常接近1,从而导致浮点精度下降。在示例中,t = 1.07509或t = -1.07509。因此,交点的两个点相等

    x0 + t*n = (0.0257661, -0.798332, 0.601666)
    x0 - t*n = (-0.0327606, -0.784759, 0.618935)
    
  7. 最后,我们可以通过将地心线(x,y,z)转换为地理坐标,将这些解转换回(lat,lon):

    lon = ArcTan(x,y)
    lat = ArcTan(Sqrt[x^2+y^2], z)
    

    为经度,使用广义反正切范围为返回值-180到180度(在计算应用,这一函数的两个 x和y作为参数,而不仅仅是比Y / X;它有时被称为“ATAN2”)。

    我获得了两个解决方案(-88.151426、36.989311)和(-92.390485、38.238380),在图中以黄色圆点显示。

3D图

轴显示地心(x,y,z)坐标。灰色斑块是地球表面经度-95至-87度,纬度33至40度(以1度刻度标出)的部分。地球表面已经部分透明,可以显示所有三个球体。计算出的解决方案的正确性通过黄点位于球体的交点处显而易见。


比尔,太棒了。您可以根据尝试实施的人添加一个说明。在步骤2中,您没有明确给出从度到弧度的转换。
泽西岛安迪

@Jersey谢谢您的建议编辑。我做了一些更改以避免冗余,并保持公式尽可能清晰。阅读了您所指的线程后,我还插入了一个链接来解释点积。
ub

8

椭球的情况下:

这个问题是寻找被定义为“中线”的海上边界之一的概括,关于这一主题有大量文献。我对此问题的解决方案是利用等距的方位角投影:

  1. 在交叉点猜
  2. 使用这个猜出的交点作为等距方位角投影的中心来投影两个基点,
  3. 解决二维投影空间中的相交问题。
  4. 如果新的相交点与旧的相交点太远,请返回到步骤2。

该算法二次收敛,并在椭球上产生精确的解。(对于海洋边界,要求准确性,因为它决定了捕鱼,石油和矿产的权利。)

测地线第14节给出了关于旋转椭球的公式。椭球等距方位角投影由GeographicLib提供。在椭球的大地测量投影中可以使用MATLAB版本 。


+1那是一篇了不起的论文:您在这里的谦虚描述并不能公正。
ub

还请参阅“测地线算法”在我的测地线短纸dx.doi.org/10.1007/s00190-012-0578-z(免费下载!),加上勘误和增补的这些论文geographiclib.sf.net/geod-addenda.html
cffk

1

这是一些R代码可以做到这一点:

p1 <- cbind(-90.234036, 37.673442) 
p2 <- cbind(-90.953669, 36.109997 )

library(geosphere)
steps <- seq(0, 360, 0.1)
c1 <- destPoint(p1, steps, 107.5 * 1852)
c2 <- destPoint(p2, steps, 145 * 1852)

library(raster)
s1 <- spLines(c1)
s2 <- spLines(c2)

i <- intersect(s1, s2)
coordinates(i)

#        x        y
# -92.38241 38.24267
# -88.15830 36.98740

s <- bind(s1, s2)
crs(s) <- "+proj=longlat +datum=WGS84"
plot(s)
points(i, col='red', pch=20, cex=2)

1

@whuber的答案之后,这是一些Java代码,由于两个原因,它们很有用:

  • 它突出显示了有关ArcTan的陷阱(适用于Java,也许还有其他语言?)
  • 它处理可能的极端情况,包括@whuber答案中未提及的情况。

它不是经过优化或完整的(我已经省略了明显的类,例如Point),但应该可以解决问题。

public static List<Point> intersection(EarthSurfaceCircle c1, EarthSurfaceCircle c2) {

    List<Point> intersections = new ArrayList<Point>();

    // project to (x,y,z) with unit radius
    UnitVector x1 = UnitVector.toPlanar(c1.lat, c1.lon);
    UnitVector x2 = UnitVector.toPlanar(c2.lat, c2.lon);

    // convert radii to radians:
    double r1 = c1.radius / RadiusEarth;
    double r2 = c2.radius / RadiusEarth;

    // compute the unique point x0
    double q = UnitVector.dot(x1, x2);
    double q2 = q * q;
    if (q2 == 1) {
        // no solution: circle centers are either the same or antipodal
        return intersections;
    }
    double a = (Math.cos(r1) - q * Math.cos(r2)) / (1 - q2);
    double b = (Math.cos(r2) - q * Math.cos(r1)) / (1 - q2);
    UnitVector x0 = UnitVector.add(UnitVector.scale(x1, a), UnitVector.scale(x2, b));

    // we only have a solution if x0 is within the sphere - if not,
    // the circles are not touching.
    double x02 = UnitVector.dot(x0, x0);
    if (x02 > 1) {
        // no solution: circles not touching
        return intersections;
    }

    // get the normal vector:
    UnitVector n = UnitVector.cross(x1, x2);
    double n2 = UnitVector.dot(n, n);
    if (n2 == 0) {
        // no solution: circle centers are either the same or antipodal
        return intersections;
    }

    // find intersections:
    double t = Math.sqrt((1 - UnitVector.dot(x0, x0)) / n2);
    intersections.add(UnitVector.toPolar(UnitVector.add(x0, UnitVector.scale(n, t))));
    if (t > 0) {
        // there's only multiple solutions if t > 0
        intersections.add(UnitVector.toPolar(UnitVector.add(x0, UnitVector.scale(n, -t))));
    }
    return intersections;
}

另外,重要的是,请注意-的用法与atan2@whuber答案的预期相反(我不知道为什么,但是有效):

    public static Point toPolar(UnitVector a) {
        return new Point(
                Math.toDegrees(Math.atan2(a.z, Math.sqrt(a.x * a.x + a.y * a.y))),
                Math.toDegrees(Math.atan2(a.y, a.x)));          
    }

0

@wuhber答案的有效“ R”代码。

P1 <- c(37.673442, -90.234036)
P2 <- c(36.109997, -90.953669) 

#1 NM nautical-mile is 1852 meters
R1 <- 107.5
R2 <- 145

x1 <- c(
  cos(deg2rad(P1[2])) * cos(deg2rad(P1[1])),  
  sin(deg2rad(P1[2])) * cos(deg2rad(P1[1])),
  sin(deg2rad(P1[1]))
);

x2 <- c(
  cos(deg2rad(P2[2])) * cos(deg2rad(P2[1])),
  sin(deg2rad(P2[2])) * cos(deg2rad(P2[1])),
  sin(deg2rad(P2[1]))
);

r1 = R1 * (pi/180) * (1/60)
r2 = R2 * (pi/180) * (1/60)

q = dot(x1,x2)
a = (cos(r1) - cos(r2) * q) / (1 - q^2)
b = (cos(r2) - cos(r1) * q)/ (1 - q^2)

n <- cross(x1,x2)

x0 = a*x1 + b*x2


t = sqrt((1 - dot(x0, x0))/dot(n,n))

point1 = x0 + (t * n)
point2 = x0 - (t * n)

lat1 = rad2deg(atan2(point1[2] ,point1[1]))
lon1= rad2deg(asin(point1[3]))
paste(lat1, lon1, sep=",")

lat2 = rad2deg(atan2(point2[2] ,point2[1]))
lon2 = rad2deg(asin(point2[3]))
paste(lat2, lon2, sep=",")

-1

如果一个圆是Nortstar,则存在单位球最简单的方法。

您可以使用Nortstar来衡量自己的纬度。然后,您在此球体上具有相对位置。v1(0,sin(la),cos(la))您可以从almanach知道另一颗恒星(star2)的位置(角度)。v2(sin(lo2)* cos(la2),sin(la2),cos(lo2)* cos(la2))其向量。从球面方程。

lo2是相对经度。它的未知

您与star2之间的夹角也可以测量,(m)而且您知道,两个单位向量的内积为cos(夹角)。cos(m)= dot(v1,v2)现在您可以计算相对经度(lo2)。lo2 = acos((cos(m)-sin(la)* sin(la2))/(cos(la)* cos(la2))))

毕竟,您将star2的真实经度添加到lo2中。(或子,取决于它在您的西边还是东边。)现在,lo2是您的经度。

对不起,我的英语,我从没学过这种语言。


2件事:北极星是极星。

另一个。因为相对于水平线测得的角度,总是需要90角校正。它对m角也有效。

ps:实际角度均值:星号位置-时间校正。


尚不清楚这如何回答问题。
whuber
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.