如何判断经纬度坐标点是否位于指定多边形内(C#实现)
C# 基于射线投射法实现点与多边形位置关系判断
射线投射法核心逻辑:从待测点向水平右侧发射射线,统计射线与多边形各边的交点数量,交点为奇数则点在多边形内部,偶数(含0)则在外部;实现中额外处理了点落在线段上、浮点计算精度误差的边界场景。
基础数据结构定义
先定义通用坐标点结构,可直接承载投影后的平面坐标、十进制度经纬度:
public struct GeoPoint { /// <summary> /// 横坐标(对应经度/投影坐标系X值) /// </summary> public double X { get; } /// <summary> /// 纵坐标(对应纬度/投影坐标系Y值) /// </summary> public double Y { get; } public GeoPoint(double x, double y) { X = x; Y = y; } }
核心判断实现
public static class PolygonPositionChecker { private const double Epsilon = 1e-8; /// <summary> /// 判断点是否在多边形内部/边上 /// </summary> /// <param name="testPoint">待测点</param> /// <param name="polygonVertices">按顺时针/逆时针顺序排列的多边形顶点,无需首尾重复闭合</param> /// <returns>true=内部/边上,false=外部</returns> /// <exception cref="ArgumentException">多边形顶点数不足3个时抛出</exception> public static bool IsPointInPolygon(GeoPoint testPoint, IReadOnlyList<GeoPoint> polygonVertices) { int vertexCount = polygonVertices.Count; if (vertexCount < 3) { throw new ArgumentException("有效多边形至少需要3个顶点", nameof(polygonVertices)); } bool insideFlag = false; // 遍历多边形所有边:j为边的起点,i为边的终点,最后一条边连接末顶点和首顶点闭合 for (int i = 0, j = vertexCount - 1; i < vertexCount; j = i++) { GeoPoint p1 = polygonVertices[i]; GeoPoint p2 = polygonVertices[j]; // 点在当前边上,直接判定为内部 if (IsPointOnSegment(testPoint, p1, p2)) { return true; } // 判断边的两个端点是否在待测点水平射线的两侧,同侧则无交点 bool p1AboveRay = p1.Y > testPoint.Y; bool p2AboveRay = p2.Y > testPoint.Y; if (p1AboveRay == p2AboveRay) { continue; } // 计算射线与当前边的交点横坐标 double intersectX = (testPoint.Y - p1.Y) * (p2.X - p1.X) / (p2.Y - p1.Y) + p1.X; // 交点在待测点右侧,翻转内部标记 if (testPoint.X < intersectX - Epsilon) { insideFlag = !insideFlag; } } return insideFlag; } /// <summary> /// 辅助方法:判断点是否落在线段上 /// </summary> private static bool IsPointOnSegment(GeoPoint p, GeoPoint segStart, GeoPoint segEnd) { // 三点共线校验:叉积为0 double cross = (p.X - segStart.X) * (segEnd.Y - segStart.Y) - (p.Y - segStart.Y) * (segEnd.X - segStart.X); if (Math.Abs(cross) > Epsilon) { return false; } // 点坐标在段的包围盒范围内 bool xInRange = p.X >= Math.Min(segStart.X, segEnd.X) - Epsilon && p.X <= Math.Max(segStart.X, segEnd.X) + Epsilon; bool yInRange = p.Y >= Math.Min(segStart.Y, segEnd.Y) - Epsilon && p.Y <= Math.Max(segStart.Y, segEnd.Y) + Epsilon; return xInRange && yInRange; } }
给定测试用例调用示例
你提供的输入参数如下:
- 待测点:X=
16269479,Y=58606014 - 多边形顶点共10个,按给定POLYGON顺序解析
// 初始化待测点 var testAddressPoint = new GeoPoint(16269479, 58606014); // 初始化多边形顶点 var targetPolygon = new List<GeoPoint> { new GeoPoint(16270489, 58824802), new GeoPoint(16271613, 58822105), new GeoPoint(16287958, 58802519), new GeoPoint(16297720, 58795480), new GeoPoint(16306633, 58792276), new GeoPoint(16309717, 58790244), new GeoPoint(16307960, 58785942), new GeoPoint(16296537, 58775348), new GeoPoint(16303836, 58769601), new GeoPoint(16305650, 58769594) }; // 执行判断 bool isInPolygon = PolygonPositionChecker.IsPointInPolygon(testAddressPoint, targetPolygon);
运行结果为false,即你提供的地址坐标在目标多边形外部。
说明:你给出的坐标为整数型米制投影坐标,上述平面计算逻辑可直接使用无精度损失;如果后续使用十进制度球面经纬度做小范围(城市级及以下)判断,该实现误差可忽略,跨国家/大洲的大范围判断建议先将坐标转为平面投影后再计算。
内容的提问来源于stack exchange,提问作者learner
相关产品推荐
相关产品推荐

