百度地图路书(BMapLib.LuShu) - 移动到指定途经点暂停

一好友发过来了一个问题,她们的一个系统中,需要使用百度地图的路书功能来绘制整个飞机的航线以及动态过程,所以,就参考了百度地图中的一个Demo,

Demo地址:https://lbsyun.baidu.com/jsdemo.htm#webgl1_7

官方Demo示例

百度地图的集成使用,这里就不过多的说了,列一下主要的代码,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//引入依赖库
<script type="text/javascript" src="//api.map.baidu.com/api?type=webgl&v=1.0&ak=您的密钥"></script>
<script type="text/javascript" src="//api.map.baidu.com/library/LuShu/gl/src/LuShu_min.js"></script>

//地图显示容器
<div id="allmap"></div>

//创建地图并设置中心点和级别
var map = new BMapGL.Map("allmap");
var point = new BMapGL.Point(116.404, 39.925);
map.centerAndZoom(point, 4);
map.enableScrollWheelZoom();

//显示路径
var path = [
new BMapGL.Point(116.617562,40.0823),
new BMapGL.Point(37.700058,55.850864)
];
var polyline = new BMapGL.Polyline(path, {
clip: false,
geodesic: true,
strokeWeight: 3
});
map.addOverlay(polyline);

//创建路书
var fly = "小飞机图标"
lushu = new BMapGLLib.LuShu(map, polyline.getPath(), {
geodesic: true,
autoCenter: true,
icon: new BMapGL.Icon(fly, new BMapGL.Size(48, 48), { anchor: new BMapGL.Size(24, 24) }),
speed: 1000000,
enableRotation: true
});

//开启运动
lushu.start()


接着直接看问题

官方的示例可以看到,设置好起始和终止坐标后,然后使用LuShu的相关api就可以展示行进的整个过程。
而她们希望是行进到其中的某个途经点时停止到这里。

她们查阅官方文档,并试验过的方法

BMapLib.LuShu的类参考文档:http://api.map.baidu.com/library/LuShu/1.2/docs/symbols/BMapLib.LuShu.html

官方文档说明

可以根据BMapLib.LuShu的构造参数中的landmarkPois来实现途经点的临时驻留。咱们就用官方的这个Demo的运行时来做一些代码上的验证。

0x00 验证官方的landmarkPois功能

首先,咱们在官方的示例中添加一个途经点,比如,路过一下哈萨克斯坦吧:)

1
2
3
4
5
6
var path = [
new BMapGL.Point(116.617562,40.0823),
//新加一个途经点,根据起始结束坐标,估算一个差不多的就行,做测试
new BMapGL.Point(70.273823,48.0273),
new BMapGL.Point(37.700058,55.850864)
];

接着,配置landmarkPois信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//添加地标信息,与path中新加的路径点一致,方便测试
lushu = new BMapGLLib.LuShu(map, polyline.getPath(), {
geodesic: true,
autoCenter: true,
icon: new BMapGL.Icon(fly, new BMapGL.Size(48, 48), { anchor: new BMapGL.Size(24, 24) }),
speed: 1000000,
enableRotation: true,
landmarkPois: [{
lng: 70.273823,
lat: 48.0273,
html: '停止点',
pauseTime: 10
}]
});

点击上方的【运行】则可以看到右侧的小飞机已经开始移动,并停留在我们设置的地标上,会停留10s后继续行进。

新加的途经点

0x01 怎么让行进到地标后直接停止就不动了

按照程序的设计思维,停留时间参数的定义一般会是:

  • 0 或者 null: 默认,表示不停留
  • 正数:表示停留的时长
  • -1: 表示长时间驻留,不走了

由于javascript中,0和null在逻辑运算中都是false,所以建议放一起,仅作为假设。
js中的null和0

我们可以将landmarkPois参数中的pauseTime: 10修改为 pauseTime: 0pauseTime: -1等各种情况来测试,发现都会不停留直接过去了,而官方文档中也没有明确的说明取值范围等。

背景交代完毕,哈哈哈哈,如果只是跟大家讨论百度地图怎么集成,那纯属浪费你们时间,随便搜索一下大把大把的资料。而我主要想说的就是,如果是你,作为开发人员,碰到这个问题该怎么做?

  • 利用各种搜索引擎,变换关键词进行搜索,总有人做过相似的,抄还不会么。
  • 找人询问,不行就各种技术群里、论坛发文章,总有人做过相似的,一说一听不爽么。
  • ???

最近面试了不少的前端开发人员,可以说是跟着应聘者一起着急上火,感觉都是将一天重复了N年,咱不排斥混口饭,毕竟大家都是混口饭,但是,技术深度以及解决问题的能力是一个职业人应有的基本素养。会用几个框架组件又有什么用呢,碰到百度不到的问题就歇菜了么?实属不应该,说的有些偏题了,书归正文。

解决一个问题,就跟解答一到数学题是一样的,要先分析已知条件。

