在vue项目中使用百度地图,添加标记物、位置纠偏、信息弹窗、画轨迹,坐标转换,逆地址解析
参考vue-baidu-map开发文档
安装npm install vue-baidu-map --save,全局注册,在main.js中引入以下代码
import BaiduMap from ‘vue-baidu-map’ Vue.use(BaiduMap, { ak: ‘申请的百度地图ak’ })
<div id="allmap"><baidu-map class="bm-view":center="center"@ready="handler"></baidu-map></div><style scoped lang="scss">#allmap {width: 100%;height: 100%;overflow: hidden;position: absolute;}</style>
// 引入自定义图标import icon from '@/assets/image/icon.png'
调用接口获取经纬度数据,初始化地图并添加marker,其中涉及到this指代,逆地址解析,坐标转换(百度地图位置纠偏)等。
调用接口获取经纬度集合,经纬度坐标为百度坐标的不用转换,为GPS坐标等其它坐标类型时需要转换为百度坐标;获取到经纬度集合数据后,进行地图初始化,设置地图中心位置、缩放比例、是否可鼠标滚动缩放,添加标记点等;标记点上添加点击事件并弹窗信息。
getData() {this.locationData = [] //初始化经纬度数组集let self = thislet query = {...... // 接口请求参数}// 调用接口获取经纬度数组集dataList(query).then(res => {this.listData = res.data.listthis.listData.map(row => {if (row.longitude && row.longitude) {let real_point = new BMap.Point(row.longitude, row.latitude);// 将gps经纬度转换成百度地图的经纬度new BMap.Convertor().translate([real_point], 1, 5, function (data) {if (data.status === 0) {self.locationData.push(data.points[0]);}})}})// 设置延迟,待数据获取到后初始化地图,handler方法在下面定义this.$nextTick(() => {setTimeout(() => {self.handler(self.locationData)}, 400);});}).catch(error => {console.log(error)})},// 地图初始化handler(locationData) {let self = thisvar map = new BMap.Map("allmap", {enableMapClick: false});// 设置中心位置点let point = new BMap.Point(116.404, 39.915);map.centerAndZoom(point, 12);// 地图比例尺map.addControl(new BMap.ScaleControl({anchor: BMAP_ANCHOR_BOTTOM_RIGHT}));// 鼠标滑动缩放map.enableScrollWheelZoom(true);this.$nextTick(() => {setTimeout(() => {map.setViewport(locationData);// 调用添加标记物方法self.addMarker(locationData, map);}, 100);});},// 向地图添加经纬度位置点标记物addMarker(pointArray, map) {let self = thislet marker;for (var i = 0; i < pointArray.length; i++) {// 自定义图标let icon = new BMap.Icon(icon, new BMap.Size(26, 45), {imageOffset: new BMap.Size(-25 - 17, 0),anchor: new BMap.Size(13, 40)});marker = new BMap.Marker(pointArray[i], {icon: icon});map.addOverlay(marker);// 设置标记物上的文字marker.setLabel(new BMap.Label('标记点说明'));// 调用点击标记物显示信息弹窗this.setInfo(map, marker, pointArray[i])}},// 设置点击地图标记点信息弹窗的内容 self指代window,避免与地图fuc的this混淆setInfo(map, marker, pt) {let self = thisvar opts = {width: 170, // 信息窗口宽度height: 170, // 信息窗口高度title: "位置信息", // 信息窗口标题}marker.addEventListener("click", function (e) {// 逆地址解析-根据经纬度获取中文地址self.getAddress(pt)// 设置延迟,等获取到中文地址再显示弹窗信息self.$nextTick(() => {setTimeout(() => {str = "经度:" + pt.lng + "<br>纬度:" + pt.lat + "<br>地址:" + self.addresslet infoWindow = new BMap.InfoWindow(str, opts); // 创建信息窗口对象 map.openInfoWindow(infoWindow, pt); //开启信息窗口}, 200);});});},// 逆地址解析-根据经纬度获取中文地址getAddress(point) {let self = thislet geoc = new BMap.Geocoder({extensions_town: true});// 根据坐标得到地址描述 geoc.getLocation(new BMap.Point(point.lng, point.lat), function (result) {if (result) {self.address = result.address}});}
定时调用接口画实时运动轨迹,如果是历史轨迹就稍微简单点。同时注意跟添加标记物一样,要注意经纬度坐标是不是百度地图坐标,不是则需要进行转换,我的经纬度坐标是GPS坐标,转换的地方有注释。
起点位置,调用一次接口获取起点并进行标记;设置定时器调用接口获取下一个经纬度坐标,定时器设置时长根据后端实时获取到的数据进行设置,要比后端时间稍长,同时要处理后端没有获取到经纬度或者无效经纬度的情况(比如用前一个点代替);有2个经纬度坐标就可以进行划线;设置当前位置的marker,为矢量小车图标,同时根据当前划线的角度纠正小车图标头部朝向前进方向,不需要区分方向的图标则不用进行角度处理;页面关闭或者长时间不操作页面时注意定时器的清除。
handleMap(row) {this.infoData = rowclearInterval(this.timer);let self = thisself.lineArray = []var map = new BMap.Map("allmap", {enableMapClick: false});// 先调用一次获取到初始的起点,再启动定时器,避免初始化时没有图标getData(row.id).then(res => {if (res.status === 200) {if (res.data != '' && res.data.longitude && res.data.latitude) {longLatitude(res.data.gps)self.center = {lng: res.data.longitude,lat: res.data.latitude}let point = new BMap.Point(self.center.lng, self.center.lat);map.centerAndZoom(point, 14);// 画轨迹self.addLine(map)self.timer = setInterval(() => {self.getData(row.id)}, 2000)} else {self.center = {lng: row.longitude,lat: row.latitude,}let real_point = new BMap.Point(self.center.lng, self.center.lat);new BMap.Convertor().translate([real_point], 1, 5, function (data) {if (data.status === 0) {point.push(data.points[0])map.centerAndZoom(data.points[0], 12);self.setCarIcon(map, row, data.points[0]);}})}}}).catch(error => {console.log(error)})map.enableScrollWheelZoom(true);map.addControl(new BMap.ScaleControl({anchor: BMAP_ANCHOR_BOTTOM_RIGHT}));},// 获取经纬度数据getData(id) {let self = thisvehicleStatus(id).then(res => {if (res.status === 200) {self.center = {lng: res.data.longitude,lat: res.data.latitude}let real_point = new BMap.Point(self.center.lng, self.center.lat);new BMap.Convertor().translate([real_point], 1, 5, function (data) {if (data.status === 0) {self.lineArray.push(data.points[0]);}})}}).catch(error => {console.log(error)})}// 划线addLine(map) {let self = thisvar PointArr = []let real_point = new BMap.Point(self.center.lng, self.center.lat);new BMap.Convertor().translate([real_point], 1, 5, function (data) {if (data.status === 0) {self.lineArray.push(data.points[0]);addStartMarker(new BMap.Point(data.points[0].lng, data.points[0].lat), '起点', map);}})PointArr = self.lineArray// 自定义运动中的显示图片var drivingPoint = new BMap.Icon('../../assets/image/icon.png', new BMap.Size(48, 24), {anchor: new BMap.Size(24, 12),imageSize: new BMap.Size(48, 24)});var i = 0;var interval = setInterval(function () {if (i >= PointArr.length && PointArr.length > 1) {clearInterval(interval);return;}if (PointArr.length > 60) {clearInterval(interval);return;}drowLine(map, PointArr[i], PointArr[i + 1]);map.setCenter(PointArr[i + 1]);i = i + 1;}, 4000);// 划线function drowLine(map, PointArr, PointArrNext) {if (PointArr != undefined && PointArrNext != undefined) {var polyline = new BMap.Polyline([new BMap.Point(PointArr.lng, PointArr.lat),new BMap.Point(PointArrNext.lng, PointArrNext.lat)], {strokeColor: "blue",strokeWeight: 4,strokeOpacity: 0.8});map.addOverlay(polyline);addMarkerEnd(new BMap.Point(PointArrNext.lng, PointArrNext.lat), '行驶图标', map, new BMap.Point(PointArr.lng, PointArr.lat)); // 添加图标} else {addMarkerEnd(new BMap.Point(PointArr.lng, PointArr.lat), '终点', map, new BMap.Point(PointArr.lng, PointArr.lat)); // 添加终点图标}}// 设置起始图标addStartMarker(point, name, mapInit) {if (name == "起点") {window.marker = new BMap.Marker(point, {icon: drivingPoint}); // 创建标注mapInit.addOverlay(window.marker); // 将标注添加到地图中}},// 设置行驶和终点图标let carMk;function addMarkerEnd(point, name, mapInit, prePoint) {if (name == "行驶图标") {if (carMk) {mapInit.removeOverlay(carMk);}carMk = new BMap.Marker(point, {icon: drivingPoint});carMk.setRotation(getAngle(point, prePoint) - 90); // 旋转的角度mapInit.removeOverlay(window.marker) // 将起点图标移除mapInit.addOverlay(carMk);} else {mapInit.removeOverlay(carMk);carMk = new BMap.Marker(point, {icon: drivingPoint});carMk.setRotation(getAngle(point, prePoint) - 90);mapInit.addOverlay(carMk);}}//获得角度的函数function getAngle(n, next) {var retvar w1 = n.lat / 180 * Math.PIvar j1 = n.lng / 180 * Math.PIvar w2 = next.lat / 180 * Math.PIvar j2 = next.lng / 180 * Math.PIret = 4 * Math.pow(Math.sin((w1 - w2) / 2), 2) - Math.pow(Math.sin((j1 - j2) / 2) * (Math.cos(w1) - Math.cos(w2)), 2);ret = Math.sqrt(ret);var temp = Math.sin((j1 - j2) / 2) * (Math.cos(w1) + Math.cos(w2));ret = ret / temp;ret = (Math.atan(ret) / Math.PI * 180) + 180;ret += 90;if (j1 - j2 < 0) {if (w1 - w2 < 0) {ret;} else {ret = -ret + 180;}} else {if (w1 - w2 < 0) {ret = 180 + ret;} else {ret = -ret;}}// 某个点没有经纬度时取上一次的角度if (!isNaN(ret)) {localStorage.setItem('retobj', JSON.stringify(ret))} else {ret = JSON.parse(localStorage.getItem('retobj'));}return ret;}}
tips:坐标转换也可以用coordtransform,使用方法见
/wandergis/coordtransform,使用这个方法时最好使用new BMap.Point(point)处理下,不然有些地方像setCenter(point),逆地址解析时会不起作用。