/// <summary> /// 判断点在围栏内(暴力遍历法) /// <para> /// 计算原理 /// </para> /// <para> /// 地理围栏一般是多边形,判断点在多边形内部可以通过射线法来判断点是否在多边形内部。从一点出发沿着X轴画一条射线,依次判断该射线与每条边的交点,并统计交点个数,如果交点数为奇数,则在多边形内部,如果焦点数是偶数,则在外部,射线法对凸和非凸多边形都适用,复杂度为O(N),其它N是边数。 /// 当地理围栏多边形数目较少时,我们可以依次遍历每一个多边形(暴力遍历法),然后用射线法进行判断,这样效率也很高。而当多边形数目较多时,比如有10万个多边形,这个时候需要执行10万次射线法,响应时间达到3.9秒。 /// </para> /// </summary> /// <param name="point"></param> /// <param name="pointRange"></param> /// <returns></returns> private static bool isPointInPolygon(Util.LBS.LBSPoint point, List <Util.LBS.LBSPoint> pointRange) { int iSum = 0, iCount; double dLng1, dLng2, dLat1, dLat2, dLng; double ALat = point.Lat; double ALon = point.Lng; if (pointRange.Count < 3) { return(false); } iCount = pointRange.Count; for (int i = 0; i < iCount; i++) { if (i == iCount - 1) { dLng1 = pointRange[i].Lng; dLat1 = pointRange[i].Lat; dLng2 = pointRange[0].Lng; dLat2 = pointRange[0].Lat; } else { dLng1 = pointRange[i].Lng; dLat1 = pointRange[i].Lat; dLng2 = pointRange[i + 1].Lng; dLat2 = pointRange[i + 1].Lat; } //以下语句判断A点是否在边的两端点的水平平行线之间,在则可能有交点,开始判断交点是否在左射线上 if (((ALat >= dLat1) && (ALat < dLat2)) || ((ALat >= dLat2) && (ALat < dLat1))) { if (Math.Abs(dLat1 - dLat2) > 0) { //得到 A点向左射线与边的交点的x坐标: dLng = dLng1 - ((dLng1 - dLng2) * (dLat1 - ALat)) / (dLat1 - dLat2); // 如果交点在A点左侧(说明是做射线与 边的交点),则射线与边的全部交点数加一: if (dLng < ALon) { iSum++; } } } } if (iSum % 2 != 0) { return(true); } return(false); }
/// <summary> /// 最小外包法 /// </summary> /// <param name="point"></param> /// <param name="pointRange"></param> /// <returns></returns> private static bool isSimpleMatch(Util.LBS.LBSPoint point, List <Util.LBS.LBSPoint> pointRange) { double max_lng = pointRange.Max(x => x.Lng); double max_lat = pointRange.Max(x => x.Lat); double min_lon = pointRange.Min(x => x.Lng); double min_lat = pointRange.Min(x => x.Lat); double aLng = point.Lng; double aLat = point.Lat; if (aLng >= max_lng || aLng <= min_lon || aLat >= max_lat || aLat <= min_lat) { return(false); } else { return(true); } }
/// <summary> /// 围栏计算(点是否在围栏内) /// </summary> /// <param name="latlon">单点坐标</param> /// <param name="APoints">坐标集合</param> /// <returns></returns> public static bool MBR(Util.LBS.LBSPoint point, List <Util.LBS.LBSPoint> pointRange) { if (pointRange == null || pointRange.Count < 3) { throw new ArgumentException("集合 pointRange 的长度不足3, 无法计算"); } if (false == (pointRange.Count == pointRange.Where(i => i.LocationType == point.LocationType).Count())) { throw new ArgumentException("传入参数的属性 LocationType 不一致"); } if (isSimpleMatch(point, pointRange)) // 先用最小外包法判断 { // 符合最小外包法判断后用暴力遍历法细致判断 return(isPointInPolygon(point, pointRange)); } else { return(false); } }