判断点是否在任意多边形内

匿名 (未验证) 提交于 2019-12-02 23:43:01

最近项目用到:在Google map上判断事发地点,是否在管辖区域内。也就是典型的判断一个点是否在不规则任意多边形内的例子。

但是Google Map没有提供相应的api,找资料发现百度地图提供了一个工具类,肿么办,为了一个工具类,加入百度地图吗,操蛋,这是不可能的!

百度地图api链接:http://wiki.lbsyun.baidu.com/cms/androidsdk/doc/v3_7_0/com/baidu/mapapi/utils/SpatialRelationUtil.html

Point Inclusion

Point Inclusion ― Part II

我们有结论:

具体演示效果见

GitHub:https://github.com/shaoshuai904/GoogleMap_Demo

下面是代码部分:

/**  * Polygon 与 Point 空间关系 工具类  *  * @author maple  */ public class SpatialRelationUtil {         /**      * 返回一个点是否在一个多边形区域内(推荐)      *      * @param mPoints 多边形坐标点列表      * @param point   待判断点      * @return true 多边形包含这个点,false 多边形未包含这个点。      */     public static boolean isPolygonContainsPoint1(List<LatLng> mPoints, LatLng point) {         LatLngBounds.Builder boundsBuilder = LatLngBounds.builder();         for (LatLng ll : mPoints)             boundsBuilder.include(ll);         // 如果point不在多边形Bounds范围内,直接返回false。         if (boundsBuilder.build().contains(point)) {             return isPolygonContainsPoint(mPoints, point);         } else {             return false;         }     }       /**      * 返回一个点是否在一个多边形区域内      *      * @param mPoints 多边形坐标点列表      * @param point   待判断点      * @return true 多边形包含这个点,false 多边形未包含这个点。      */     public static boolean isPolygonContainsPoint(List<LatLng> mPoints, LatLng point) {         int nCross = 0;         for (int i = 0; i < mPoints.size(); i++) {             LatLng p1 = mPoints.get(i);             LatLng p2 = mPoints.get((i + 1) % mPoints.size());             // 取多边形任意一个边,做点point的水平延长线,求解与当前边的交点个数             // p1p2是水平线段,要么没有交点,要么有无限个交点             if (p1.longitude == p2.longitude)                 continue;             // point 在p1p2 底部 --> 无交点             if (point.longitude < Math.min(p1.longitude, p2.longitude))                 continue;             // point 在p1p2 顶部 --> 无交点             if (point.longitude >= Math.max(p1.longitude, p2.longitude))                 continue;             // 求解 point点水平线与当前p1p2边的交点的 X 坐标             double x = (point.longitude - p1.longitude) * (p2.latitude - p1.latitude) / (p2.longitude - p1.longitude) + p1.latitude;             if (x > point.latitude) // 当x=point.x时,说明point在p1p2线段上                 nCross++; // 只统计单边交点         }         // 单边交点为偶数,点在多边形之外 ---         return (nCross % 2 == 1);     }       /**      * 返回一个点是否在一个多边形边界上      *      * @param mPoints 多边形坐标点列表      * @param point   待判断点      * @return true 点在多边形边上,false 点不在多边形边上。      */     public static boolean isPointInPolygonBoundary(List<LatLng> mPoints, LatLng point) {         for (int i = 0; i < mPoints.size(); i++) {             LatLng p1 = mPoints.get(i);             LatLng p2 = mPoints.get((i + 1) % mPoints.size());             // 取多边形任意一个边,做点point的水平延长线,求解与当前边的交点个数               // point 在p1p2 底部 --> 无交点             if (point.longitude < Math.min(p1.longitude, p2.longitude))                 continue;             // point 在p1p2 顶部 --> 无交点             if (point.longitude > Math.max(p1.longitude, p2.longitude))                 continue;               // p1p2是水平线段,要么没有交点,要么有无限个交点             if (p1.longitude == p2.longitude) {                 double minX = Math.min(p1.latitude, p2.latitude);                 double maxX = Math.max(p1.latitude, p2.latitude);                 // point在水平线段p1p2上,直接return true                 if ((point.longitude == p1.longitude) && (point.latitude >= minX && point.latitude <= maxX)) {                     return true;                 }             } else { // 求解交点                 double x = (point.longitude - p1.longitude) * (p2.latitude - p1.latitude) / (p2.longitude - p1.longitude) + p1.latitude;                 if (x == point.latitude) // 当x=point.x时,说明point在p1p2线段上                     return true;             }         }         return false;     }   }

使用说明:只需要将SpatialRelationUtil这个工具类,复制到你的项目就可以直接使用,不用添加任何jar包。

好多人说不知道LatLngBounds类的具体实现,其实这是Google map包中的一个类,内部功能很简单,就是提供了一个构造器Builder可以不断的往里面添加经纬度点LatLng,不断计算更新Bounds范围和center中心点。

下面贴上整理后的LatLngBounds类,可以减去导入Google map包的麻烦

/**  * 经纬度范围类  *  * 复写com.google.android.gms.maps.model.LatLngBounds中核心方法  *  * @author maple  */ class LatLngBounds constructor(         private val southwest: LatLng,// 左下角 点         private val northeast: LatLng // 右上角 点 ) {       val center: LatLng         get() {             // 计算中心点纬度             val centerLat = (this.southwest.latitude + this.northeast.latitude) / 2.0             // 计算中心点经度             val neLng = this.northeast.longitude // 右上角 经度             val swLng: Double = this.southwest.longitude // 左下角 经度             val centerLng: Double = if (swLng <= neLng) {                 (neLng + swLng) / 2.0             } else {                 (neLng + 360.0 + swLng) / 2.0             }             return LatLng(centerLat, centerLng)         }       // 某个点是否在该范围内(包含边界)     fun contains(point: LatLng): Boolean {         return latContains(point.latitude) && this.lngContains(point.longitude)     }       // 某个纬度值是否在该范围内(包含边界)     private fun latContains(lat: Double): Boolean {         return this.southwest.latitude <= lat && lat <= this.northeast.latitude     }       // 某个经度值是否在该范围内(包含边界)     private fun lngContains(lng: Double): Boolean {         return if (this.southwest.longitude <= this.northeast.longitude) {             this.southwest.longitude <= lng && lng <= this.northeast.longitude         } else {             this.southwest.longitude <= lng || lng <= this.northeast.longitude         }     }       // 小数据量可以使用该方法,大数据量建议使用Builder中的include()     fun including(point: LatLng): LatLngBounds {         val swLat = Math.min(this.southwest.latitude, point.latitude)         val neLat = Math.max(this.northeast.latitude, point.latitude)         var neLng = this.northeast.longitude         var swLng = this.southwest.longitude         val pLng = point.longitude         if (!this.lngContains(pLng)) {             if (zza(swLng, pLng) < zzb(neLng, pLng)) {                 swLng = pLng             } else {                 neLng = pLng             }         }         return LatLngBounds(LatLng(swLat, swLng), LatLng(neLat, neLng))     }       /**      * LatLngBounds生成器      */     class Builder {         private var swLat = 1.0 / 0.0   // 左下角 纬度         private var swLng = 0.0 / 0.0   // 左下角 经度         private var neLat = -1.0 / 0.0  // 右上角 纬度         private var neLng = 0.0 / 0.0   // 右上角 经度           fun include(point: LatLng): Builder {             this.swLat = Math.min(this.swLat, point.latitude)             this.neLat = Math.max(this.neLat, point.latitude)             val pLng = point.longitude             if (java.lang.Double.isNaN(this.swLng)) {                 this.swLng = pLng             } else {                 if (lngContains(pLng)) {                     return this                 }                 if (zza(this.swLng, pLng) < zzb(this.neLng, pLng)) {                     this.swLng = pLng                     return this                 }             }             this.neLng = pLng             return this         }           // 某个经度值是否在该范围内(包含边界)         private fun lngContains(lng: Double): Boolean {             return if (this.swLng <= this.neLng) {                 this.swLng <= lng && lng <= this.neLng             } else {                 this.swLng <= lng || lng <= this.neLng             }         }           fun build(): LatLngBounds {             // Preconditions.checkState(!java.lang.Double.isNaN(this.swLng), "no included points")             return LatLngBounds(LatLng(this.swLat, this.swLng), LatLng(this.neLat, this.neLng))         }     }       companion object {         fun builder(): Builder {             return Builder()         }           // 前者 - 后者         private fun zza(var0: Double, var2: Double): Double {             return (var0 - var2 + 360.0) % 360.0         }           // 后者 - 前者         private fun zzb(var0: Double, var2: Double): Double {             return (var2 - var0 + 360.0) % 360.0         }     }   }

LatLng就是一个x,y的点类

 /**      * 经纬度点      */     class LatLng(             val latitude: Double,   // 纬度             val longitude: Double   // 经度     ) {         override fun toString(): String {             return "LatLng(latitude=$latitude, longitude=$longitude)"         }     }

效果展示:这个Demo展示判断事发地点是否在管辖区域内,也就是判断圆心是否在某一个基础区域内,如果在基础区域内,显示圆的半径(单位英里),如果不在基础区域给予提示:Point not in jurisdiction(事发点不在管辖范围内)。

本文转自:https://blog.csdn.net/shao941122/article/details/51504519

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!