【问题标题】:Checking if a longitude/latitude coordinate resides inside a complex polygon in an embedded device?检查经度/纬度坐标是否位于嵌入式设备的复杂多边形内?
【发布时间】:2012-12-06 16:02:04
【问题描述】:

我需要用户能够在地图上绘制一个复杂的多边形,然后让应用程序检查给定的经度/纬度是否位于该多边形内。

我只能找到使用不补偿地球曲率的简单 x/y 笛卡尔坐标系的算法。

用户在 PC 上绘制多边形,其中点通过无线电传输到嵌入式设备,然后需要检查给定多边形是否位于其当前位置(从 GPS 获取)。

由于这是针对嵌入式设备,我无法使用大型库,而是需要算法自己执行检查或使用非常小的库。但我似乎找不到任何这样的算法。

【问题讨论】:

  • 你在看什么样的距离?我经常做这种工作,如果您谈论的是相对较小的多边形,例如
  • 请注意,如果您正在使用 GIS,您可能会喜欢这个 SO 姐妹网站:gis.stackexchange.com
  • @PeterJ 我希望在非常宽的范围(数百公里)上使用它,但仍然需要非常接近的精度,因为它也将在小于 1 公里的分段区域内使用
  • 很公平,但请记住,除非段相距很远,否则不会有任何影响。例如,一条 200 公里的高速公路有一系列相隔几公里的路段,不会有太大的不同。只有当你说在 100 公里长的多边形上有一条边时,它才会真正产生影响,这在我的经验中是非常罕见的。大多数现实生活中的事情,比如说城市边界在实践中并不是那么直截了当,所以值得考虑一下可能的错误,如果你真的需要它。在高速公路上,如果您真的需要这种精度,您可能还需要一些航位推算。

标签: algorithm math embedded geometry gis


【解决方案1】:

这是我用 C# 为包含顶点列表的 Polygon 类编写的实现。它不考虑地球的曲率。相反,您会在运行之前将多边形预处理成更小的部分。

这个算法的性能非常好。即使对于具有数千条边的多边形,它在我的桌面上也只需大约一两毫秒即可完成。

代码已经优化了不少,所以不像伪代码那样可读。

public bool Contains(GeoLocation location)
{
    if (!Bounds.Contains(location))
        return false;

    var lastPoint = _vertices[_vertices.Length - 1];
    var isInside = false;
    var x = location.Longitude;
    foreach (var point in _vertices)
    {
        var x1 = lastPoint.Longitude;
        var x2 = point.Longitude;
        var dx = x2 - x1;

        if (Math.Abs(dx) > 180.0)
        {
            // we have, most likely, just jumped the dateline (could do further validation to this effect if needed).  normalise the numbers.
            if (x > 0)
            {
                while (x1 < 0)
                    x1 += 360;
                while (x2 < 0)
                    x2 += 360;
            }
            else
            {
                while (x1 > 0)
                    x1 -= 360;
                while (x2 > 0)
                    x2 -= 360;
            }
            dx = x2 - x1;
        }

        if ((x1 <= x && x2 > x) || (x1 >= x && x2 < x))
        {
            var grad = (point.Latitude - lastPoint.Latitude) / dx;
            var intersectAtLat = lastPoint.Latitude + ((x - x1) * grad);

            if (intersectAtLat > location.Latitude)
                isInside = !isInside;
        }
        lastPoint = point;
    }

    return isInside;
}

基本思想是找到跨越您要测试的点的“x”位置的多边形的所有边。然后你会发现它们中有多少与在你的点上方延伸的垂直线相交。如果偶数在该点上方交叉,那么您就在多边形之外。如果上面有一个奇数交叉,那么你就在里面。

【讨论】:

  • 感谢您的回复!使用这个算法你能达到什么样的精度?考虑到它不能补偿地球的曲率,它是否只适用于半小距离,或者即使对于较大的多边形(比如 50 公里左右)也能正常工作?
  • @ChewToy,精度随距离而变化。如果您需要更高的精度,请找到球体上两个相邻顶点的中点,然后插入一个新顶点。重复执行此操作,直到多边形在您要求的公差范围内。或者,修改算法以考虑地球曲率的方式执行相交测试。
  • 非常感谢算法,条件 Bounds.Contains(location) 有什么作用?
  • @VladimirGhetau 这是一个可选的优化。如果您有一个矩形边界框,其中包含多边形中所有点的范围,那么在遍历所有顶点之前对框进行快速测试可能会更快。如果该点不在边界内,则它不能在多边形内。
  • 嗨 Drew,这个算法会告诉你一个随机顶点是否在多边形内。我试图通过删除任何 >= 和 和
【解决方案2】:

很好的解释和简单的 C 代码,您可以根据需要进行转换

http://alienryderflex.com/polygon/

如果您有许多不重叠的多边形,请将多边形检查与 RTree 结合起来,以便快速剔除搜索树。

【讨论】:

    猜你喜欢
    • 2017-08-08
    • 2018-08-27
    • 2015-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多