5.3 KiB
5.3 KiB
title | date | author | tags | categories | ||
---|---|---|---|---|---|---|
Openlayers轨迹播放 | 2020-12-25 | ac |
|
|
轨迹播放
轨迹播放是非常常见的需求,像旅游路线、徒步轨迹或项目中的巡检人员的巡检轨迹的复盘等,可以回顾户外运动的轨迹,分析和规划最优的行进路线。
实现思路
轨迹通常是收集移动端发送回来的点集,将点集组织成线,渲染到地图上;
-
获取轨迹数据,可以是线也可以是点集,将其渲染到地图上;
-
创建移动点图层,将轨迹中的第一个点作为点要素的
geometry
-
通过设置步长对点集进行插值,得到轨迹播放的轨迹点集合;
-
设置总时间,使用定时器
setTimeout
,在每个点的平均时间内,不断地改变移动点的geometry
对象(将轨迹点集合中的点,设置到要素中)
我们先在http://geojson.io/上绘制要使用的轨迹点,这里我们使用的是线要素LineString
。
初始化地图,将轨迹数据展示在地图上。如果是点集也可以在获取到数据后组织成线要素进行展示。
var map = new ol.Map({
target: "map",
layers:[
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
// center: ol.proj.fromLonLat([113,23]),
center: [113.9537, 22.5024],
zoom: 14,
projection:"EPSG:4326"
})
});
//获取轨迹数据并展示在地图上
var trackSource = new ol.source.Vector();
var geojsonFormat = new ol.format.GeoJSON();
fetch("data/geojson/trackData.json").then(function(response){
return response.json();
}).then(function(result){
var features = geojsonFormat.readFeatures(result);
trackSource.addFeatures(features);
//showCarLayer(trackSource);//获取轨迹数据后展示小车移动效果
});
var trackLine = new ol.layer.Vector({
source: trackSource,
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: "#0096c7",
width: 5
})
})
});
map.addLayer(trackLine);
在获取到轨迹数据后,就可以对其进行插值,增加移动点图层,设置图标样式。
var distance = 5;//步长
var totalTime = 60*1000; //总时间
var srcUri = "./img/car.png";
function showCarLayer(source){
//设置汽车图标样式
var carStyle = new ol.style.Style({
image: new ol.style.Icon({
src: srcUri,
scale: 0.2,
offset: [-18,40]
})
});
//获取轨迹线段的所有坐标点数组
var coordinates = source.getFeatures()[0].getGeometry().getCoordinates();
var firstPoint = new ol.geom.Point(coordinates[0]);
var focusFeature = new ol.Feature(firstPoint);
focusFeature.setStyle(carStyle);
var carLayer = new ol.layer.Vector({
source: new ol.source.Vector({
features:[focusFeature]
})
});
map.addLayer(carLayer);
//计算轨迹点点集
var pointArr = [];
pointArr.push(coordinates[0]);
for(var i=1;i<coordinates.length;i++){
var pointArrLast = pointArr[pointArr.length - 1];
var dd = calcDistance(pointArrLast,coordinates[i]);
if(dd > distance){
var ratio = distance/dd;
var endX = pointArrLast[0] + ratio*(coordinates[i][0] - pointArrLast[0]);
var endY = pointArrLast[1] + ratio*(coordinates[i][1] - pointArrLast[1]);
pointArr.push([endX,endY]);
i--;
}else{
pointArr.push(coordinates[i]);
}
}
//展示小车移动
var stopTime = totalTime/pointArr.length;
for(let j=1;j<pointArr.length-1;j++){
setTimeout(function () {
let focusPoint = new ol.geom.Point(pointArr[j]);
let angle = calcAngle(pointArr[j - 1],pointArr[j])
focusFeature.setGeometry(focusPoint);
focusFeature.getStyle().getImage().setRotation(angle)
},stopTime*j);
}
}
其中的calcDistance
和calcAngle
是用来计算移动点间的距离和角度的。
function calcDistance(preCoor,postCoor){
var projPreCoor = ol.proj.fromLonLat(preCoor);
var projPostCoor = ol.proj.fromLonLat(postCoor);
return Math.sqrt((projPostCoor[0]-projPreCoor[0])*(projPostCoor[0]-projPreCoor[0])+
(projPostCoor[1]-projPreCoor[1])*(projPostCoor[1]-projPreCoor[1]));
}
/**
* 计算小车旋转的角度,单位是弧度
* @param preCoor
* @param postCoor
* @returns {number}
*/
function calcAngle(preCoor,postCoor) {
var startx = preCoor[0],
starty = preCoor[1],
endx = postCoor[0],
endy = postCoor[1];
var tan = 0;
if (endx === startx) {
// tan = Math.atan(0) * 180 / Math.PI
tan = Math.atan(0) ;
} else {
tan = Math.atan(Math.abs((endy - starty) / (endx - startx))) ;
}
if (endx >= startx && endy >= starty)//第一象限
{
return -tan;
} else if (endx > startx && endy < starty)//第四象限
{
return tan;
} else if (endx < startx && endy > starty)//第二象限
{
return tan - Math.PI;
} else {
return Math.PI - tan; //第三象限
}
}