• C# 判断经纬度是否在区域内,是否偏离线路


    最近在做 经纬度点 是否在区域内、是否偏离既定线路功能,有些地图也提供了相应的功能,但是要掉接口

    速度就会慢,由于数据量比较大,在网上找了一些代码,整理测试一下,还是比较准确,在这分享给大家。

    主要原理:

    判断点是否在区域内(光线投射算法):

    求解从该点向右发出的水平线射线与多边形各边的交点,当交点数为奇数,则在内部。

    不过要注意几种特殊情况:1、点在边或者顶点上;2、点在边的延长线上;3、点出发的水平射线与多边形相交在顶点上

    判是否偏离线路:

    求点到直线的垂足,获取最小值判断是否超过预设的距离。

    1.公式
    设直线方程为ax+by+c=0,点坐标为(m,n)
    则垂足为((b*b*m-a*b*n-a*c)/(a*a+b*b),(a*a*n-a*b*m-b*c)/(a*a+b*b))
    2.计算点到线段的最近点

    如果该线段平行于X轴(Y轴),则过点point作该线段所在直线的垂线,垂足很容
    易求得,然后计算出垂足,如果垂足在线段上则返回垂足,否则返回离垂足近的端
    点;

    如果该线段不平行于X轴也不平行于Y轴,则斜率存在且不为0。设线段的两端点为
    pt1和pt2,斜率为:
    k = ( pt2.y - pt1. y ) / (pt2.x - pt1.x );
    该直线方程为:
    y = k* ( x - pt1.x) + pt1.y
    其垂线的斜率为 - 1 / k,
    垂线方程为:
    y = (-1/k) * (x - point.x) + point.y
    联立两直线方程解得:
    x = ( k^2 * pt1.x + k * (point.y - pt1.y ) + point.x ) / ( k^2 + 1)
    y = k * ( x - pt1.x) + pt1.y;

    原文链接:https://blog.csdn.net/ffgcc/article/details/80033038

    直接上代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Commons
    {
    
        /// <summary>
        /// 地图计算帮助类
        /// </summary>
        public class MapHelper
        {
            /// <summary>
            /// 地球半径,单位 m
            /// </summary>
            private static double EARTH_RADIUS = 6378137.0;
    
    
            /// <summary>
            /// 判断点是否在多边形内或多边形上
            /// </summary>
            /// <param name="point">坐标点</param>
            /// <param name="Points">多边形边界点集合</param>
            /// <returns></returns>
            public static bool IsPtInPoly(MapPoint point, MapPoint[] Points)
            {
                double ALon= point.x, ALat= point.y;
                int iSum, iCount, iIndex;
                double dLon1 = 0, dLon2 = 0, dLat1 = 0, dLat2 = 0, dLon;
                if (Points.Length < 3)
                {
                    return false;
                }
                iSum = 0;
                iCount = Points.Length;
                for (iIndex = 0; iIndex < iCount; iIndex++)
                {
                    if (ALon == Points[iIndex].x && ALat == Points[iIndex].y)  //A点在多边形上    
                        return true;
    
                    if (iIndex == iCount - 1)
                    {
                        dLon1 = Points[iIndex].x;
                        dLat1 = Points[iIndex].y;
                        dLon2 = Points[0].x;
                        dLat2 = Points[0].y;
                    }
                    else
                    {
                        dLon1 = Points[iIndex].x;
                        dLat1 = Points[iIndex].y;
                        dLon2 = Points[iIndex + 1].x;
                        dLat2 = Points[iIndex + 1].y;
                    }
    
                    //以下语句判断A点是否在边的两端点的纬度之间,在则可能有交点
                    if (((ALat > dLat1) && (ALat < dLat2)) || ((ALat > dLat2) && (ALat < dLat1)))
                    {
                        if (Math.Abs(dLat1 - dLat2) > 0)
                        {
                            //获取A点向左射线与边的交点的x坐标:
                            dLon = dLon1 - ((dLon1 - dLon2) * (dLat1 - ALat)) / (dLat1 - dLat2);
                            //如果交点在A点左侧,则射线与边的全部交点数加一:
                            if (dLon < ALon)
                            {
                                iSum++;
                            }
                            //如果相等,则说明A点在边上
                            if (dLon == ALon)
                                return true;
                        }
                    }
                }
                if ((iSum % 2) != 0)
                {
                    return true;
                }
                return false;
            }
    
    
            /// <summary>
            /// 判断是否偏离航线在允许范围内
            /// </summary>
            /// <param name="point">实时点用于判断此点是否偏离航线</param>
            /// <param name="points">航线组成的点坐标</param>
            /// <param name="allowRange">允许偏离航线的距离 单位:m</param>
            /// <returns>true -- 未偏离航线 ; false -- 偏离航线</returns>
            public static bool PointToPintLine(MapPoint point, List<MapPoint> points, double allowRange)
            {
                double minDistance = -1;
    
                for (int i = 0; i < points.Count - 1; i++)
                {
                    if (points[i].x== points[i+1].x&& points[i].y== points[i+1].y)
                    {
                        continue;
                    }
                    /**
                     * 获取线段的x取值范围和Y的取值范围
                     */
                    double[] rangeX = new double[2];
                    double[] rangeY = new double[2];
                    if (points[i].x > points[i + 1].x)
                    {
                        rangeX[0] = points[i + 1].x;
                        rangeX[1] = points[i].x;
                    }
                    else
                    {
                        rangeX[0] = points[i].x;
                        rangeX[1] = points[i + 1].x;
                    }
    
                    if (points[i].y > points[i + 1].y)
                    {
                        rangeY[0] = points[i + 1].y;
                        rangeY[1] = points[i].y;
                    }
                    else
                    {
                        rangeY[0] = points[i].y;
                        rangeY[1] = points[i + 1].y;
                    }
    
                    /**
                     * 根据两点求出直线方程AX+BY+C=0中,A B C 的值
                     */
                    double a = points[i + 1].y - points[i].y;
                    double b = points[i].x - points[i + 1].x;
                    double c = points[i + 1].x * points[i].y - points[i].x * points[i + 1].y;
    
                    /**
                     * 求点到直线的垂足以及距离
                     */
                    //得到垂足点
                    MapPoint foot = getFootOfPerpendicular(point.x, point.y, a, b, c);
                    //得到距离
                    double distance = getDistance(point.x, point.y, foot.x, foot.y);
    
                    /**
                     * 判断垂足是否在线段上
                     */
                    if (foot.x >= rangeX[0] && foot.x <= rangeX[1] &&
                            foot.y >= rangeY[0] && foot.y <= rangeY[1])
                    {
                        /**
                         * 1.如果在线段上则记录值
                         * 2.跟minDistance比较,如果小于目前值则进行替换(若是初始值(-1)也进行替换)
                         */
                        if ((minDistance == -1) || (minDistance != -1 && distance < minDistance))
                        {
                            minDistance = distance;
                        }
                    }
                }
                /**
                 * 1.看是否minDistance是否是初始值,
                 * 2.如果是初始值则再次计算点到首末两点的距离,若均大于allowRange则认为偏离航线
                 * 3.如果不是初始值则判断最小值是否小于allowRange
                 */
                if (minDistance == -1)
                {
                    /**
                     * 计算点到首末两点的距离
                     */
                    MapPoint startPoint = points[0];
                    MapPoint endPoint = points[points.Count - 1];
    
                    double startPointDistance = getDistance(point.x, point.y, startPoint.x, startPoint.y);
                    double endPointDistance = getDistance(point.x, point.y, endPoint.x, endPoint.y);
                    double distance = (startPointDistance <= endPointDistance ? startPointDistance : endPointDistance);
                    minDistance = distance;
                }
                if (minDistance <= allowRange)
                {
                    return true;
                }
                else
                {
                    return false;
                }
    
            }
    
            /**
             * Description 求点到直线的垂足
             *
             * @param x1
             *            点横坐标
             * @param y1
             *            点纵坐标
             * @param A
             *            直线方程一般式系数A
             * @param B
             *            直线方程一般式系数B
             * @param C
             *            直线方程一般式系数C
             * @return 垂足点
             */
            private static MapPoint getFootOfPerpendicular(double x1, double y1, double A, double B, double C)
            {
                if (A * A + B * B < 1e-13)
                    return null;
    
                if (Math.Abs(A * x1 + B * y1 + C) < 1e-13)
                {
                    return new MapPoint(x1, y1);
                }
                else
                {
                    double newX = (B * B * x1 - A * B * y1 - A * C) / (A * A + B * B);
                    double newY = (-A * B * x1 + A * A * y1 - B * C) / (A * A + B * B);
                    return new MapPoint(newX, newY);
                }
            }
    
            /**
             * 根据经纬度,计算两点间的距离
             *
             * @param longitude1 第一个点的经度
             * @param latitude1  第一个点的纬度
             * @param longitude2 第二个点的经度
             * @param latitude2  第二个点的纬度
             * @return 返回距离 单位 米
             */
            public static double getDistance(double longitude1, double latitude1, double longitude2, double latitude2)
            {
                // 纬度
                double lat1 = ToRadians(latitude1);
                double lat2 = ToRadians(latitude2);
                // 经度
                double lng1 = ToRadians(longitude1);
                double lng2 = ToRadians(longitude2);
                // 纬度之差
                double a = lat1 - lat2;
                // 经度之差
                double b = lng1 - lng2;
                // 计算两点距离的公式
                double s = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a / 2), 2) +
                        Math.Cos(lat1) * Math.Cos(lat2) * Math.Pow(Math.Sin(b / 2), 2)));
                // 弧长乘地球半径, 返回单位: 千米
                s = s * EARTH_RADIUS;
                return s;
            }
    
            /// <summary>
            /// 角度转弧度
            /// </summary>
            /// <param name="degrees"></param>
            /// <returns></returns>
            public static double ToRadians(double degrees)
            {
                double radians = (Math.PI / 180) * degrees;
                return (radians);
            }
    
        }
        /// <summary>
        /// 地图点
        /// </summary>
        public class MapPoint
        {
            /// <summary>
            /// 经度
            /// </summary>
            public double x { get; set; }
            /// <summary>
            /// 纬度
            /// </summary>
            public double y { get; set; }
            /// <summary>
            /// 地图点
            /// </summary>
            /// <param name="alon">经度</param>
            /// <param name="alat">纬度</param>
            public MapPoint(double alon, double alat)
            {
                this.x = alon;
                this.y = alat;
            }
    
            public MapPoint(object alon, object alat)
            {
                this.x = Convert.ToDouble(alon);
                this.y = Convert.ToDouble(alat);
            }
        }
    }

    测试:

    [TestMethod()]
            public void IsPtInPolyTest()
            {
                //判断是否在多边形内
                //MapPoint[] ps = new MapPoint[] { new MapPoint(120.2043, 30.2795), new MapPoint(120.2030, 30.2511), new MapPoint(120.1810, 30.2543), new MapPoint(120.1798, 30.2781), new MapPoint(120.1926, 30.2752) };
                //MapPoint n1 = new MapPoint(120.1936, 30.2846);
                //MapPoint n2 = new MapPoint(120.1823, 30.2863);
                //MapPoint n3 = new MapPoint(120.2189, 30.2712);
                //MapPoint y1 = new MapPoint(120.1902, 30.2712);
                //MapPoint y2 = new MapPoint(120.1866, 30.2672);
                //MapPoint y4 = new MapPoint(120.1869, 30.2718);
                //bool aa = MapHelper.IsPtInPoly(120.2043, 30.2795, ps);
                bool aa = false;
                MapPoint[] ps1 = new MapPoint[] { new MapPoint(30.59119542666749, 104.050569540718), new MapPoint(30.588443179070143, 104.05052662537724), new MapPoint(30.588664839488466, 104.0601933058818), new MapPoint(30.5905027542628, 104.0577471314589), new MapPoint(30.59129701820391, 104.05441046371537) };
                MapPoint n22 = new MapPoint(30.589948612774652, 104.05258656173338);
                MapPoint n221 = new MapPoint(30.59234063418448, 104.05193210278689);
                //在区域内
                aa = MapHelper.IsPtInPoly(new MapPoint(30.589948612774652, 104.05258656173338), ps1);
                //不在区域内
                aa = MapHelper.IsPtInPoly(new MapPoint(30.59234063418448, 104.05193210278689), ps1);
            }
        [TestMethod()]
            public void pointToPintLineTest()
            {
                //判断是否偏离线路
                MapPoint[] ps1 = new MapPoint[] {
                    new MapPoint(30.585548284620927, 104.05539820326156),
                    new MapPoint(30.58535432554657, 104.05175039929757),
                    new MapPoint(30.585151129909544, 104.04671857559433),
                    new MapPoint(30.58310991374495, 104.04661128724243),
                    new MapPoint(30.581114837410443, 104.046643473748),
                    //new MapPoint(30.58206815224024, 104.04642201916893),
                    //new MapPoint(30.579241379387206, 104.04642205588303),
                    //new MapPoint(30.576698720819568, 104.04636078755512),
                };
                //在线上
               bool a= MapHelper.PointToPintLine(new MapPoint(30.58398736487427, 104.04660055840725), ps1.ToList(), 10);
                //不在
                a = MapHelper.PointToPintLine(new MapPoint(30.58228683361441, 104.05176744705074), ps1.ToList(), 10);
            }
  • 相关阅读:
    第2层交换和生成树协议(STP)__散知识点
    OSPF
    EIGRP和OSPF__EIGRP
    EIGRP和OSPF__邻居发现
    IP路由__距离矢量路由选择协议
    IP路由__动态路由
    IP路由__静态路由
    IP路由__IP路由选择过程
    Cisco的互联网络操作系统IOS和安全设备管理器SDM__CDP
    Cisco的互联网络操作系统IOS和安全设备管理器SDM__备份和恢复Cisco 配置
  • 原文地址:https://www.cnblogs.com/piaoxuewuming/p/16317390.html
Copyright © 2020-2023  润新知