web技術分享| 【高德地圖】實現自定義的軌跡回放

語言: CN / TW / HK

實現(軌跡回放)方式有兩種:

  • 第一種是使用 JS APIAMap.PolyLine (折線)等圖形配合實現。
  • 第二種是使用 JS APIAMapUI 元件庫 配合使用,利用 PathSimplifier軌跡展示 元件)繪製出行動軌跡。

方案選擇

以上兩種實現方式我們可以根據兩個 因素 來決定哪一種更加適合自己: 節點數量 的多少、 排布的密集度

前者適合節點數量較少,排布比較稀鬆,例如,計程車軌跡回放,計程車行駛速度快,週期上報的時間也會相對較長。後者更加針對節點數量巨大、排布密集的路徑,按秒記錄位置的飛機行進軌跡,精細的地理邊界等等。

實現流程

無論選擇兩種方式,我們都需要先收集到客戶端上報的資訊,這些資訊可以自定義,通常我們會包含: 經緯度、速度、逆編碼之後的地理位置、方向、海拔 等基本地理資訊,同時我們也可以加入一些 自定義 的資訊,例如:人員資訊(頭像暱稱等)、出行資訊(訂單等)。

實現的流程:

  • 客戶端按(時間)週期上報地理資訊以及自定義資訊。
  • 服務端按時間軸儲存客戶上報的資訊。
  • 按(時間等)條件查詢出使用者的軌跡,並通過簡化演算法去除一部分節點(例如,節點距離十分微小、或者多個點都在同一條直線、3點之間,其中一點略有偏差無法繪製成直線等等),最終獲得適合繪製的路徑(陣列)。
  • 根據路徑去繪製使用者的行動軌跡。

路徑簡化演算法(可選)

客戶端上報的資料是按時間週期上報的,也就是說每個時間都對應了一個經緯度,經緯度在地圖上就是一個又一個點,將這些點連線時,我們會得到 N 多條折線,為了繪製的軌跡更加美觀,行動路線更加明確平滑,通常我們需要一個演算法來簡化折線。

例如:

  • A 點和 B 點,兩者距離不到 1 畫素,則可以去掉 B 點,只留 A 點。
  • ABC 三點在一條直線上,或者, B 點僅僅稍微偏離 A 點和 C 點構成的線段,那麼 B 點就可以去掉。

這裡官方也推薦了一種演算法庫 simplify.js 供大家參考,這裡不做過多的闡述。

實現示例

車輛軌跡回放

這裡我們使用第一種方式來實現 - 利用 JS APIAMap.PolyLine

實現原理:

  • 在地圖上繪製 車輛標記AMap.Marker )。
  • 利用 AMap.PolyLine 繪製出兩條軌跡: 歷史軌跡駕駛途徑過的軌跡 ,以顏色區分。
  • 按照一定的速度使車輛前進,並監聽 Maker 移動的事件,在事件回撥中,將車輛( Marker )位置設定為地圖中心點,給使用者視覺主觀上一種車輛在前進的感覺,同時延長 駕駛途徑過的軌跡
  • 對於實現場景比較複雜的,需要進行自定義處理的比如:

    • 檢視每個節點的資料 ,我們可以把每個節點給繪製出來,節點被點選時顯示該節點的資料。
    • 移動倍速播放 ,首先按上報的時間間隔來播放,選擇倍速之後,改變 MarKer 移動的 duration
    • 其他自定義。

自定義 API

我們可以讓車輛:

  • 開始移動
  • 暫停移動
  • 恢復移動
  • 停止移動

程式碼示例

