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

1.公式

2.计算点到线段的最近点

pt1和pt2，斜率为：
k = ( pt2.y - pt1. y ) / (pt2.x - pt1.x );

y = k* ( x - pt1.x) + pt1.y

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;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

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;
}
}
else {
//计算点距离
double startPointDistance = getDistance(point.x, point.y, points[i].x, points[i].y);
double endPointDistance = getDistance(point.x, point.y, points[i+1].x, points[i + 1].y);
distance = (startPointDistance <= endPointDistance ? startPointDistance : endPointDistance);
if (minDistance==-1||minDistance > distance)
{
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 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)));
// 弧长乘地球半径, 返回单位: 千米
return s;
}

/// <summary>
/// 角度转弧度
/// </summary>
/// <param name="degrees"></param>
/// <returns></returns>
{
double radians = (Math.PI / 180) * degrees;
}

}
/// <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);
}

posted @ 2022-05-27 14:02  逍遥子_何  阅读(745)  评论(0编辑  收藏  举报