【发布时间】:2008-12-26 19:59:09
【问题描述】:
假设我有以下:
- 由最小和最大纬度和经度定义的区域(通常是“经纬度矩形”,尽管除了某些投影外它实际上不是矩形)。
- 由中心纬度/经度和半径定义的圆
如何判断:
- 这两个形状是否重叠?
- 圆是否完全包含在矩形内?
我正在寻找一个完整的公式/算法,而不是数学课本身。
【问题讨论】:
标签: language-agnostic math geometry gis trigonometry
假设我有以下:
如何判断:
我正在寻找一个完整的公式/算法,而不是数学课本身。
【问题讨论】:
标签: language-agnostic math geometry gis trigonometry
警告:如果圆圈/“矩形”跨越球体的大部分,这可能会很棘手,例如:
“矩形”:最小长=-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
如果 PTEST = union(PCORNER,PLAT,PLONG) 中的任何以下点 P 如下所述,则圆与边界框相交,对于 InsideCircle(),不都返回相同的结果:
上面列出的这些点 PLAT 和 PLONG 是边界框上离圆“最近”的点(如果不是角;我在引号中使用“最近”,在纬度/长距离和不是大圆距离),并涵盖了圆心位于边界框边界的一侧但圆上的点“溜过”边界框边界的情况。
如果 PTEST 中的所有点 P 返回 InsideCircle(P) == +1(都在圆圈内),则圆圈包含整个边界框。
如果 PTEST 中的所有点 P 返回 InsideCircle(P) == -1(都在圆外),则圆完全包含在边界框内。
否则圆和边界框之间至少有一个交点。请注意,这不会计算这些点的位置,尽管如果您在 PTEST 中取任意 2 个点 P1 和 P2,其中 InsideCircle(P1) = -InsideCircle(P2),那么您可以通过二等分找到一个交点(效率低下)。 (如果 InsideCircle(P) 返回 0,那么您就有一个交点,尽管浮点数学中的相等性通常不可信。)
可能有一种更有效的方法可以做到这一点,但上述方法应该可行。
【讨论】:
使用Stereographic projection。所有圆圈(特别是纬度、经度和您的圆圈)都映射到平面中的圆圈(或线)。现在只是关于平面几何中的圆和线的问题(更好的是,所有经度都是通过0的线,所有纬度都是围绕0的圆)
【讨论】:
假设:
第一次检查是微不足道的。第二次检查只需要找到四个距离。第三个检查只需要找到从圆心到(最近框纬度,圆心经度)的距离。
第四次检查需要找到最接近圆心的边界框的经线。然后找到距离圆心最远的经线所在的大圆的中心。找到从圆心到大圆心的初始方位。从该轴承上的圆心找到点圆半径。如果该点位于距离圆心最近的经线的另一侧,则圆和边界框在该侧相交。
在我看来,这应该是有缺陷的,但我一直没能找到它。
我似乎无法解决的真正问题是找到完美包含圆的边界框(对于不包含极点的圆)。最小/最大纬度的方位似乎是圆心纬度和圆半径/(球体周长/4)的函数。在赤道附近,它下降到 pi/2(东)或 3*pi/2(西)。随着中心接近极点,半径接近球周长/4,方位接近零(北)或pi(南)。
【讨论】:
这个怎么样?
找到将矩形中心点Cr 连接到圆心的向量v。找到点i,其中v 与矩形相交。如果||i-Cr|| + r > ||v|| 则它们相交。
换句话说,矩形内线段的长度加上圆内线段的长度应该大于总长度(v,中心连接线段)。
寻找点i 应该是棘手的部分,特别是如果它落在经度边缘,但你应该能够比我更快地想出一些东西。
编辑:此方法无法判断圆是否完全在矩形内。为此,您需要找到从其中心到矩形所有四个边缘的距离。
编辑:以上内容不正确。 在某些情况下,正如 Federico Ramponi 所建议的那样,即使在欧几里得几何中它也不起作用。我会发布另一个答案。请不接受这一点,并随时投反对票。我会尽快删除它。
【讨论】:
这应该适用于地球上的任何点。如果您想将其更改为不同大小的球体,只需将 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!";
}
【讨论】:
再试一次...
我认为解决方案是测试一组点,正如 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 迭代求解。
好的,我知道你想要的是代码,而不是数学。但我只有数学。
【讨论】:
有关欧几里得几何的答案,请参阅:Circle-Rectangle collision detection (intersection)
【讨论】: