业务场景举例:快递选择收获区域、车辆电子围栏、运动轨迹路线、地理位置信息检测范围和地图等过滤等等。

比方说地图上有一块区域(抽象成多边形),然后里面每一个位置点(像素点)都有对应的GPS的经纬度坐标值,题目要求的就是判断任意点(用户输入的信息)与多边形的位置关系(是否在里面还是在图形区域外面)。

具体有一个需求为:每一个店维护多个可配送的地址,配送地址为地图中的多边形区域,用户选择收货地址的时候需要判断该收货地址在不在多边形区域内。(给定一个点的坐标以及一个多边形的所有顶点坐标。要求能够判断这个点是在多边形内,还是在多边形外? )

验证地址:Map Polygon/Polyline Tool https://www.keene.edu/campus/maps/tool/

以上的ABCDE,分别是以下数组里面的数据

[java] view plaincopy
  1. Point[] ps = new Point[] { new Point(120.2043 , 30.2795), new Point(120.2030 , 30.2511), new Point(120.1810 , 30.2543), new Point(120.1798 , 30.2781), new Point(120.1926,30.2752) };

那么问题来了:如何判断一个指定的经纬度点是否落在一个多边形区域内?

这个是算法和判断的依据也是解答本题的关键。(保证准确率和速度的关键)

网络上有很多算法和第三库来实现这类功能,但本文着重讲原理和自己实现写程序。

话不多说,直接抛出写程序通常用的流程模型图,如下;

经过在网上的一番搜索,

发现目前比较通用的就是射线法和坐标轴法,而我采用的就是X轴射线法。

主要理论来源于西安交大的一篇论文(即参考文献的第二条)

判断一个点向左的射线跟一个多边形的交叉点有几个,如果结果为奇数的话那么说明这个点落在多边形中,反之则不在。

1、理论支持:如果从需要判断的点出发的一条射线与该多边形的焦点个数为奇数,则该点在此多边形内,否则该点在此多边形外。(射线不能与多边形顶点相交)2、编程思路:该程序的思路是从A点出发向左做一条水平射线(平行于x轴,向X轴的反方向),判断与各边是否有焦点。dLon1, dLon2, dLat1, dLat2分别表示边的起点和终点的经度和纬度(x轴和y轴)。先判断A点是否在边的两端点d1和d2的水平平行线之间,不在则不可能有交点,继续判断下一条边。在之间则说明可能与A点向左的射线有交点,接下来利用几何方法得到A点的水平直线与该边交点的x坐标。然后判断交点的x坐标在A点的左侧还是右侧,左侧则总交点数加一,右侧则不在A点左射线上,继续判断下一条边。

代码讲解: 主要的类有两个↓↓

一个是坐标点的抽象类(点的坐标位置),另一个是位置关系判断工具类(判断点和多边形的关系)。 

因为坐标描点要涉及到坐标以及小数点的经纬关系,故要用到浮点型运算,也就是要使用到double双精度。而地图涉及到很多个像素点构成的图形区域,故要用到list数组。结果要展示需要用到js代码。

def IsPtInPoly(aLon, aLat, pointList):  ''''' :param aLon: double 经度 :param aLat: double 纬度 :param pointList: list [(lon, lat)...] 多边形点的顺序需根据顺时针或逆时针,不能乱 '''  iSum = 0  iCount = len(pointList)  if(iCount < 3):  return False  for i in range(iCount):  pLon1 = pointList[i][0]  pLat1 = pointList[i][1]  if(i == iCount - 1):  pLon2 = pointList[0][0]  pLat2 = pointList[0][1]  else:  pLon2 = pointList[i + 1][0]  pLat2 = pointList[i + 1][1]  if ((aLat >= pLat1) and (aLat < pLat2)) or ((aLat>=pLat2) and (aLat < pLat1)):  if (abs(pLat1 - pLat2) > 0):  pLon = pLon1 - ((pLon1 - pLon2) * (pLat1 - aLat)) / (pLat1 - pLat2);  if(pLon < aLon):  iSum += 1  if(iSum % 2 != 0):  return True  else:  return False  

1、是坐标点的抽象类

Java代码  
  1. package com.niux.crm.core.common.bmap;
  2. /**
  3. * 用于构造百度地图中的经纬度点
  4. *
  5. * @author zhengtian
  6. * @date 2013-8-5 下午02:54:41
  7. */
  8. public class BmapPoint {
  9. private double lng;// 经度
  10. private double lat;// 纬度
  11. public BmapPoint() {
  12. }
  13. public BmapPoint(double lng, double lat) {
  14. this.lng = lng;
  15. this.lat = lat;
  16. }
  17. @Override
  18. public boolean equals(Object obj) {
  19. if (obj instanceof BmapPoint) {
  20. BmapPoint bmapPoint = (BmapPoint) obj;
  21. return (bmapPoint.getLng() == lng && bmapPoint.getLat() == lat) ? true : false;
  22. } else {
  23. return false;
  24. }
  25. }
  26. public double getLng() {
  27. return lng;
  28. }
  29. public void setLng(double lng) {
  30. this.lng = lng;
  31. }
  32. public double getLat() {
  33. return lat;
  34. }
  35. public void setLat(double lat) {
  36. this.lat = lat;
  37. }
  38. }

2、位置关系判断工具类

点与多边形的位置关系的判定规则:1、,根据多边形的坐标,虚拟出一个外包矩形,主要是为了提前过滤不相关的点,减少运算量。2、然后判断是否有重合的点。3、判断点与斜线的交点。4、判断点过顶点的情况。5、判断点与边重合的情况。6、判断点在边上的情况。

其中点过顶点,以及点与边重合的情况,主要采用了加权边的思想,论文与代码中有注释。

Java代码  
  1. package com.niux.crm.core.common.bmap;
  2. import java.util.Arrays;
  3. /**
  4. * 用于点与多边形位置关系的判断
  5. *
  6. * @author zhengtian
  7. * @date 2013-8-5 上午11:59:35
  8. */
  9. public class GraphUtils {
  10. /**
  11. * 判断点是否在多边形内(基本思路是用交点法)
  12. *
  13. * @param point
  14. * @param boundaryPoints
  15. * @return
  16. */
  17. public static boolean isPointInPolygon(BmapPoint point, BmapPoint[] boundaryPoints) {
  18. // 防止第一个点与最后一个点相同
  19. if (boundaryPoints != null && boundaryPoints.length > 0
  20. && boundaryPoints[boundaryPoints.length - 1].equals(boundaryPoints[0])) {
  21. boundaryPoints = Arrays.copyOf(boundaryPoints, boundaryPoints.length - 1);
  22. }
  23. int pointCount = boundaryPoints.length;
  24. // 首先判断点是否在多边形的外包矩形内,如果在,则进一步判断,否则返回false
  25. if (!isPointInRectangle(point, boundaryPoints)) {
  26. return false;
  27. }
  28. // 如果点与多边形的其中一个顶点重合,那么直接返回true
  29. for (int i = 0; i < pointCount; i++) {
  30. if (point.equals(boundaryPoints[i])) {
  31. return true;
  32. }
  33. }
  34. /**
  35. * 基本思想是利用X轴射线法,计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则在多边形内。还会考虑一些特殊情况,如点在多边形顶点上
  36. * , 点在多边形边上等特殊情况。
  37. */
  38. // X轴射线与多边形的交点数
  39. int intersectPointCount = 0;
  40. // X轴射线与多边形的交点权值
  41. float intersectPointWeights = 0;
  42. // 浮点类型计算时候与0比较时候的容差
  43. double precision = 2e-10;
  44. // 边P1P2的两个端点
  45. BmapPoint point1 = boundaryPoints[0], point2;
  46. // 循环判断所有的边
  47. for (int i = 1; i <= pointCount; i++) {
  48. point2 = boundaryPoints[i % pointCount];
  49. /**
  50. * 如果点的y坐标在边P1P2的y坐标开区间范围之外,那么不相交。
  51. */
  52. if (point.getLat() < Math.min(point1.getLat(), point2.getLat())
  53. || point.getLat() > Math.max(point1.getLat(), point2.getLat())) {
  54. point1 = point2;
  55. continue;
  56. }
  57. /**
  58. * 此处判断射线与边相交
  59. */
  60. if (point.getLat() > Math.min(point1.getLat(), point2.getLat())
  61. && point.getLat() < Math.max(point1.getLat(), point2.getLat())) {// 如果点的y坐标在边P1P2的y坐标开区间内
  62. if (point1.getLng() == point2.getLng()) {// 若边P1P2是垂直的
  63. if (point.getLng() == point1.getLng()) {
  64. // 若点在垂直的边P1P2上,则点在多边形内
  65. return true;
  66. } else if (point.getLng() < point1.getLng()) {
  67. // 若点在在垂直的边P1P2左边,则点与该边必然有交点
  68. ++intersectPointCount;
  69. }
  70. } else {// 若边P1P2是斜线
  71. if (point.getLng() <= Math.min(point1.getLng(), point2.getLng())) {// 点point的x坐标在点P1和P2的左侧
  72. ++intersectPointCount;
  73. } else if (point.getLng() > Math.min(point1.getLng(), point2.getLng())
  74. && point.getLng() < Math.max(point1.getLng(), point2.getLng())) {// 点point的x坐标在点P1和P2的x坐标中间
  75. double slopeDiff = 0.0d;
  76. if (point1.getLat() > point2.getLat()) {
  77. slopeDiff = (point.getLat() - point2.getLat()) / (point.getLng() - point2.getLng())
  78. - (point1.getLat() - point2.getLat()) / (point1.getLng() - point2.getLng());
  79. } else {
  80. slopeDiff = (point.getLat() - point1.getLat()) / (point.getLng() - point1.getLng())
  81. - (point2.getLat() - point1.getLat()) / (point2.getLng() - point1.getLng());
  82. }
  83. if (slopeDiff > 0) {
  84. if (slopeDiff < precision) {// 由于double精度在计算时会有损失,故匹配一定的容差。经试验,坐标经度可以达到0.0001
  85. // 点在斜线P1P2上
  86. return true;
  87. } else {
  88. // 点与斜线P1P2有交点
  89. intersectPointCount++;
  90. }
  91. }
  92. }
  93. }
  94. } else {
  95. // 边P1P2水平
  96. if (point1.getLat() == point2.getLat()) {
  97. if (point.getLng() <= Math.max(point1.getLng(), point2.getLng())
  98. && point.getLng() >= Math.min(point1.getLng(), point2.getLng())) {
  99. // 若点在水平的边P1P2上,则点在多边形内
  100. return true;
  101. }
  102. }
  103. /**
  104. * 判断点通过多边形顶点
  105. */
  106. if (((point.getLat() == point1.getLat() && point.getLng() < point1.getLng()))
  107. || (point.getLat() == point2.getLat() && point.getLng() < point2.getLng())) {
  108. if (point2.getLat() < point1.getLat()) {
  109. intersectPointWeights += -0.5;
  110. } else if (point2.getLat() > point1.getLat()) {
  111. intersectPointWeights += 0.5;
  112. }
  113. }
  114. }
  115. point1 = point2;
  116. }
  117. if ((intersectPointCount + Math.abs(intersectPointWeights)) % 2 == 0) {// 偶数在多边形外
  118. return false;
  119. } else { // 奇数在多边形内
  120. return true;
  121. }
  122. }
  123. /**
  124. * 判断点是否在矩形内在矩形边界上,也算在矩形内(根据这些点,构造一个外包矩形)
  125. *
  126. * @param point
  127. *            点对象
  128. * @param boundaryPoints
  129. *            矩形边界点
  130. * @return
  131. */
  132. public static boolean isPointInRectangle(BmapPoint point, BmapPoint[] boundaryPoints) {
  133. BmapPoint southWestPoint = getSouthWestPoint(boundaryPoints); // 西南角点
  134. BmapPoint northEastPoint = getNorthEastPoint(boundaryPoints); // 东北角点
  135. return (point.getLng() >= southWestPoint.getLng() && point.getLng() <= northEastPoint.getLng()
  136. && point.getLat() >= southWestPoint.getLat() && point.getLat() <= northEastPoint.getLat());
  137. }
  138. /**
  139. * 根据这组坐标,画一个矩形,然后得到这个矩形西南角的顶点坐标
  140. *
  141. * @param vertexs
  142. * @return
  143. */
  144. private static BmapPoint getSouthWestPoint(BmapPoint[] vertexs) {
  145. double minLng = vertexs[0].getLng(), minLat = vertexs[0].getLat();
  146. for (BmapPoint bmapPoint : vertexs) {
  147. double lng = bmapPoint.getLng();
  148. double lat = bmapPoint.getLat();
  149. if (lng < minLng) {
  150. minLng = lng;
  151. }
  152. if (lat < minLat) {
  153. minLat = lat;
  154. }
  155. }
  156. return new BmapPoint(minLng, minLat);
  157. }
  158. /**
  159. * 根据这组坐标,画一个矩形,然后得到这个矩形东北角的顶点坐标
  160. *
  161. * @param vertexs
  162. * @return
  163. */
  164. private static BmapPoint getNorthEastPoint(BmapPoint[] vertexs) {
  165. double maxLng = 0.0d, maxLat = 0.0d;
  166. for (BmapPoint bmapPoint : vertexs) {
  167. double lng = bmapPoint.getLng();
  168. double lat = bmapPoint.getLat();
  169. if (lng > maxLng) {
  170. maxLng = lng;
  171. }
  172. if (lat > maxLat) {
  173. maxLat = lat;
  174. }
  175. }
  176. return new BmapPoint(maxLng, maxLat);
  177. }
  178. }

