基于Leaflet实现路径轨迹回放功能
生活随笔
收集整理的這篇文章主要介紹了
基于Leaflet实现路径轨迹回放功能
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
效果圖:
說明:
?????? 1.該功能是在這篇博客基礎上完成的:ArcGIS JS API 實現路徑軌跡回放? 但是里面有點小問題:首先,小車并不是勻速運動的,而是每一段的運行時間固定,所以在該博客上進行了修改;另一方面,Leaflet中沒有提供設置圖標旋轉角度的方法,因此需要先對Marker類進行擴展。
?????? 2.另外還參考了百度地圖路書開源庫,本來是想直接把js文件拿過來用,但是里面很多方法都和Bmap對象綁定,不能直接使用。
?????? 3.百度開源庫中是先將經緯度坐標轉為平面坐標再進行插值,插值之后再轉為經緯度坐標,這樣做的目的就只為了計算出真實距離,然后根據設置的速度進行真實模擬;Leaflet中沒有提供地理坐標轉平面坐標的方法,因此是直接用的經緯度進行插值,因此回放速度只是一個相對速度。
實現代碼:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>路徑軌跡回放</title><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="stylesheet" href="./lib/leaflet/leaflet.css" /><script src="./lib/leaflet/leaflet.js"></script></head><style>* { margin: 0; padding: 0; }html, body { height: 100%; }#mapid { width:100%; height:100%; }.input-card{z-index: 50;display: flex;flex-direction: column;min-width: 0;word-wrap: break-word;background-color: #fff;background-clip: border-box;border-radius: .25rem;width: 8rem;border-width: 0;border-radius: 0.4rem;box-shadow: 0 2px 6px 0 rgba(114, 124, 245, .5);position: fixed;bottom: 1rem;right: 1rem;-ms-flex: 1 1 auto;flex: 1 1 auto;padding: 0.75rem 1.25rem;} </style><body><div id="mapid" style="z-index: 10" ></div> <div class="input-card"><button id="run" onclick="start()">run</button><button id="stop" onclick="stop()">stop</button><button id="pause" onclick="pause()">pause</button> </div><script>/*** 為Marker類添加方法*/(function() {// save these original methods before they are overwrittenvar proto_initIcon = L.Marker.prototype._initIcon;var proto_setPos = L.Marker.prototype._setPos;var oldIE = (L.DomUtil.TRANSFORM === 'msTransform');L.Marker.addInitHook(function () {var iconOptions = this.options.icon && this.options.icon.options;var iconAnchor = iconOptions && this.options.icon.options.iconAnchor;if (iconAnchor) {iconAnchor = (iconAnchor[0] + 'px ' + iconAnchor[1] + 'px');}this.options.rotationOrigin = this.options.rotationOrigin || iconAnchor || 'center center' ;this.options.rotationAngle = this.options.rotationAngle || 0;// Ensure marker keeps rotated during draggingthis.on('drag', function(e) { e.target._applyRotation(); });});L.Marker.include({_initIcon: function() {proto_initIcon.call(this);},_setPos: function (pos) {proto_setPos.call(this, pos);this._applyRotation();},_applyRotation: function () {if(this.options.rotationAngle) {this._icon.style[L.DomUtil.TRANSFORM+'Origin'] = this.options.rotationOrigin;if(oldIE) {// for IE 9, use the 2D rotationthis._icon.style[L.DomUtil.TRANSFORM] = 'rotate(' + this.options.rotationAngle + 'deg)';} else {// for modern browsers, prefer the 3D accelerated versionthis._icon.style[L.DomUtil.TRANSFORM] += ' rotateZ(' + this.options.rotationAngle + 'deg)';}}},setRotationAngle: function(angle) {this.options.rotationAngle = angle;this.update();return this;},setRotationOrigin: function(origin) {this.options.rotationOrigin = origin;this.update();return this;}});})();var map = L.map('mapid', {center: [38.8631169, 2.3708919],zoom: 5,crs: L.CRS.EPSG3857,layers: [L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'})]});var _opts = {icon: null,enableRotation:true //允許小車旋轉};//移動到當前點的索引this.i = 0;var latlngs = [[45.51, 2.3708919],[37.77, 8.54235],[34.04, 9.52532],[36.04, 10.52532],[40.04, 14.52532],[47.04, 15.52532]];var _path = latlngs;var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);var myIcon = L.icon({iconUrl: 'car.png',iconSize: [24, 24]});function start(){var me = this,len = me._path.length;//不是第一次點擊開始,并且小車還沒到達終點if (me.i && me.i < len - 1) {//沒按pause再按start不做處理if (!me._fromPause) {return;}else if(!me._fromStop){//按了pause按鈕,并且再按start,直接移動到下一點//并且此過程中,沒有按stop按鈕//防止先stop,再pause,然后連續不停的start的異常me._moveNext(++me.i);}}else {//第一次點擊開始,或者點了stop之后點開始me._addMarker();me._moveNext(me.i);}//重置狀態this._fromPause = false;this._fromStop = false;}function _addMarker(callback) {if (this._marker) {this.stop();this._marker.remove();}var marker =new L.Marker(_path[0],{icon: myIcon }).addTo(map)this._marker = marker;}/*** 移動到下一個點*/function _moveNext(index) {var me = this;if (index < this._path.length - 1) {this._move(me._path[index], me._path[index + 1], me._tween);}}/*** 移動小車* @param {Number} poi 當前的步長.* @param {Point} initPos 經緯度坐標初始點.* @param {Point} targetPos 經緯度坐標目標點.* @param {Function} effect 緩動效果,實現插值* @return 無返回值.*/function _move(initPos,targetPos,effect) {var me = this,//當前的幀數currentCount = 0,//步長timer = 10, //10毫秒為一步step = 0.1,//總步數count = Math.round(me._getDistance(initPos[0], initPos[1],targetPos[0],targetPos[1]) / step);//如果小于1直接移動到下一點if (count < 1) {this._moveNext(++me.i);return;}//兩點之間勻速移動var angle;me._intervalFlag = setInterval(function() {//兩點之間當前幀數大于總幀數的時候,則說明已經完成移動if (currentCount >= count) {clearInterval(me._intervalFlag);//移動的點已經超過總的長度if(me.i > me._path.length){return;}//運行下一個點me._moveNext(++me.i);}else {currentCount++;var x = effect(initPos[0], targetPos[0], currentCount, count),y = effect(initPos[1], targetPos[1], currentCount, count);var pos =L.latLng(x,y);//設置markerif(currentCount == 1){if(me._opts.enableRotation == true){//initPos=[lat,lng],leaflet中坐標對的格式為(緯度,經度),因此要計算角度的話,X對應經度,即initPos[1]angle = me._getAngle(initPos[1], initPos[0],targetPos[1],targetPos[0]);}}//正在移動me._marker.remove();//先刪除me._marker.setRotationAngle(angle);me._marker._latlng = pos;//設置圖標位置me._marker.addTo(map);}},timer);}/*** 緩動效果* 初始坐標,目標坐標,當前的步長,總的步長* @private*/function _tween(initPos, targetPos, currentCount, count) {var b = initPos, c = targetPos - initPos, t = currentCount,d = count;return c * t / d + b;}/*** 計算兩點間的距離*/function _getDistance(pxA,pyA, pxB,pyB) {return Math.sqrt(Math.pow(pxA - pxB, 2) + Math.pow(pyA - pyB, 2));}/*** 計算角度* @param startx* @param starty* @param endx* @param endy* @returns {number}*/function _getAngle(startx, starty, endx, endy) {var tan = 0if (endx == startx) {tan = 90;} else {tan = Math.atan(Math.abs((endy - starty) / (endx - startx))) * 180 / Math.PI;console.log(tan);}if (endx >= startx && endy >= starty)//第一象限{return -tan;} else if (endx > startx && endy < starty)//第四象限{return tan;} else if (endx < startx && endy > starty)//第二象限{return tan - 180;} else {return 180 - tan; //第三象限}}/*** 停止*/function stop() {this.i = 0;this._fromStop = true;clearInterval(this._intervalFlag);}/*** 暫停*/function pause() {clearInterval(this._intervalFlag);//標識是否是按過pause按鈕this._fromPause = true;}</script> </body> </html>文件下載地址:https://download.csdn.net/download/wml00000/10769999
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的基于Leaflet实现路径轨迹回放功能的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JS事件冒泡与事件捕获
- 下一篇: JS实现回调函数