【问题标题】:Determining if a lat-long rect and a circle on a sphere overlap确定经纬度矩形和球体上的圆是否重叠
【发布时间】:2008-12-26 19:59:09
【问题描述】:

假设我有以下:

  • 由最小和最大纬度和经度定义的区域(通常是“经纬度矩形”,尽管除了某些投影外它实际上不是矩形)。
  • 由中心纬度/经度和半径定义的圆

如何判断:

  1. 这两个形状是否重叠?
  2. 圆是否完全包含在矩形内?

我正在寻找一个完整的公式/算法,而不是数学课本身。

【问题讨论】:

    标签: language-agnostic math geometry gis trigonometry


    【解决方案1】:

    警告:如果圆圈/“矩形”跨越球体的大部分,这可能会很棘手,例如:

    “矩形”:最小长=-90度,最大长=+90度,最小纬度=+70度,最大纬度=+80度

    圆:center = lat = +85deg, long = +160deg, radius = 20deg(例如,如果 A 点在圆上,C 点是圆心,O 点是球心,那么角度 AOC = 40deg )。

    这些相交但数学可能有几个案例来检查相交/包含。以下点位于上述圆上:P1=(+65deg lat,+160deg long), P2=(+75deg lat, -20deg long)。 P1 在“矩形”之外,P2 在“矩形”内,因此圆/“矩形”至少相交 2 个点。

    好的,这是我对解决方案的概述:


    令 C = 半径为 R 的圆心(如上表示为球面角)。 C 有纬度 LATC 和经度 LONGC。由于“矩形”这个词在这里有点误导(等纬度的线不是大圆的段),我将使用“边界框”这个词。

    • 函数InsideCircle(P) 返回+1,0 或-1: 如果点P 在圆内,则返回+1,如果点P 在圆上,则返回-1,如果点 P 在圆外:计算 C 和任何点 P 之间的大圆距离 D(表示为球角)将告诉您 P 是否在圆内:InsideCircle(P) = sign(R-D)(正如用户 @Die in Sente 提到的,大圆距离已在此论坛上的其他地方询问)

    • 定义PANG(x) = x 的主角 = MOD(x+180deg, 360deg)-180deg。 PANG(x) 始终介于 -180 度和 +180 度之间(含)(+180 度应映射到 -180 度)。

    • 要定义边界框,您需要知道 4 个数字,但经度存在一个小问题。 LAT1 和 LAT2 表示边界纬度(假设 LAT1

      • LAT1
      • abs(PANG(LONGP-LONGM))

    如果 PTEST = union(PCORNER,PLAT,PLONG) 中的任何以下点 P 如下所述,则圆与边界框相交,对于 InsideCircle(),不都返回相同的结果:

    • PCORNER = 边界框的 4 个角
    • 边界框两侧的点 PLAT(没有或 2 个)与圆的中心共享相同的纬度,如果 LATC 在 LAT1 和 LAT2 之间,在这种情况下,这些点具有纬度 LATC 和经度 LONG1 和 LONG2。
    • 边界框两侧的 PLONG 点(没有或 2 或 4 个!)与圆心的经度相同。这些点有 EITHER 经度 = LONGC OR 经度 PANG(LONGC-180)。如果 abs(PANG(LONGC-LONGM))

    上面列出的这些点 PLAT 和 PLONG 是边界框上离圆“最近”的点(如果不是角;我在引号中使用“最近”,在纬度/长距离和不是大圆距离),并涵盖了圆心位于边界框边界的一侧但圆上的点“溜过”边界框边界的情况。

    如果 PTEST 中的所有点 P 返回 InsideCircle(P) == +1(都在圆圈内),则圆圈包含整个边界框。

    如果 PTEST 中的所有点 P 返回 InsideCircle(P) == -1(都在圆外),则圆完全包含在边界框内。

    否则圆和边界框之间至少有一个交点。请注意,这不会计算这些点的位置,尽管如果您在 PTEST 中取任意 2 个点 P1 和 P2,其中 InsideCircle(P1) = -InsideCircle(P2),那么您可以通过二等分找到一个交点(效率低下)。 (如果 InsideCircle(P) 返回 0,那么您就有一个交点,尽管浮点数学中的相等性通常不可信。)

    可能有一种更有效的方法可以做到这一点,但上述方法应该可行。

    【讨论】:

    • 你觉得那个案子很棘手?考虑一个沙漏形状的“矩形”(由于跨越一根杆子),或者半径 > 90 度的圆。
    • 最小/最大纬度定义的矩形可以包含极点吗?听起来不像。我还假设圆圈是“小”圆圈,例如半径
    • 它也不是一个矩形。拐角之间的最短距离(纬度)不是直线。一个圆可以重叠,但不包含 4 个角中的任何一个,它也可以重叠一条直线,但不能重叠“矩形”。
    • @bruceatk:我很确定我涵盖了你提到的情况,我想我会将“矩形”重命名为“边界框”,因为我并不是要暗示它是一个真正的矩形(球体表面上不能有任何东西)。如果圆与它重叠,则集合 PLAT 或 PLONG 中的至少一个点在圆内。
    • 很好的答案,谢谢,虽然比 aib 的要复杂一些。
    【解决方案2】:

    使用Stereographic projection。所有圆圈(特别是纬度、经度和您的圆圈)都映射到平面中的圆圈(或线)。现在只是关于平面几何中的圆和线的问题(更好的是,所有经度都是通过0的线,所有纬度都是围绕0的圆)

    【讨论】:

      【解决方案3】:
      • 是的,如果方框角包含圆心。
      • 是的,如果任何方框角在圆心半径内。
      • 是的,如果该框包含圆心经度,并且最接近圆心纬度的框纬度的经度交点在圆心半径内。
      • 是的,如果框包含圆心的纬度,并且最短交点轴承上距圆心半径距离的点“超出”最近的框经度;其中最短交点方位角是通过找到从圆心到零纬度点的初始方位角和 pi/2“超过”最近的箱形经度的经度来确定的。
      • 否,否则。

      假设:

      • 您可以找到从 A 点到 B 点的最小航向的初始方位。
      • 您可以找到两点之间的距离。

      第一次检查是微不足道的。第二次检查只需要找到四个距离。第三个检查只需要找到从圆心到(最近框纬度,圆心经度)的距离。

      第四次检查需要找到最接近圆心的边界框的经线。然后找到距离圆心最远的经线所在的大圆的中心。找到从圆心到大圆心的初始方位。从该轴承上的圆心找到点圆半径。如果该点位于距离圆心最近的经线的另一侧,则圆和边界框在该侧相交。

      在我看来,这应该是有缺陷的,但我一直没能找到它。

      我似乎无法解决的真正问题是找到完美包含圆的边界框(对于不包含极点的圆)。最小/最大纬度的方位似乎是圆心纬度和圆半径/(球体周长/4)的函数。在赤道附近,它下降到 pi/2(东)或 3*pi/2(西)。随着中心接近极点,半径接近球周长/4,方位接近零(北)或pi(南)。

      【讨论】:

        【解决方案4】:

        这个怎么样?

        找到将矩形中心点Cr 连接到圆心的向量v。找到点i,其中v 与矩形相交。如果||i-Cr|| + r > ||v|| 则它们相交。

        换句话说,矩形内线段的长度加上圆内线段的长度应该大于总长度(v,中心连接线段)。

        寻找点i 应该是棘手的部分,特别是如果它落在经度边缘,但你应该能够比我更快地想出一些东西。

        编辑:此方法无法判断圆是否完全在矩形内。为此,您需要找到从其中心到矩形所有四个边缘的距离。

        编辑:以上内容不正确。 在某些情况下,正如 Federico Ramponi 所建议的那样,即使在欧几里得几何中它也不起作用。我会发布另一个答案。请不接受这一点,并随时投反对票。我会尽快删除它。

        【讨论】:

        • 这是一个很好的方法。这个理论看起来很合理,但数学仍然非常棘手,因为“矩形”实际上并不是矩形,并且矢量将位于球面上。我在这个问题上的第一个 +1,在一系列非常错误的信息 -1 之后。
        • 不是我希望的完整算法,但肯定是一个好的开始,谢谢!
        • 注意等式中的不等号 ,否则你的断言是错误的。
        • 但是,如果您的测试(使用 >)失败,则不能说没有发生交集。你不能在欧几里得平面上,“显然”你也不能在球面上。
        • 例如,画一个左下角(0,0)和右上角(2,8)的矩形,以(3,8)为圆心,半径为2 . 测试(带>)失败,但两个数字相交。
        【解决方案5】:

        这应该适用于地球上的任何点。如果您想将其更改为不同大小的球体,只需将 kEarchRadiusKms 更改为您想要的球体半径即可。

        此方法用于计算纬度和经度点之间的距离。

        我从这里得到了这个距离公式: http://www.codeproject.com/csharp/distancebetweenlocations.asp

        public static double Calc(double Lat1, double Long1, double Lat2, double Long2)
        {
            double dDistance = Double.MinValue;
            double dLat1InRad = Lat1 * (Math.PI / 180.0);
            double dLong1InRad = Long1 * (Math.PI / 180.0);
            double dLat2InRad = Lat2 * (Math.PI / 180.0);
            double dLong2InRad = Long2 * (Math.PI / 180.0);
        
            double dLongitude = dLong2InRad - dLong1InRad;
            double dLatitude = dLat2InRad - dLat1InRad;
        
            // Intermediate result a.
            double a = Math.Pow(Math.Sin(dLatitude / 2.0), 2.0) +
                       Math.Cos(dLat1InRad) * Math.Cos(dLat2InRad) *
                       Math.Pow(Math.Sin(dLongitude / 2.0), 2.0);
        
            // Intermediate result c (great circle distance in Radians).
            double c = 2.0 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1.0 - a));
        
            // Distance.
            // const Double kEarthRadiusMiles = 3956.0;
            const Double kEarthRadiusKms = 6376.5;
            dDistance = kEarthRadiusKms * c;
        
            return dDistance;
        }
        

        如果矩形任意顶点之间的距离小于圆半径的距离,则圆和矩形重叠。如果圆心和所有顶点之间的距离大于圆的半径,并且所有这些距离都小于矩形的宽度和高度,那么圆应该在矩形的内部。

        如果您发现我的代码有问题,请随时更正我的代码,因为我确信存在一些我没有想到的情况。

        此外,我不确定这是否适用于跨越半球末端的矩形,因为距离方程可能会失效。

        public string Test(double cLat,
            double cLon,
            double cRadius,
            double rlat1,
            double rlon1,
            double rlat2,
            double rlon2,
            double rlat3,
            double rlon3,
            double rlat4,
            double rlon4)
        {
            double d1 = Calc(cLat, cLon, rlat1, rlon1);
            double d2 = Calc(cLat, cLon, rlat2, rlon2);
            double d3 = Calc(cLat, cLon, rlat3, rlon3);
            double d4 = Calc(cLat, cLon, rlat4, rlon4);
        
            if (d1 <= cRadius ||
                d2 <= cRadius ||
                d3 <= cRadius ||
                d4 <= cRadius)
            {
        
                return "Circle and Rectangle intersect...";
            }
        
            double width = Calc(rlat1, rlon1, rlat2, rlon2);
            double height = Calc(rlat1, rlon1, rlat4, rlon4);
        
            if (d1 >= cRadius &&
                d2 >= cRadius &&
                d3 >= cRadius &&
                d4 >= cRadius &&
                width >= d1 &&
                width >= d2 &&
                width >= d3 &&
                width >= d4 &&
                height >= d1 &&
                height >= d2 &&
                height >= d3 &&
                height >= d4)
            {
                return "Circle is Inside of Rectangle!";
            }
        
        
        
            return "NO!";
        }
        

        【讨论】:

        • 不错的代码,但是当我和许多其他人第一次阅读这个问题时,你已经陷入了同样的陷阱。彻底阅读其他答案和 cmets。如果所有四个角都在圆外,那么在里面的边上仍然可能有点!
        【解决方案6】:

        再试一次...

        我认为解决方案是测试一组点,正如 Jason S 建议的那样,但我不同意他选择的点,我认为这在数学上是错误的。

        您需要在纬度/经度框两侧找到与圆心的距离为局部最小值或最大值的点。将这些点添加到角集合中,然后上面的算法应该是正确的。

        即,让经度为 x 维度,纬度为 y 维度,让每个 框的一侧是参数曲线 P(t) = P0 + t (P1-P0) o

        令 f(P) = f(P.x, P.y) 为到圆心的距离。

        那么 f (P0 + t (P1-P0)) 是 t 的距离函数:g(t)。找出距离函数的导数为零的所有点:g'(t) == 0。(当然,丢弃的解决方案超出域 0

        不幸的是,这需要找到一个超越表达式的零,所以没有封闭形式的解决方案。这类方程只能通过 Newton-Raphson 迭代求解。

        好的,我知道你想要的是代码,而不是数学。但我只有数学。

        【讨论】:

        • 啊哈,我承认了。 (我认为。:) 双重问题(找到圆的极值 lat/long 点并测试那些 + 中心以查看哪些在边界框内)可能更容易,但也有超验的废话。
        【解决方案7】:

        有关欧几里得几何的答案,请参阅:Circle-Rectangle collision detection (intersection)

        【讨论】:

          猜你喜欢
          • 2010-10-11
          • 1970-01-01
          • 2021-09-02
          • 2012-01-01
          • 2020-03-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-09-24
          相关资源
          最近更新 更多