突然来了个新需求,产品说要在地图上加一个测距功能。经历一定波折后,最终还是圆满实现了。 今天我将基于百度地图,用两种方法实现测距功能。
效果大致如下图。这是从 百度地图官网上看到的,可以看出百度地图其实是自带测距功能的,只是需要自己调用它。
一、引入百度地图
1.1获得ak
在一切的开始之前,要有百度地图的AK密钥。具体可参考官网的 文档,此处不做过多赘述。
1.2vue项目中引入
创建一个vue项目,建立一个ceju.vue文件。在template中加入下面的代码: <div class="map" id="container"></div>。要给这个div的样式上增加高度和宽度。根据 教程,在mounted中写入以下的代码:
let map = new BMapGL.Map("container");
let point = new BMapGL.Point(116.404, 39.915);
map.centerAndZoom(point, 15);
this.map = map;
在public的index.html文件下,加入这样一行代码<script type="text/javascript" src="https://api.map.baidu.com/api?v=1.0&&type=webgl&ak=你的ak"></script>
在这里需要注意的是,我引入的地图版本是GL,百度地图还有其他版本。引入的代码也是不同的。当然之后new BMapGL.Map这行代码也要进行相应的修改。如果想要使用bmap-draw库的话,要用GL版本的。
JavaScript API v3.0:<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=您的密钥"></script>
JavaScript API Lite v1.0:<script type="text/javascript" src="//api.map.baidu.com/api?ak=您的密钥&type=lite&v=1.0"></script>
启动服务后,可以看到地图,代表我们第一步完成了。
二、基于bmap-draw库的测距方法实现功能
2.1关于bmap-draw
BMap Draw 是一个基于百度地图 JSAPI 的轻量级鼠标绘制库,提供了鼠标绘制、编辑、裁切、合并、复制黏贴、移动、测量等多种几何图形操作能力,助力开发者开箱即用式快速实现自己的几何图形编辑器。如下图:
我们想要的测距就在其中。
2.2引入
根据 教程操作。通过npm引入。
npm install bmap-draw
引入距离测绘类 DistanceMeasure,根据 文档 操作。此外也可以参考距离测绘类来写。官网的教程是用react写的,我们使用vue,需要进行一定的修改。
引入测距。
import { DrawScene, DistanceMeasure } from "bmap-draw";
画两个按钮,绑定两个事件。
<button @click="openMeasure">开始测距</button>
<button @click="closeMeasure">关闭测距</button>
再写两个方法。开始测距的方法。
openMeasure() {
const scene = new DrawScene(this.map);
const distance = new DistanceMeasure(scene, {
isSeries: false, // 不连续测量
// unit: 'metric'
});
this.distance = distance
// 监听测量结果
distance.addEventListener("measure-length-end", (e) => {
console.log("measure-end", e);
});
},
结束测距的方法:
closeMeasure() {
this.distance.close()
},
效果如下,感觉还可以。
关闭测距功能是这样的,一般来说,你可以双击地图关闭测距,你也可以不双击地图,直接点关测距,缺点是线段结束的地方没有圆点,没有总和长度。如下图。
三、自己写一个测距功能
还有一种方法是自己写,这里主要用的是getDistance(start: Point , end: Point )这个方法,返回的结果是两点之间的距离,单位为米。自己画的话,最好有个UI,因为那些红圈啊,红叉啊,得有个图,比较方便,不然的话就是svg。我也只是做了一个很粗糙的,勉强实现功能,要真的说效果的话,还是百度的方法比较好。
具体思路: 开始测距后,监听地图的单击事件,得到point,然后绘制一个圆点marker,绘制折线,计算每两个点的距离并绘制label,监听双击事件,结束绘制,并移除地图的监听事件。这里有个问题是同时监听单击事件和双击事件的情况下,在地图上双击,也会触发两次单击,这样地图上的点就会多,我这里就不监听 dblclick了,两次单击间隔不超过某个时间段,就算是双击了。主要是三个方法:
openMeasure:开始测距点击事件。
openMeasure() {
this.map.addEventListener("click", this.mapClick);
},
closeMeasure:关闭测距点击事件。
closeMeasure() {
this.map.removeEventListener("click");
this.totalDistance = 0;
},
mapClick:地图点击事件。
mapClick(e) {
const timestamp = new Date().getTime();
this.timeList.push(timestamp);
let point = e.latlng;
if (
this.timeList.length > 1 &&
timestamp - this.timeList[this.timeList.length - 2] < 250
) {
let icon = new BMapGL.Icon(this.closeUrl, new BMapGL.Size(12, 12), {
imageOffset: new BMapGL.Size(0, 0),
});
let endMarker = new BMapGL.Marker(point, { icon });
endMarker.addEventListener("click", (e) => {
this.map.clearOverlays();
});
let label = new BMapGL.Label(`总长:${this.totalDistance}米`, {
offset: new BMapGL.Size(-45, -25),
});
endMarker.setLabel(label);
this.map.addOverlay(endMarker);
this.closeMeasure();
return;
}
this.pointList.push(point);
let icon = new BMapGL.Icon(this.circleUrl, new BMapGL.Size(12, 12), {
imageOffset: new BMapGL.Size(0, 0),
});
let marker = new BMapGL.Marker(point, { icon });
if (this.pointList.length > 1) {
let end = this.pointList.length;
let list = [this.pointList[end - 1], this.pointList[end - 2]];
let line = new BMapGL.Polyline(list, {
strokeColor: "#FF0E0E",
strokeWeight: 2,
strokeOpacity: 0.5,
});
this.map.addOverlay(line);
let distance = this.map.getDistance(...list);
this.totalDistance += distance;
let label = new BMapGL.Label(`${distance}米`);
marker.setLabel(label);
}
this.map.addOverlay(marker);
},
大概效果如下: