173 lines
5.3 KiB
Markdown
173 lines
5.3 KiB
Markdown
---
|
|
title: Openlayers轨迹播放
|
|
date: 2020-12-25
|
|
author: ac
|
|
tags:
|
|
- 轨迹
|
|
categories:
|
|
- GIS
|
|
---
|
|
|
|
## 轨迹播放
|
|
|
|

|
|
|
|
轨迹播放是非常常见的需求,像旅游路线、徒步轨迹或项目中的巡检人员的巡检轨迹的复盘等,可以回顾户外运动的轨迹,分析和规划最优的行进路线。
|
|
|
|
**实现思路**
|
|
|
|
轨迹通常是收集移动端发送回来的点集,将点集组织成线,渲染到地图上;
|
|
|
|
- 获取轨迹数据,可以是线也可以是点集,将其渲染到地图上;
|
|
- 创建移动点图层,将轨迹中的第一个点作为点要素的`geometry`
|
|
|
|
- 通过设置步长对点集进行插值,得到轨迹播放的轨迹点集合;
|
|
- 设置总时间,使用定时器`setTimeout`,在每个点的平均时间内,不断地改变移动点的`geometry`对象(将轨迹点集合中的点,设置到要素中)
|
|
|
|
我们先在[http://geojson.io/](http://geojson.io/)上绘制要使用的轨迹点,这里我们使用的是线要素`LineString`。
|
|
|
|

|
|
|
|
初始化地图,将轨迹数据展示在地图上。如果是点集也可以在获取到数据后组织成线要素进行展示。
|
|
|
|
```javascript
|
|
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);
|
|
```
|
|
|
|
在获取到轨迹数据后,就可以对其进行插值,增加移动点图层,设置图标样式。
|
|
|
|
```javascript
|
|
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`是用来计算移动点间的距离和角度的。
|
|
|
|
```javascript
|
|
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; //第三象限
|
|
}
|
|
}
|
|
```
|
|
|