基于Leaflet实现路径轨迹回放功能
效果图:
说明:
1.该功能是在这篇博客基础上完成的: 但是⾥⾯有点⼩问题:⾸先,⼩车并不是匀速运动的,⽽是每⼀段的运⾏时间固定,所以在该博客上进⾏了修改;另⼀⽅⾯,Leaflet中没有提供设置图标旋转⾓度的⽅法,因此需要先对Marker类进⾏扩展。
2.另外还参考了,本来是想直接把js⽂件拿过来⽤,但是⾥⾯很多⽅法都和Bmap对象绑定,不能直接使⽤。
3.百度开源库中是先将经纬度坐标转为平⾯坐标再进⾏插值,插值之后再转为经纬度坐标,这样做的⽬的就只为了计算出真实距离,然后根据设置的速度进⾏真实模拟;Leaflet中没有提供地理坐标转平⾯坐标的⽅法,因此是直接⽤的经纬度进⾏插值,因此回放速度只是⼀个相对速度。
实现代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta chart="UTF-8">
<title>路径轨迹回放</title>
<meta chart="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;
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" ></div>
<div class="input-card">
<button id="run" onclick="start()">run</button>
<button id="stop" onclick="stop()">stop</button>
<button id="pau" onclick="pau()">pau</button>
</div>
<script>
/**
* 为Marker类添加⽅法
*/
(function() {
// save the original methods before they are overwritten
var proto_initIcon = L.Marker.prototype._initIcon;
var proto_tPos = L.Marker.prototype._tPos;
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) {cee
iconAnchor = (iconAnchor[0] + 'px ' + iconAnchor[1] + 'px');
}
ationOrigin = ationOrigin || iconAnchor || 'center center' ;
ationAngle = ationAngle || 0;
// Ensure marker keeps rotated during dragging
<('drag', function(e) { e.target._applyRotation(); });
});
L.Marker.include({
_initIcon: function() {
proto_initIcon.call(this);
},
_tPos: function (pos) {
proto_tPos.call(this, pos);
this._applyRotation();
},
_applyRotation: function () {
if(ationAngle) {
this._icon.style[L.DomUtil.TRANSFORM+'Origin'] = ationOrigin;
if(oldIE) {
/
/ for IE 9, u the 2D rotation
this._icon.style[L.DomUtil.TRANSFORM] = 'rotate(' + ationAngle + 'deg)';
} el {
// for modern browrs, prefer the 3D accelerated version
this._icon.style[L.DomUtil.TRANSFORM] += ' rotateZ(' + ationAngle + 'deg)';
},
tRotationAngle: function(angle) {
ationAngle = angle;
this.update();
return this;
},
tRotationOrigin: function(origin) {
ationOrigin = origin;
this.update();
return this;
}
});
})();
var map = L.map('mapid', {
center: [38.8631169, 2.3708919],
zoom: 5,
crs: L.CRS.EPSG3857,
layers: [
L.tileLayer('{s}.tile.openstreetmap/{z}/{x}/{y}.png', {
attribution: '© <a href="osm/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],
edesign[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) {
//没按pau再按start不做处理
if (!me._fromPau) {
return;
}el if(!me._fromStop){
//按了pau按钮,并且再按start,直接移动到下⼀点
//并且此过程中,没有按stop按钮
//防⽌先stop,再pau,然后连续不停的start的异常
me._moveNext(++me.i);
//第⼀次点击开始,或者点了stop之后点开始
me._addMarker();
me._moveNext(me.i);
}
//重置状态
this._fromPau = fal;
this._fromStop = fal;
}
function _addMarker(callback) {
if (this._marker) {
this.stop();
this._ve();
}
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 = und(me._getDistance(initPos[0], initPos[1],targetPos[0],targetPos[1]) / step);
涨价英文//如果⼩于1直接移动到下⼀点
if (count < 1) {
this._moveNext(++me.i);
return;
television什么意思}
//两点之间匀速移动
autvar angle;
me._intervalFlag = tInterval(function() {
/
/两点之间当前帧数⼤于总帧数的时候,则说明已经完成移动
if (currentCount >= count) {
clearInterval(me._intervalFlag);
//移动的点已经超过总的长度
if(me.i > me._path.length){
return;
}
}el {
currentCount++;
var x = effect(initPos[0], targetPos[0], currentCount, count),
y = effect(initPos[1], targetPos[1], currentCount, count);
var pos =L.latLng(x,y);
//设置marker
if(currentCount == 1){
if(me._ableRotation == true){
//initPos=[lat,lng],leaflet中坐标对的格式为(纬度,经度),因此要计算⾓度的话,X对应经度,即initPos[1] angle = me._getAngle(initPos[1], initPos[0],targetPos[1],targetPos[0]);
}
}
//正在移动
me._ve();//先删除
me._marker.tRotationAngle(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;
}
联合王国/**
trueman* 计算两点间的距离
*/
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 = 0
if (endx == startx) {
tan = 90;
} el {
tan = Math.atan(Math.abs((endy - starty) / (endx - startx))) * 180 / Math.PI;
console.log(tan);
}
if (endx >= startx && endy >= starty)//第⼀象限
{
长脸男生发型设计
return -tan;
} el if (endx > startx && endy < starty)//第四象限
{
return tan;