關於地圖軌跡回放的一點小研究

大家在做交通相關業務時,不可避免的要用到軌跡回放的相關功能,即根據一段時間內的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,歡迎大家多留言,多點贊,多批評指正,當然由於時間倉促,主要爲了演示思想,代碼主要寫了運動部分,其餘部分還沒時間寫,希望大家下載後能夠不斷完善,一起進步。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章