【关于3D地图图形请参考】

IOS高德3D地图画多边形,以及判断某一经纬度是否在该多边形内 - 简书 https://www.jianshu.com/p/fb1177cac1ec

翻看了一下高德提供的技术文档,没找到3D地图下该怎么完成此功能。无奈自己翻找高德的SDK,发现了这两个类:MAPolygon MAOverlayRenderer

MAPolygon用于定义一个由多个点组成的闭合多边形,点与点之间按顺序尾部相连,第一个点与最后一个点相连,通常MAPolygon是MAPolygonView的model

MAOverlayRenderer是地图覆盖物Renderer的基类,提供绘制overlay的接口但并无实际的实现(render相关方法只能在重写后的glRender方法中使用)

【参考文献】
1、两条直线的关系 
http://www.cnblogs.com/devymex/archive/2010/08/19/1803885.html 
2、点与多边形的关系

http://wenku.baidu.com/view/5e3913a2b0717fd5360cdccf.html?qq-pf-to=pcqq.c2c

3、https://en.wikipedia.org/wiki/Haversine_formula

4、百度map js代码 https://www.cnblogs.com/relax/p/3507014.html

5、关于经纬度得到的多边形面积。 https://www.cnblogs.com/JeffController/p/5618742.html

【附录坐标轴函数公式图】

直接上代码(两个点)半正矢公式 计算(Haversine formula):因为难度大暂时不考虑此类算法。


关于指定的经纬度是否落在多边形内 - https://blog.csdn.net/qq_22929803/article/details/46818009