首先,我们要在脑中对BMapLib.LuShu的功能进行建模,比如,通过设置的landmarkPois参数可以让行进到途经点后进行指定时间的驻留,那么,接着就是,分析一下这个驻留是如何实现的。

找到百度地图的路书源代码:http://api.map.baidu.com/library/LuShu/1.2/src/LuShu.js

短短代码,只有600来行,通过关键词搜索,可以找到这样一个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 否经过某个点的index
* @param {Point} markerPoi 当前小车的坐标点.
* @return 无返回值.
*/
_troughPointIndex: function(markerPoi) {
//获取构造new BMapGLLib.LuShu()中设置的 landmarkPois 数据,并赋值给t
var t = this._opts.landmarkPois, distance;

//遍历设置的地标数据
for (var i = 0, len = t.length; i < len; i++) {
//landmarkPois中的点没有出现过的话
if (!t[i].bShow) {
distance = this._map.getDistance(new BMap.Point(t[i].lng, t[i].lat), markerPoi);
//两点距离小于10米,认为是同一个点
if (distance < 10) {
t[i].bShow = true;
//如果判定为该地标,则返回当前索引
return i;
}
}
}
return -1;
}

查看 _troughPointIndex 函数的调用历史,或者搜索关键词,看在哪个逻辑中调用了这个函数来获取地标索引。可以找到这样一个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 设置小车上方infowindow的内容,位置等
* @param {Point} pos 经纬度坐标点.
* @return 无返回值.
*/
_setInfoWin: function(pos) {
//设置上方overlay的position
var me = this;
if(!me._overlay){
return;
}
me._overlay.setPosition(pos, me._marker.getIcon().size);
//获取pos经纬度是否处于地标列表中,如果!=-1 表示存在设置中
var index = me._troughPointIndex(pos);
if (index != -1) {
clearInterval(me._intervalFlag);
me._overlay.setHtml(me._opts.landmarkPois[index].html);
me._overlay.setPosition(pos, me._marker.getIcon().size);
//暂停视图,字面理解,这里是要进行暂停了,跟进函数
me._pauseForView(index);
}else {
me._overlay.setHtml(me._opts.defaultContent);
}
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 在某个点暂停的时间
* @param {Number} index 点的索引.
* @return 无返回值.
*/
_pauseForView: function(index) {
var me = this;
//获取定时器指针
//将设置的地标数据中的pauseTime * 1000,如果设置的是10,则10 * 1000 换算为秒
var t = setTimeout(function() {
//运行下一个点
me._moveNext(++me.i);
},me._opts.landmarkPois[index].pauseTime * 1000);

me._setTimeoutQuene.push(t);
},

也可以接着 _setInfoWin函数向上跟踪,查看被调用链,是可以跟到 _move 函数的,这个是整个路书移动的核心函数,这里就不详细介绍了,我们主要是实现长时间停留。

通过_pauseForView函数的逻辑,可以看出来,主要使用setTimeout来做了个延时,由于时间计算并必填了,所以,我们设置0-1也都不会生效。

那么,我们可以修改这里的代码逻辑,来实现一个pauseTime: -1则进行长时间停留。

如何长时间停留呢?查看官方文档,可以看到有这样一个函数

pause()

如果pauseTime: -1则调用pause()来暂停住,就可以实现长时间驻留,反之,则继续走原有的逻辑。

思路和方法都有了,怎么实现呢?改源码再打包确实有些累,在js语言中,为什么会要求大家必须理解原型的概念,在这里,我们就可以利用原型的特性来完成函数的覆盖。

在官方的示例中,通过原型覆盖_pauseForView函数,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//覆盖函数
BMapGLLib.LuShu.prototype._pauseForView = function(index){
console.log("到达经停点")

var me = this;
//获取途经点信息
const landmarkItem = me._opts.landmarkPois[index]

//获取途经点设置的暂停时间
const pauseTime = landmarkItem.pauseTime

//添加长时间驻留,如果是-1,则相当于暂停
if(pauseTime == -1){
this.pause()
}else{
//驻留指定时间,原函数逻辑
var t = setTimeout(function() {
//运行下一个点
me._moveNext(++me.i);
},pauseTime * 1000);
me._setTimeoutQuene.push(t);
}
}

//路书设置,pauseTime设置为-1表示到了就暂停
lushu = new BMapGLLib.LuShu(map, polyline.getPath(), {
geodesic: true,
autoCenter: true,
icon: new BMapGL.Icon(fly, new BMapGL.Size(48, 48), { anchor: new BMapGL.Size(24, 24) }),
speed: 1000000,
enableRotation: true,
landmarkPois: [{
lng: 70.273823,
lat: 48.0273,
html: '停止点',
pauseTime: -1
}]
});

运行效果如图
代码及运行效果

代码只是示例验证代码,实际应用还得根据业务场景进行封装,这里之所以想说这个事儿,主要也是为你我做个警示,不要总是沉浸于会用多少框架,解决不了基础的问题,难免就会吐槽别人框架都是坑,而实际是自己没用好。