电子围栏的实现(一):矩形、圆与多边形的处理

本文介绍电子围栏区域处理算法,包括矩形、圆形和多边形区域处理。矩形区域通过坐标范围判断点是否在内;圆形区域基于经纬度计算点与圆心距离;多边形区域采用铅垂线内点法,通过计算点与多边形交点个数判断点的位置,该方法适用于各类多边形。

矩形区域处理

针对矩形区域处理,复杂度较低。设矩形ABCD的四个顶点分别为A、B、C、D,坐标分别为A(x1,y1)、B(x2,y2)、C(x3,y3)、D(x4,y4),设点E(x5,y5)是需要判断的点。那么判断点E是否在矩形区域的方法是:x5>x3且x5<x2,同时满足y5>y3且y5<y2,当然我们也可以把A、D两个点作为参照

/**
  * 判断是否在矩形范围内
  * @param lat		测试点纬度
  * @param lng		测试点经度
  * @param minLat	纬度范围限制1
  * @param maxLat	纬度范围限制2
  * @param minLng	经度范围限制1
  * @param maxLng	经度范围限制2
  * @return
  */
public static boolean isInRectangleArea(double lat, double lng, double minLat, double maxLat, double minLng, double maxLng){
		
    if(isInRange(lat, minLat, maxLat)){//如果在维度范围内
        if(minLng*maxLng>0){
	    if(isInRange(lng, minLng, maxLng)){
		return true;
	    }else{
	        return false;
	    }
        }else{
	    if(Math.abs(minLng)+Math.abs(maxLng)<180){
		if(isInRange(lng, minLng, maxLng)){
		    return true;
		}else{
		    return false;
		}
	    }else{
                double left = Math.max(minLng, maxLng);
	        double right = Math.min(minLng, maxLng);
	        if(isInRange(lng, left, 180) || isInRange(lng, right, -180)){
                    return true;
	         }else{
		    return false;
	         }
	    }
	}
    }else{
	return false;
    }
}
	

 

圆形区域处理

对于圆形区域,我们假设地球是正圆的,同时假设圆心为B(Xb,Yb),A(Xa,Ya)为判断的点,Xb,Yb为B点经纬度,Xa,Ya为A点经纬度,A,B是地球表面的两个点,如图2中的a所示,已知A、B两点的经度后,我们可以计算CD的地面长度为:

    (Xb-Xa)*\pi *R/180,R地球半径                                                                                                                      (1)

将CD向上平移至FB,F、G、B、A在球面上,沿着O'O的方向投影,如图2中的b,FB在面OCD的投影为HK,HK的长度和FB长度相等,即

FB=HK=OF*cos(Yb*\pi/180)*(Xb-Xa)*\pi/180=R*(Xb-Xa)*cos(Yb*\pi/180)*\pi/180                                                 (2)

由式(1)和式(2)知:

FB=HK=CD*cos(Yb*\pi/180)                                                                                                                       (3)

AF的长度为:

(Ya-Yb)*\pi*R/180                                                                                                                                              (4)

AF是垂直于FB,不考虑地表曲面,满足

AB^2=AF^2+FB^2                                                                                                                                                   (5)

因为B为圆心,A在圆内的条件是AB长度小于圆的半径r,根据式(1)、式(3)、式(4)、式(5)知:

\sqrt{((Xb-Xa)*\pi*R*cos(Yb*\pi/180)/180)^2+((Xb-Xa)*\pi*R*cos(Yb*\pi/180)/180)^2}\leq \leqslant r    (6)

CD不但可以平移至B点,也可以 平移至A点,当然算出的结果不一样,这里将式(6)中的Yb取值为(Ya+Yb)/2,即去平均值,效果较佳。

/**
 * 地球半径
 */
private static double EARTH_RADIUS = 6378138.0;

private static double rad(double d){
	return d * Math.PI / 180.0;
}

/**
 * 计算是否在圆上(单位/千米)
 * @param radius	半径
 * @param lat1		维度
 * @param lng1		经度
 * @return
 */
public static boolean isInCircle(double radius, double lat1, double lng1, double lat2, double lng2){
	
	double radLat1 = rad(lat1);
	double radLat2 = rad(lat2);
	double a = radLat1 - radLat2;
	double b = rad(lng1) - rad(lng2);
	double s = 2 * Math.asin(Math.sqrt(Math.pow(
			Math.sin(a/2), 2) + 
			Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2), 2)));
	s = s * EARTH_RADIUS;
	s = Math.round(s * 10000) / 10000;
	if(s > radius){//不在圆上
		return false;
	}else{
		return true;
	}
}

 

多边形区域处理

