关于地图轨迹回放的一点小研究

大家在做交通相关业务时,不可避免的要用到轨迹回放的相关功能,即根据一段时间内的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,欢迎大家多留言,多点赞,多批评指正,当然由于时间仓促,主要为了演示思想,代码主要写了运动部分,其余部分还没时间写,希望大家下载后能够不断完善,一起进步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值