百度地图绘制弧线示例代码讲解

2,382 阅读4分钟

百度地图api弧线: lbsyun.baidu.com/jsdemo.htm#…

var BMapLib = window.BMapLib = BMapLib || {};
(function() {
	BMapLib.CurveLine = CurveLine;

	function CurveLine(points, opts) {
		var self = this;
		// getCurvePoints函数是获取points数组相邻两点中二阶贝塞尔曲线的50个点
		var curvePoints = getCurvePoints(points);
		// new BMap.Polyline新建一个折线对象
		var polyline = new BMap.Polyline(curvePoints, opts);
		// 添加lineupdate(覆盖物的属性发生变化时触发)事件
		polyline.addEventListener("lineupdate", function() {
			if (this.isEditing) {
				this.enableEditing()
			}
		});
		// 记录points中的每个点,作用是在点上添加图片覆盖层
		polyline.cornerPoints = points;
		polyline.editMarkers = [];
		polyline.enableEditing = function() {
			var self = this;
			// 先判断一下map是否存在,应为this指向问题
			if (self.map) {
				self.disableEditing();
				for (var i = 0; i < self.cornerPoints.length; i++) {
				    // Marker覆盖层对象
					var marker = new BMap.Marker(self.cornerPoints[i], {
						icon: new BMap.Icon("http://api.map.baidu.com/library/CurveLine/1.5/src/circle.png", new BMap.Size(16, 16)),
						// enableDragging是否可拖拽
						enableDragging: true,
						raiseOnDrag: true
					});
					// marker添加拖拽定制时的事件,重新计算下对应的点坐标
					marker.addEventListener("dragend", function() {
						self.cornerPoints.length = 0;
						for (var i = 0; i < self.editMarkers.length; i++) {
							self.cornerPoints.push(self.editMarkers[i].getPosition())
						}
						var curvePoints = getCurvePoints(self.cornerPoints);
						self.setPath(curvePoints)
					});
					marker.index = i;
					self.editMarkers.push(marker);
					// 将覆盖层添加到地图上
					self.map.addOverlay(marker)
				}
			}
			self.isEditing = true
		};
		polyline.disableEditing = function() {
			this.isEditing = false;
			for (var i = 0; i < this.editMarkers.length; i++) {
				this.map.removeOverlay(this.editMarkers[i]);
				this.editMarkers[i] = null
			}
			this.editMarkers.length = 0
		};
		polyline.getPath = function() {
			return curvePoints
		};
		return polyline
	}
	function extend(child, parent) {
		for (var p in parent) {
			if (parent.hasOwnProperty(p)) {
				child[p] = parent[p]
			}
		}
		return child
	}
	// getCurvePoints利用for循环将每两个点之间的50个点添加到curvePoints中
	function getCurvePoints(points) {
		var curvePoints = [];
		for (var i = 0; i < points.length - 1; i++) {
			var p = getCurveByTwoPoints(points[i], points[i + 1]);
			if (p && p.length > 0) {
				curvePoints = curvePoints.concat(p)
			}
		}
		return curvePoints
	}
	//  getCurveByTwoPoints获取两个点之间的50个点
	function getCurveByTwoPoints(obj1, obj2) {
		if (!obj1 || !obj2 || !(obj1 instanceof BMap.Point) || !(obj2 instanceof BMap.Point)) {
			return null
		}
		// B1 B2 B3使用的是二阶贝塞尔曲线的函数表达式
		// B_{2}(t) = (1 - t)^2P_0 + 2t(1 - t)P_1 + t^2P_2 , t\in[0, 1]
		// P_0为起始点  P_2为终止点  P_1为控制点, 选好的控制点之后,两点连线的曲线就出来了
		var B1 = function(x) {
				return 1 - 2 * x + x * x
			};
		var B2 = function(x) {
				return 2 * x - 2 * x * x
			};
		var B3 = function(x) {
				return x * x
			};
		curveCoordinates = [];
		// count控制两点之间的数量
		var count = 50;
		var isFuture = false;
		var t, h, h2, lat3, lng3, j, t2;
		var LnArray = [];
		var i = 0;
		var inc = 0;
		if (typeof(obj2) == "undefined") {
			if (typeof(curveCoordinates) != "undefined") {
				curveCoordinates = []
			}
			return
		}
		// 将经纬度全部转化为固定格式Float类型的数据
		var lat1 = parseFloat(obj1.lat);
		var lat2 = parseFloat(obj2.lat);
		var lng1 = parseFloat(obj1.lng);
		var lng2 = parseFloat(obj2.lng);
		// 经度为-180 到 180度,这里将经度差取小于180度以内的,表示一个半球之间
		if (lng2 > lng1) {
			if (parseFloat(lng2 - lng1) > 180) {
				if (lng1 < 0) {
					lng1 = parseFloat(180 + 180 + lng1)
				}
			}
		}
		if (lng1 > lng2) {
			if (parseFloat(lng1 - lng2) > 180) {
				if (lng2 < 0) {
					lng2 = parseFloat(180 + 180 + lng2)
				}
			}
		}
		j = 0;
		t2 = 0;
		// t表示两点之间的角度, h表示两点之间的距离
		if (lat2 == lat1) {
			t = 0;
			h = lng1 - lng2
		} else {
			if (lng2 == lng1) {
				t = Math.PI / 2;
				h = lat1 - lat2
			} else {
				t = Math.atan((lat2 - lat1) / (lng2 - lng1));
				h = (lat2 - lat1) / Math.sin(t)
			}
		}
		// t2作用是控制 PI/5 的偏移量,因为经纬度不是1:1的坐标轴,这里不能用严格的中垂线去获取中垂线上的点,这里使用PI/5的偏移量来控制
		// 测试的时候最大用到 PI/4 才能使得曲线不变形,可以在0 - PI/4来控制弧度
		if (t2 == 0) {
			t2 = (t + (Math.PI / 5))
		}
		h2 = h / 2;
		// 计算控制点的经纬度,由起始点添加偏移量
		lng3 = h2 * Math.cos(t2) + lng1;
		lat3 = h2 * Math.sin(t2) + lat1;
		// 计算曲线上的点
		for (i = 0; i < count + 1; i++) {
			curveCoordinates.push(new BMap.Point(
        (lng1 * B1(inc) + lng3 * B2(inc)) + lng2 * B3(inc), 
        (lat1 * B1(inc) + lat3 * B2(inc) + lat2 * B3(inc))
        ));
			inc = inc + (1 / count)
		}
		return curveCoordinates
	}
})();