如图3,判断点p在多边形内的方法是:用点p的水平坐标去和多边形相交,得到若干个交点,如果点p两侧的交点数量都是奇数个时,说明p点在多边形内,即铅垂线内点法。使用这种方法,适合任意多边形,包括凸多边形和凹多边形,同时适用于有孔的多边形。下面给出这种算法的Java实现

/**
 * 判断点是否在多边形内
 * @param polygon	多边形
 * @param point		检测点
 * @return			点在多边形内返回true,否则返回false
 */
public static boolean IsPtInPoly(List<Point2D.Double> polygon, Point2D.Double point){
	
	int N = polygon.size();
	boolean boundOrVertex = true;//如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
	int intersectCount = 0;//cross points count of x--交叉点计数X
	double precision = 2e-10;//浮点类型计算时候与0比较时候的容差
	Point2D.Double p1, p2;//neighbour bound vertices--临近绑定顶点
	Point2D.Double p = point;//当前点
	
	p1 = polygon.get(0);//left vertex--左顶点
	for (int i = 1; i <= N; ++i) {//check all rays--检查所有射线
		if(p.equals(p1))
		return boundOrVertex;//p is an vertex--p是一个顶点
		
		p2 = polygon.get(i % N);//right vertex--右顶点
		if(p.x < Math.min(p1.x, p2.x) || p.x > Math.max(p1.x, p2.x)){//ray is outside of our interests--射线不在我们的兴趣范围之内
			p1 = p2;
			continue;//next ray left point--下一条射线的左边点
		}
		
		if(p.x > Math.min(p1.x, p2.x) && p.x < Math.max(p1.x, p2.x)){//ray is crossing over by the algorithm(common part of)--射线被算法穿越(常见的一部分)
			if(p.y <= Math.max(p1.y, p2.y)){//x is before of ray--x在射线之前
				if(p1.x == p2.x && p.y >= Math.min(p1.y, p2.y)){//overlies on a horizontal ray--在一条水平射线上
					return boundOrVertex;
				}
				
				if(p1.y == p2.y){//ray is vertical--射线是垂直的
					if(p1.y == p.y){//overlies on a vertical ray--覆盖在垂直光线上
						return boundOrVertex;
					}else{//before ray--射线之前
						++intersectCount;
					}
					
				}else{//cross point on the left side--左边的交叉点
					double xinters = (p.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;//cross point of y--y的交叉点
					if(Math.abs(p.y - xinters) < precision){//overlies on a ray--覆盖在射线
						return boundOrVertex;
					}
					
					if(p.y < xinters){//before ray--射线之前
						++intersectCount;
					}
				}
			}
		}else{//special case when ray is crossing through the vertex--特殊情况下,当射线穿过顶点
			if(p.x == p2.x && p.y <= p2.y){//p crossing over p2--p交叉p2
				Point2D.Double p3 = polygon.get((i+1) % N);//next vertex--下一个顶点
				if(p.x >= Math.min(p1.x, p3.x) && p.x <=Math.max(p1.x, p3.x)){//p.x lies between p1.x & p3.x--p.x在p1.x和p3.x之间
					++intersectCount;
				}else{
					intersectCount +=  2;
				}
			}
		}
		p1 = p2;//next ray left point--下一条射线的左边点
	}
	if(intersectCount % 2 == 0){//偶数在多边形外
		return false;
	}else{//奇数在多边形内
		return true;
	}
}

 

补充:铅垂线内点法

铅垂线内点法的基本思想是从待判别点向外引垂线,计算其与多边形交点的个数,若交点个数为奇数,则点在多边形内;若交点个数为偶数,则该点在多边形外,如下图上面两图。

这种方法不但适合于任意凸、凹多边形,而且适合于甚至于有孔的多边形,当然, 如果铅垂线恰好教育多边形的顶点或边上时,这时只需计算一侧的交点个数即可,如上图下面两图。

 

 

 

 

 

 

 

<think>好的,用户想在Vue项目中使用vueBMap Draw库实现电子围栏的绘制功能,支持形、矩形多边形,以及回显、选中和修改操作。我需要先了解vueBMap Draw的基本用和相关API。 首先,用户需要安装和配置vueBMap。可能需要引入百度地图的API脚本,并正确初始化BMap组件。然后,vueBMap Draw库可能提供了绘制控件,需要添加到地图上。不同的图形类型(形、矩形多边形)可能需要不同的配置参数。 接下来是绘制功能的实现。用户可能需要调用绘制控件的方,监听绘制完成的事件,获取绘制的图形数据并保存。例如,当用户绘制完形时,触发事件,获取心坐标和半径,存储起来以便后续使用。 关于回显,可能需要将之前保存的图形数据重新渲染到地图上。这里需要注意每个图形的不同结构,比如多边形由多个组成,形需要中心和半径等。可能需要使用BMap的覆盖物类,如Circle、Polygon等,根据数据重新创建覆盖物并添加到地图。 选中和修改功能可能需要使用到交互控件,比如允许用户击已存在的图形进行选择,然后拖拽顶或边来修改形状。vueBMap Draw可能提供了modify和select控件,需要启用这些控件,并监听修改事件来更新存储的数据。 另外,用户可能需要处理不同图形类型之间的切换,比如选择绘制形还是矩形。这可以通过动态设置绘制控件的mode来实现,或者在绘制前配置相应的参数。 还需要考虑地图的事件处理,比如击、拖拽等,避免事件冲突。可能需要在地图的不同状态下启用或禁用某些事件监听器。 最后,示例代码的结构应该清晰,包含组件的引入、地图初始化、绘制控件配置、事件处理函数,以及数据存储和更新逻辑。需要确保代码中的关键部分有注释说明,方便用户理解。 可能遇到的问题包括百度地图API的加载顺序,确保在组件初始化之前完成;图形数据的正确转换,比如经纬度对象的处理;以及选中和修改时的事件冒泡问题。需要检查vueBMap Draw的文档,确认各个方和事件的使用方式。 总结步骤:安装配置库、初始化地图、添加绘制控件、处理绘制完成事件、存储图形数据、回显图形、启用选择和修改控件、更新数据。同时,提供不同图形类型的切换方和示例代码。</think>在Vue项目中使用vueBMap Draw实现电子围栏功能,可以通过以下步骤实现核心功能(基于百度地图JSAPI): ```vue <template> <div> <b-map :center="center" :zoom="13" class="map-container"> <!-- 绘制控件 --> <b-draw-control :active="drawType" @drawend="handleDrawEnd" :options="{ circle: true, rectangle: true, polygon: true }" /> <!-- 修改控件 --> <b-modify-control v-if="selectedFeature" :features="[selectedFeature]" @modifyend="handleModifyEnd" /> </b-map> </div> </template> <script> export default { data() { return { center: '北京', fences: [], // 存储所有围栏 selectedFeature: null, // 当前选中围栏 drawType: null // 当前绘制类型 } }, methods: { // 绘制完成回调 handleDrawEnd(event) { const geometry = event.feature.getGeometry() const newFence = { type: geometry.getType(), coordinates: this.getCoordinates(geometry), feature: event.feature } this.fences.push(newFence) }, // 修改完成回调 handleModifyEnd(event) { const index = this.fences.findIndex(f => f.feature === event.features[0]) if (index > -1) { const geometry = event.features[0].getGeometry() this.fences[index].coordinates = this.getCoordinates(geometry) } }, // 坐标转换方 getCoordinates(geometry) { if (geometry instanceof BMap.Circle) { return { center: geometry.getCenter(), radius: geometry.getRadius() } } if (geometry instanceof BMap.Polygon) { return geometry.getPath().map(point => [point.lng, point.lat]) } if (geometry instanceof BMap.Rectangle) { const bounds = geometry.getBounds() return [ [bounds.getSouthWest().lng, bounds.getSouthWest().lat], [bounds.getNorthEast().lng, bounds.getNorthEast().lat] ] } return null }, // 回显已有围栏 renderExistingFences() { this.fences.forEach(fence => { const feature = new BMap.Feature(this.createGeometry(fence)) this.$refs.map.addOverlay(feature) }) }, // 创建几何图形 createGeometry(fence) { switch (fence.type) { case 'circle': return new BMap.Circle( new BMap.Point(...fence.coordinates.center), fence.coordinates.radius ) case 'polygon': return new BMap.Polygon( fence.coordinates.map(p => new BMap.Point(p[0], p[1])) ) case 'rectangle': return new BMap.Rectangle( new BMap.Bounds( new BMap.Point(...fence.coordinates[0]), new BMap.Point(...fence.coordinates[1]) ) ) } } } } </script> ``` ### 实现说明: 1. **绘制控制**:通过`<b-draw-control>`组件配置支持的图形类型,使用`drawend`事件捕获绘制结果[^1] 2. **数据存储**:将几何图形转换为可序列化的坐标数据,存储时需区分不同类型: - 形:中心坐标+半径 `{ center: [lng, lat], radius: number }` - 矩形:西南角+东北角坐标 `[[swLng, swLat], [neLng, neLat]]` - 多边形:顶坐标数组 `[[lng1,lat1], [lng2,lat2], ...]` 3. **修改交互**:使用`<b-modify-control>`绑定选中要素,通过`modifyend`事件更新存储数据 4. **回显实现**:通过`createGeometry`方将存储数据重新转换为BMap几何对象
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值