You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何判断经纬度坐标点是否位于指定多边形内(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 15:39:13