如何判断一个指定的位置点坐标(GPS上的经纬度)是否落在一个多边形区域内?相关推荐

  1. android 经纬度的范围内,在Android里如何判断一个指定的经纬度点是否落在一个多边形区域内...

    在lbs开发中,可能要碰到这样的问题,如何判断一个指定的经纬度点是否落在一个多边形区域内,比如我在地图上画了一个多边形区域,然后给出一个经纬度点,怎样判断这个点是否在这个多边形范围之内,由于我用的是a ...

  2. 在Android里如何判断一个指定的经纬度点是否落在一个多边形区域内

    在lbs开发中,可能要碰到这样的问题,如何判断一个指定的经纬度点是否落在一个多边形区域内,比如我在地图上画了一个多边形区域,然后给出一个经纬度点,怎样判断这个点是否在这个多边形范围之内,由于我用的是a ...

  3. Java判断gps点是否在中国,如何判断一个指定的位置点坐标(GPS上的经纬度)是否落在一个多边形区域内?...

    业务场景举例:快递选择收获区域.车辆电子围栏.运动轨迹路线.地理位置信息检测范围和地图等过滤等等. 比方说地图上有一块区域(抽象成多边形),然后里面每一个位置点(像素点)都有对应的GPS的经纬度坐标值 ...

  4. JAVA判断一个地理坐标是否在一个多边形区域内和是否在一个圆形区域内(经纬度)

    怎么样判断一个坐标点在一个多边形区域内?包括规则多边形,不规则多边形,还有圆... 1 判断一个坐标是否在圆形区域内? 多边形和圆分开写,首先简单的就是判断是否在圆里面,如何判断一个坐标是否在圆形区域 ...

  5. 微信小程序 - 判断一个经纬度是否在一个多边形区域内

    功能描述: 最近公司做了一个定位打卡的小程序,需要判断用户的经纬度是否在一个闭合的多边形区域内,在区域内允许打卡,否则提示"不再打卡范围".由于腾讯地图小程序SDK没有提供相关功能 ...

  6. java判断地图范围_百度地图java 判断当前位置是否在多边形区域内

    package com.haiyisoft.cAssistant.adapter.hessian; import java.awt.geom.Point2D; import java.util.Arr ...

  7. 【INS-30014】无法检查指定的位置是否位于CFS上的解决办法

    安装oracle数据库过程中,出现[INS-30014]无法检查指定的位置是否位于CFS上的解决办法如下: 安装过程中,选择 "仅安装数据库软件",在安装成功后,使用DBCA工具创 ...

  8. 安装Oracle数据库提示:【INS-30014】无法检查指定的位置是否位于CFS上

    问题:安装Oracle数据库提示:[INS-30014]无法检查指定的位置是否位于CFS上 解决:修改hosts文件.该全局数据库名都无效.通过返回第1步骤选择仅安装数据库软件,可安装成功.再通过Da ...

  9. processing判断一个点(鼠标事件)是否在三角形、圆、椭圆、矩形内之第二章(超详细鼠标交互)

    第二篇更新啦!!!! 上一篇我们详细地讲了processing判断点击是否在三角形和圆内​​​​​​​,这一期我们主要讲如何判断点击在椭圆和矩形内部,喜欢就麻烦点赞加关注吧,谢谢噢,你的支持是我继续创 ...

  10. java 坐标系运算 判断一个地理坐标是否在电子围栏 圆、矩形、多边形区域内

    测试没问题,我用的是原始坐标:要注意的是坐标转换问题,要看当前是属于什么坐标系 经纬度与GCS(Geographic Coordinate System, 地理坐标系统) 平面坐标与PCS(Proje ...

最新文章

  1. 敏捷项目管理任务卡片多选
  2. omnigraffle怎么画曲线
  3. html5表格图片按比例缩放,JS图片等比例缩放方法完整示例
  4. 数据库面试题【五、索引的优缺点,什么时候使用索引,什么时候不能使用索引】
  5. 仓库管理的5S如何在仓库中实施
  6. Oracle JDBC中的PreparedStatement占位符过多
  7. 在Websphere 8.0上安装Liferay 6.2 Enterprise Edition
  8. 改进初学者的PID-介绍
  9. python 解决bytes和str之间 出现 TypeError的问题
  10. 基于Python的ModbusTCP客户端实现
  11. 演变模型_开放组织的演变
  12. 常用linux命令及图解(实践文档,小白都可以看得懂)
  13. Linux之32/64位int、char、int*、char*与空结构体大小
  14. mysql 高级查询总结_MySQL高级查询
  15. ISM模型的MATLAB代码
  16. 湖南师范大学2018年大学生程序设计竞赛新生赛 F-小名的回答
  17. 使用Certbot申请ssl证书
  18. 手机199开头的号码的正则验证
  19. Python 编程从入门到实践 6-7动手试一试 人
  20. 小ck活动机器人包包_你一定要入手的小ck包

热门文章

  1. 依然顺滑!Dragonwell 11如何改造全新垃圾回收器ZGC? | 龙蜥技术
  2. 将word背景设成淡绿色方法
  3. 牛皮凉席不好的原因!
  4. 【目标跟踪 MOT】JDE - Towards Real-Time Multi-Object Tracking
  5. oracle 查看进程
  6. QT从入门到入土(一)——Qt5.14.2安装教程和VS2019环境配置
  7. 清华数为DWF低代码平台使用感悟
  8. 梦三花重金修改服务器,3月6日一梦江湖游戏更新公告
  9. 英特尔cpu发布时间表_英特尔延长CPU发布周期:摩尔定律终结
  10. 优质的石材 石头VRay材质球素材推荐,不容错过