覆盖物的的基本开发: 1:创建基本类 2:创建覆盖物对象 3:将覆盖物对象添加到新建的map对象中

// 创建基本图片类,控制图片的大小,地址等基本参数
      const Icon = new BMap.Icon("https://pic.c-ctrip.com/AssetCatalog/hybird/checkin/mart_point.png", new BMap.Size(20, 20)); 
      Icon.setImageSize(new BMap.Size(20, 20));
      // 创建覆盖物对象,申明位置信息,以及覆盖物作用信息
      const startMarker = new BMap.Marker(startPosition, {icon: Icon, enableMassClear: false}); 
      const endMarker = new BMap.Marker(endPosition, {icon: Icon, enableMassClear: false}); 
      // 将覆盖物对象添加到map对象中即可
      map.addOverlay(startMarker);
      map.addOverlay(endMarker);

百度地图api3.0类:lbsyun.baidu.com/cms/jsapi/r…

补充说明: 项目里面使用到的坐标系是GPS 坐标系,传入的也是经纬度的值,但是经纬度的值运用到手机上去计算手机像素点里面不能准确的获取到两点之间中垂线上的点,所以这里将它重新处理以上,使用pointToPixel和pixelToPoint将经纬度的坐标轴转化为像素轴,从而精确的获取到中垂线上的点,相关改动点如下:

		const pointArr1 = this.map.pointToPixel(obj1)
		const pointArr2 = this.map.pointToPixel(obj2)

		var lat1 = parseFloat(pointArr1.y);
		var lat2 = parseFloat(pointArr2.y);
		var lng1 = parseFloat(pointArr1.x);
		var lng2 = parseFloat(pointArr2.x);
		
		
	    h2 = h / 16;

		lng3 = (lng1+lng2)/2 - h2 * size / (Math.sqrt(Math.pow( ((lng2-lng1) / (lat2-lat1)) , 2) + 1))
		lat3 = (lat1+lat2)/2 - h2 * size / (Math.sqrt(Math.pow( ((lat2-lat1) / (lng2-lng1)) , 2) + 1))

		for (i = 0; i < count + 1; i++) {
			curveCoordinates.push(this.map.pixelToPoint({
				x: (lng1 * B1(inc) + lng3 * B2(inc)) + lng2 * B3(inc),
				y: (lat1 * B1(inc) + lat3 * B2(inc) + lat2 * B3(inc))
			}));
			inc = inc + (1 / count)
		}
		return curveCoordinates

最后改动后的多条二阶贝塞尔曲线绘制明显。