AMap.plugin('AMap.MoveAnimation', function(){
  var marker, lineArr = [[116.478935,39.997761],[116.478939,39.997825],[116.478912,39.998549],[116.478912,39.998549],[116.478998,39.998555],[116.478998,39.998555],[116.479282,39.99856],[116.479658,39.998528],[116.480151,39.998453],[116.480784,39.998302],[116.480784,39.998302],[116.481149,39.998184],[116.481573,39.997997],[116.481863,39.997846],[116.482072,39.997718],[116.482362,39.997718],[116.483633,39.998935],[116.48367,39.998968],[116.484648,39.999861]];

  var map = new AMap.Map("container", {
    resizeEnable: true,
    center: [116.397428, 39.90923],
    zoom: 17
  });

  marker = new AMap.Marker({
    map: map,
    position: [116.478935,39.997761],
    icon: "https://a.amap.com/jsapi_demos/static/demo-center-v2/car.png",
    offset: new AMap.Pixel(-13, -26),
  });

  // 繪製歷史軌跡
  var polyline = new AMap.Polyline({
    map: map,
    path: lineArr,
    showDir:true,
    strokeColor: "#28F",  //線顏色
    // strokeOpacity: 1,     //線透明度
    strokeWeight: 6,      //線寬
    // strokeStyle: "solid"  //線樣式
  });  
    // 駕駛途徑過的軌跡
  var passedPolyline = new AMap.Polyline({
    map: map,
    strokeColor: "#AF5",  //線顏色
    strokeWeight: 6,      //線寬
  });

    // 監聽車輛移動事件
  marker.on('moving', function (e) {
      // 延長駕駛途徑過的軌跡
    passedPolyline.setPath(e.passedPath);
    // 將車輛位置設定為地圖中心點
    map.setCenter(e.target.getPosition(),true)
  });

  map.setFitView();
    
    // 開始移動
  window.startAnimation = function startAnimation () {
    marker.moveAlong(lineArr, {
      // 每一段的時長
      duration: 500,//可根據實際採集時間間隔設定
      // JSAPI2.0 是否延道路自動設定角度在 moveAlong 裡設定
      autoRotation: true,
    });
  };
  // 暫停移動
  window.pauseAnimation = function () {
      marker.pauseMove();
  };
    // 恢復移動
  window.resumeAnimation = function () {
      marker.resumeMove();
  };
    // 停止移動
  window.stopAnimation = function () {
      marker.stopMove();
  };
});

參考連結: https://lbs.amap.com/demo/jsapi-v2/example/marker/replaying-historical-running-data

飛機航班的軌跡回放

使用 JS APIAMapUI 元件庫 配合使用,利用 PathSimplifier軌跡展示 元件)繪製出行動軌跡,這種方案比較簡單,只需要進行一些配置即可,例如說方案一中的倍速播放就需要計算,同時還存在不能動態改變倍速的弊端,但是方案二卻不會存在。

實現原理:

  • 在地圖上繪製 飛機標記AMap.Marker )。
  • 利用 AMap.PolyLine 繪製出兩條軌跡: 歷史軌跡駕駛途徑過的軌跡 ,以顏色區分。
  • 配置軌跡的顏色,動畫的速度等等。
  • 對於實現場景比較複雜的,需要進行自定義處理的,可以在 PathSimplifier 提供的回撥中進行配置及處理。

示例程式碼

//載入PathSimplifier,loadUI的路徑引數為模組名中 'ui/' 之後的部分 
AMapUI.load(['ui/misc/PathSimplifier'], function(PathSimplifier) {

    if (!PathSimplifier.supportCanvas) {
        alert('當前環境不支援 Canvas!');
        return;
    }

    //啟動頁面
    initPage(PathSimplifier);
});

function initPage(PathSimplifier) {
    //建立元件例項
    var pathSimplifierIns = new PathSimplifier({
        zIndex: 100,
        map: map, //所屬的地圖例項
        getPath: function(pathData, pathIndex) {
            //返回軌跡資料中的節點座標資訊,[AMap.LngLat, AMap.LngLat...] 或者 [[lng|number,lat|number],...]
            return pathData.path;
        },
        getHoverTitle: function(pathData, pathIndex, pointIndex) {
            //返回滑鼠懸停時顯示的資訊
            if (pointIndex >= 0) {
                //滑鼠懸停在某個軌跡節點上
                return pathData.name + ',點:' + pointIndex + '/' + pathData.path.length;
            }
            //滑鼠懸停在節點之間的連線上
            return pathData.name + ',點數量' + pathData.path.length;
        },
        renderOptions: {
            //軌跡線的樣式
            pathLineStyle: {
                strokeStyle: 'red',
                lineWidth: 6,
                dirArrowStyle: true
            }
        }
    });

    //這裡構建兩條簡單的軌跡,僅作示例
    pathSimplifierIns.setData([{
        name: '軌跡0',
        path: [
            [100.340417, 27.376994],
            [108.426354, 37.827452],
            [113.392174, 31.208439],
            [124.905846, 42.232876]
        ]
    }, {
        name: '大地線',
        //建立一條包括500個插值點的大地線
        path: PathSimplifier.getGeodesicPath([116.405289, 39.904987], [87.61792, 43.793308], 500)
    }]);

    //建立一個巡航器
    var navg0 = pathSimplifierIns.createPathNavigator(0, //關聯第1條軌跡
        {
            loop: true, //迴圈播放
            speed: 1000000
        });

    navg0.start();
}

參考連結: https://lbs.amap.com/demo/amap-ui/demos/amap-ui-pathsimplifier/index