大家在做交通相关业务时,不可避免的要用到轨迹回放的相关功能,即根据一段时间内的GPS轨迹点来绘制车辆行驶的轨迹,下面结合自己做所项目相关业务以及自己的相关总结,说说轨迹回放相关功能实现。
setTimeOut或者setInterval实现轨迹回放
用settimeout或者setInterval实现轨迹回放,本质都是一样,即用定时器不断的去不间断的画线,话不多说,直接上代码
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
body, html,#allmap {width: 100%;height: 100%;overflow: hidden;margin:0;font-family:"微软雅黑";}
</style>
<script type="text/javascript" src="//api.map.baidu.com/api?v=2.0&ak=uB0gDFSUDZsnp5hSfQyGcAHA"></script>
<title>地图展示</title>
</head>
<body>
<div id="controller" align="center">
<span>倍数:</span>
<input id="speed" type="text" value="1"/>
<input id="play" type="button" value="播放" onclick="startTrack()" />
<input id="pause" type="button" value="暂停" onclick="pause()" disabled/>
<input id="reset" type="button" value="重置" onclick="reset()" />
</div>
<div id="allmap"></div>
</body>
</html>
<script type="text/javascript">
//构造坐标点
var pointArrs=[new BMap.Point(117.175051,31.857091), new BMap.Point(117.204803,31.860097),
new BMap.Point(117.204982,31.860005), new BMap.Point(117.215367,31.861967),
new BMap.Point(117.229416,31.861293),new BMap.Point(117.237357,31.858808),new BMap.Point(117.262043,31.860863)
,new BMap.Point(117.271601,31.864145),new BMap.Point(117.282021,31.867181),new BMap.Point(117.295999,31.867211)];
//新建索引
var index=0;
var points=[];
var mkr;//小车图标
var ST;
var map;
initMap()
function initMap(){
// 百度地图API功能
map = new BMap.Map("allmap"); // 创建Map实例
map.centerAndZoom(new BMap.Point(117.174656, 31.847368), 15); // 初始化地图,设置中心点坐标和地图级别
//添加地图类型控件
map.addControl(new BMap.MapTypeControl({
mapTypes:[
BMAP_NORMAL_MAP,
BMAP_HYBRID_MAP
]}));
map.setCurrentCity("合肥"); // 设置地图显示的城市 此项是必须设置的
map.enableScrollWheelZoom(true); //开启鼠标滚轮缩放
}
function startTrack(){
document.getElementById('pause').disabled=false
//获取倍数
var sp=document.getElementById('speed').value;
if (index == pointArrs.length) return;
//清除上一个车辆图标
map.removeOverlay(mkr);
//新建车辆图标
mkr = new BMap.Marker(pointArrs[index],{
icon:new BMap.Icon('bus.png',new BMap.Size(48, 48))
});
map.addOverlay(mkr);
var label = new BMap.Label("经度:"+pointArrs[index].lng+"<br/>纬度:"+pointArrs[index].lat, {
offset: new BMap.Size(20, -25),
position:pointArrs[index]
});
mkr.setLabel(label);
//相邻两点绘制线
if(index>0){
var polyline=new BMap.Polyline([pointArrs[index], pointArrs[index-1]],{
strokeColor: "red",
strokeWeight: 5,
strokeOpacity: 1
})
map.addOverlay(polyline);
}
//位置跟随
map.panTo(pointArrs[index]);
index++;
//递归调用
ST=setTimeout(startTrack,1000/sp)
}
function pause() {
document.getElementById('pause').disabled=true
document.getElementById('reset').disabled=false
if(ST){
clearTimeout(ST)
}
}
function reset() {
document.getElementById('reset').disabled=true
document.getElementById('play').disabled=false
document.getElementById('pause').disabled=true
if(ST){
clearTimeout(ST)
//清除覆盖物
map.clearOverlays();
}
index=0;
//新建车辆图标
mkr = new BMap.Marker(pointArrs[index],{
icon:new BMap.Icon('bus.png',new BMap.Size(48, 48))
});
map.addOverlay(mkr);
//位置跟随
map.panTo(pointArrs[index]);
}
</script>
效果图如下
利用百度地图路书功能方法
这种用定时器原理绘制的轨迹回放,虽然也能够满足基本需求,但是用户体验很不好,很明显看到轨迹回放不平滑,不自然,下面利用 路书 功能实现轨迹回放平滑功能。先看代码
<html lang="en">
<head>
<meta charset="utf-8" />
<title>路书</title>
<style type="text/css">
body, html{width: 100%;height: 100%;margin:0;font-family:"微软雅黑";}
#map_canvas{width:100%;height:500px;}
#result {width:100%}
</style>
<script src="//api.map.baidu.com/api?v=2.0&ak=K5HzoiMFpRG12UTerDkljIVWyNP3Bo8g"></script>
<script type="text/javascript" src="Lushu.js"></script>
</head>
<body>
<div id="map_canvas"></div>
<div id="result"></div>
<button id="run">开始</button>
<button id="stop">停止</button>
<button id="pause">暂停</button>
<button id="hide">隐藏信息窗口</button>
<button id="show">展示信息窗口</button>
<script>
var map = new BMap.Map('map_canvas');
map.enableScrollWheelZoom();
map.centerAndZoom(new BMap.Point(117.174656, 31.847368), 15);
var pointArrs=[new BMap.Point(117.175051,31.857091), new BMap.Point(117.204803,31.860097),
new BMap.Point(117.204982,31.860005), new BMap.Point(117.215367,31.861967),
new BMap.Point(117.229416,31.861293),new BMap.Point(117.237357,31.858808),new BMap.Point(117.262043,31.860863)
,new BMap.Point(117.271601,31.864145),new BMap.Point(117.282021,31.867181),new BMap.Point(117.295999,31.867211)];
map.addOverlay(new BMap.Polyline(pointArrs, {strokeColor: '#802c10'}));
map.setViewport(pointArrs);
lushu = new BMapLib.LuShu(map,pointArrs, {
defaultContent: "皖A123456",//"从天安门到百度大厦"
autoView: true,//是否开启自动视野调整,如果开启那么路书在运动过程中会根据视野自动调整
icon: new BMap.Icon('bus.png', new BMap.Size(48, 48)),
speed: 500,
enableRotation: true,//是否设置marker随着道路的走向进行旋转
landmarkPois: [
{lng:117.204803,lat:31.860097,html:'天柱路站站到了',pauseTime:2},
{lng:117.215367,lat:31.861967,html:'科学大道站到了',pauseTime:3}
]
});
//绑定事件
$("run").onclick = function(){
debugger
lushu.start();
}
$("stop").onclick = function(){
lushu.stop();
}
$("pause").onclick = function(){
lushu.pause();
}
$("hide").onclick = function(){
lushu.hideInfoWindow();
}
$("show").onclick = function(){
lushu.showInfoWindow();
}
function $(element){
return document.getElementById(element);
}
</script>
</body>
</html>
效果图如下:
我们可以看到,利用路书功能,小车一定会变得很平滑,而且接口相对调用简单,只要传入map对象,路线path,以及相关参数就可以让小车沿着路线平滑的移动,我们还惊喜的发现,Lushu.js还是开源的,既然是开源的,那我们就可以通过源码来了解一下他的实现原理,有兴趣的可以可以下载看一下,这里我们说一下他的主要原理
- 将传来的path点数组进行线路分段
- 对一段path再进行分段,通过move方法,让小车匀速在一段path里面匀速运动(通过设置path长度,步长,以及小车的速度,角度,利用setInerval方法实现)
- 利用movenext方法对path数组进行遍历操作,从而实现整个线路的匀速运动
既然知道了原理,那么我们便可以DIY自己的功能,比如我需要小车边走边画,我需要走的时候速度不一样,我需要5辆小车同时运动,下面就结合路书源码,改一下相关功能,主要实现边走边画,以及多辆小车并行
利用高德地图Marker.moveAlong方法
这个就不用多说了,依赖于高德接口,而是封装的,大家有兴趣看一下看一下官方例子:https://lbs.amap.com/api/javascript-api/example/marker/replaying-historical-running-data
基于openlayers利用postcompose事件,即不断监听地图的drawFeature,这样就形成了类似定时器的效果,如图
当然,你会发现当两点之间的距离比较大时,就会出现跳帧现象,因此,我们可以用百度路书的思路来改造算法,附上主要代码
var move=function (event) {
var initPos=[routeCoords[i][0],routeCoords[i][1]];
var targetPos=[routeCoords[i+1][0],routeCoords[i+1][1]];
//当前的帧数
var speed = speedInput.value;
speed=400;
//步长,米/秒
var timer = 10,
step = speed / (1000 / timer),
//总的步长
count = Math.round(getDistance(initPos, targetPos) / step);
var tempp;
var vecsouce=vectorLayer.getSource();
//如果小于1直接移动到下一点
if (count < 1) {
move(++i);
return;
}
else{
flag=setInterval(function () {
if(currentCount >= count){
currentCount=0;
clearInterval(flag);
move(++i);
}
else{
currentCount++;
debugger
console.log(currentCount)
if(currentCount<count){
var x = effect(initPos[0], targetPos[0], currentCount, count);
var y = effect(initPos[1], targetPos[1], currentCount, count);
//画线
if(currentCount>1){
var line=new ol.Feature({
geometry: new ol.geom.LineString([tempp,[x,y]])
})
line.setStyle(new ol.style.Style({
stroke: new ol.style.Stroke({
width: 8,
color:'red'
})
}))
vecsouce.addFeature(line)
}
if(feature){
vecsouce.removeFeature(feature)
}
feature = new ol.Feature(new ol.geom.Point([x,y]));
feature.setStyle(styles.geoMarker)
vecsouce.addFeature(feature)
tempp=[x,y]
//vectorContext.drawFeature(feature, styles.geoMarker);
}
}
},timer)
}
};
效果如下:
总结:
上述的在多种轨迹回放中,除了用postcompose事件外,其余实现的方法本质都是一样的,即当点数比较少时,通过不断分割线段加上setInterval方法实现观感上的匀速运动。
以上所有代码均已上传github上面,地址为https://github.com/wengjidong/GISTrace.git,欢迎大家多留言,多点赞,多批评指正,当然由于时间仓促,主要为了演示思想,代码主要写了运动部分,其余部分还没时间写,希望大家下载后能够不断完善,一起进步。