如何判断一个点是否在一个多边形内,何时会用到这个场景。
我们就模拟一个真是场景。我们公司是快递公司,在本地区域有6个分点。每个分点有3-5个工人负责附近的快递派遣发送,所以根据每个点的服务区域我们就能大概知道我们的服务范围。如果客户要收发快递我们会告知是否在服务范围内,且那个点离的最近,应派谁去收发快递。……
网上其实找了好多判断点是否在经纬度的多边形内,但都是Javascript版:
google算法:
其中第三个是百度官网的一个示例
查看源码,百度里面有一个文件
其中isPointInPolygon方法就是判断点是否在多边形内部
//点在多边形内function ptInPolygon(){ var pts = []; var pt1 = new BMap.Point(116.395, 39.910); var pt2 = new BMap.Point(116.394, 39.914); var pt3 = new BMap.Point(116.403, 39.920); var pt4 = new BMap.Point(116.402, 39.914); var pt5 = new BMap.Point(116.410, 39.913); pts.push(pt1); pts.push(pt2); pts.push(pt3); pts.push(pt4); pts.push(pt5); var ply = new BMap.Polygon(pts); var pt =new BMap.Point(116.400, 39.914); var result = BMapLib.GeoUtils.isPointInPolygon(pt, ply); if(result == true){ alert("点在多边形内"); } else { alert("点在多边形外") } //演示:将面添加到地图上 map.clearOverlays(); var mkr = new BMap.Marker(pt); map.addOverlay(mkr); map.addOverlay(ply); }
PHP版的也有好几个,都是翻译Javascript但试了下,几乎没一个可以判断验证的。后来在一个论坛中找到了一个很精准的计算多边形内代码,贴出来和大家分享
$point=[ 'lng'=>121.427417, 'lat'=>31.20357 ]; $arr=[ [ 'lng'=>121.23036, 'lat'=>31.218609 ], [ 'lng'=>121.233666, 'lat'=>31.210579 ], [ 'lng'=>121.247177, 'lat'=>31.206749 ], [ 'lng'=>121.276353, 'lat'=>31.190811 ], [ 'lng'=>121.267442, 'lat'=>31.237383 ], ]; $a= is_point_in_polygon($point, $arr); var_dump($a);/** * 判断一个坐标是否在一个多边形内(由多个坐标围成的) * 基本思想是利用射线法,计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则 * 在多边形内。还会考虑一些特殊情况,如点在多边形顶点上,点在多边形边上等特殊情况。 * @param $point 指定点坐标 * @param $pts 多边形坐标 顺时针方向 */function is_point_in_polygon($point, $pts) { $N = count($pts); $boundOrVertex = true; //如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true $intersectCount = 0;//cross points count of x $precision = 2e-10; //浮点类型计算时候与0比较时候的容差 $p1 = 0;//neighbour bound vertices $p2 = 0; $p = $point; //测试点 $p1 = $pts[0];//left vertex for ($i = 1; $i <= $N; ++$i) { //check all rays // dump($p1); if ($p['lng'] == $p1['lng'] && $p['lat'] == $p1['lat']) { return $boundOrVertex;//p is an vertex } $p2 = $pts[$i % $N];//right vertex if ($p['lat'] < min($p1['lat'], $p2['lat']) || $p['lat'] > max($p1['lat'], $p2['lat'])) { //ray is outside of our interests $p1 = $p2; continue;//next ray left point } if ($p['lat'] > min($p1['lat'], $p2['lat']) && $p['lat'] < max($p1['lat'], $p2['lat'])) { //ray is crossing over by the algorithm (common part of) if($p['lng'] <= max($p1['lng'], $p2['lng'])){ //x is before of ray if ($p1['lat'] == $p2['lat'] && $p['lng'] >= min($p1['lng'], $p2['lng'])) { //overlies on a horizontal ray return $boundOrVertex; } if ($p1['lng'] == $p2['lng']) { //ray is vertical if ($p1['lng'] == $p['lng']) { //overlies on a vertical ray return $boundOrVertex; } else { //before ray ++$intersectCount; } } else { //cross point on the left side $xinters = ($p['lat'] - $p1['lat']) * ($p2['lng'] - $p1['lng']) / ($p2['lat'] - $p1['lat']) + $p1['lng'];//cross point of lng if (abs($p['lng'] - $xinters) < $precision) { //overlies on a ray return $boundOrVertex; } if ($p['lng'] < $xinters) { //before ray ++$intersectCount; } } } } else { //special case when ray is crossing through the vertex if ($p['lat'] == $p2['lat'] && $p['lng'] <= $p2['lng']) { //p crossing over p2 $p3 = $pts[($i+1) % $N]; //next vertex if ($p['lat'] >= min($p1['lat'], $p3['lat']) && $p['lat'] <= max($p1['lat'], $p3['lat'])) { //p.lat lies between p1.lat & p3.lat ++$intersectCount; } else { $intersectCount += 2; } } } $p1 = $p2;//next ray left point } if ($intersectCount % 2 == 0) { //偶数在多边形外 return false; } else { //奇数在多边形内 return true; }}
打印:bool(false)
将
$point=[ 'lng'=>121.427417, 'lat'=>31.20357 ];
替换为
$point=[ 'lng'=>121.257428, 'lat'=>31.222481 ];
打印:bool(true)
------------- 扩展 -------------------------
在百度地图上绘制多边形并保存绘制的点的经纬度
鼠标绘制工具
访问效果:
保存数据:
[{"city":"上海","ranges":"{\"service_area\":[{\"name\":\"多边形1\",\"points\":[{\"lng\":121.23036,\"lat\":31.218609},{\"lng\":121.233666,\"lat\":31.210579},{\"lng\":121.247177,\"lat\":31.206749},{\"lng\":121.276353,\"lat\":31.190811},{\"lng\":121.267442,\"lat\":31.237383}]},{\"name\":\"多边形2\",\"points\":[{\"lng\":121.05846,\"lat\":31.257636},{\"lng\":121.044662,\"lat\":31.151385},{\"lng\":121.165969,\"lat\":31.157318},{\"lng\":121.165969,\"lat\":31.157318},{\"lng\":121.165969,\"lat\":31.157318}]},{\"name\":\"多边形3\",\"points\":[{\"lng\":121.16482,\"lat\":31.269489},{\"lng\":121.165395,\"lat\":31.235901},{\"lng\":121.19414,\"lat\":31.240347},{\"lng\":121.201614,\"lat\":31.262081},{\"lng\":121.243583,\"lat\":31.279859},{\"lng\":121.244733,\"lat\":31.249239},{\"lng\":121.207363,\"lat\":31.293191},{\"lng\":121.207363,\"lat\":31.293191},{\"lng\":121.207363,\"lat\":31.293191},{\"lng\":121.207363,\"lat\":31.293191}]},{\"name\":\"多边形4\",\"points\":[{\"lng\":121.371789,\"lat\":31.274921},{\"lng\":121.326946,\"lat\":31.208232},{\"lng\":121.36719,\"lat\":31.136055},{\"lng\":121.552888,\"lat\":31.167701},{\"lng\":121.590257,\"lat\":31.154352},{\"lng\":121.623602,\"lat\":31.223056},{\"lng\":121.560936,\"lat\":31.280353},{\"lng\":121.519542,\"lat\":31.300102},{\"lng\":121.519542,\"lat\":31.300102},{\"lng\":121.519542,\"lat\":31.300102},{\"lng\":121.450553,\"lat\":31.252203}]}]}"}]
还有一个编辑和保存多边形文件是复制百度地图生成器样式修改的。
百度地图生成器:
文件下载:
效果:
2017-03-2更新:升级版百度多边形编辑