大家都知道,点与多边形的关系无非是点在多边形内、点在多边形外、点在多边形的边界上。那么,如何用代码是实现肉眼就能辨别的地理位置关系呢?~
多边形的外包矩形
> 如果一个点不在多边形的外包矩形内,那它肯定不在这个多边形内
射线法判断奇偶数
> 计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则在多边形内
//判断点是否在多边形内:
function isPointInPolygon(point,polygon){
//如果是百度的点,用百度已有的方法判断该点是否在多边形的外包矩形内
if((point instanceof BMap.Point)&&(polygon instanceof BMap.Polygon)){
var polygonBounds = polygon.getBounds();
var sw = polygonBounds.getSouthWest(); //西南脚点
var ne = polygonBounds.getNorthEast(); //东北脚点
var inRect = point.lng >= sw.lng && point.lng <= ne.lng && point.lat >= sw.lat && point.lat <= ne.lat ;
if(!inRect){
return false ; //如果不在外包矩形内,更不会在多边形内。直接返回false
}else{
var pts = polygon.getPath();//获取多边形的点集合。
/**
基本思想是利用射线法,计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则在多边形内
*/
var N = pts.length;
var boundOrVertex = true; //如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
var intersectCount = 0;//cross points count of x
var precision = 2e-10; //浮点类型计算时候与0比较时候的容差
var p1, p2;//neighbour bound vertices
var p = point; //测试点
p1 = pts[0];//left vertex
for(var i = 1; i <= N; ++i){//check all rays
if(p.equals(p1)){
return boundOrVertex;//p is an vertex
}
p2 = pts[i % N];//right vertex
if(p.lat < Math.min(p1.lat, p2.lat) || p.lat > Math.max(p1.lat, p2.lat)){//ray is outside of our interests
p1 = p2;
continue;//next ray left point
}
if(p.lat > Math.min(p1.lat, p2.lat) && p.lat < Math.max(p1.lat, p2.lat)){//ray is crossing over by the algorithm (common part of)
if(p.lng <= Math.max(p1.lng, p2.lng)){//x is before of ray
if(p1.lat == p2.lat && p.lng >= Math.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
var xinters = (p.lat - p1.lat) * (p2.lng - p1.lng) / (p2.lat - p1.lat) + p1.lng;//cross point of lng
if(Math.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
var p3 = pts[(i+1) % N]; //next vertex
if(p.lat >= Math.min(p1.lat, p3.lat) && p.lat <= Math.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;
}
}
}
}
其中3个点的坐标如下:
var pointA = new BMap.Point(121.333777,31.206659);
var pointB = new BMap.Point(121.307313,31.203956);
var pointC = new BMap.Point(121.317967,31.224201);
多边形的可参考上一篇计算面积的点集合。
//调用计算面积的方法
setTimeout(function(){
alert("A是否在范围内:"+isPointInPolygon(pointA,polygon) );
alert("B是否在范围内:"+isPointInPolygon(pointB,polygon) );
alert("C是否在范围内:"+isPointInPolygon(pointC,polygon) );
},1000);
因为在代码里把在多边形上的点默认值为boundOrVertex =true 。所以C点也属于范围内的
通过计算点与范围的关系,可以拓展计算范围与范围的交叉重叠计算,在实际业务中很有用喔。