本篇介绍一下使用openlayers轨迹回放(历史轨迹),实时轨迹
1 需求
- 轨迹回放(历史轨迹)
- 实时轨迹
2 分析
主要是利用定时器,不断添加feature
- 轨迹回放(历史轨迹),一般是一次性拿到所有坐标点,按照时间间隔不断循环添加feature
- 实时轨迹,一般是通过websocket监听,不断获取最新坐标点,根据上报的频率,可能需要抽样
3 实现
<template>
<div id="map" class="map"></div>
</template>
<script setup lang="ts">
import { Feature, Map, View } from 'ol';
import { LineString, Point } from 'ol/geom';
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer';
import { get } from 'ol/proj';
import { Vector as VectorSource, XYZ } from 'ol/source';
import { Circle, Fill, Icon, Stroke, Style } from 'ol/style';
import iconSrc from '@/assets/image/truck.png';
const projection = get('EPSG:4326');
const layerTypeMap = {
vector: ['vec', 'cva'], // [矢量底图, 矢量注记]
image: ['img', 'cia'], // [影像底图, 影像注记]
terrain: ['ter', 'cta'] // [地形晕渲, 地形注记]
};
const map = shallowRef();
const source = shallowRef<VectorSource>(new VectorSource());
const trace = ref();
onMounted(() => {
initMap('image');
initTrace();
});
const initMap = (layerType = 'image') => {
const key = '替换为天地图key';
// c: 经纬度投影 w: 墨卡托投影
const matrixSet = 'c';
map.value = new Map({
target: 'map',
layers: [
// 底图
new TileLayer({
source: new XYZ({
url: `https://t{0-7}.tianditu.gov.cn/DataServer?T=${layerTypeMap[layerType][0]}_${matrixSet}&tk=${key}&x={x}&y={y}&l={z}`,
projection
})
}),
// 注记
new TileLayer({
source: new XYZ({
url: `https://t{0-7}.tianditu.gov.cn/DataServer?T=${layerTypeMap[layerType][1]}_${matrixSet}&tk=${key}&x={x}&y={y}&l={z}`,
projection
})
}),
new VectorLayer({
source: source.value,
style: new Style({
stroke: new Stroke({
color: 'rgba(228, 147, 87, 1)',
width: 3
})
})
})
],
view: new View({
center: [116.406393, 39.909006],
projection: projection,
zoom: 5,
maxZoom: 17,
minZoom: 1
})
});
};
const initTrace = () => {
trace.value = [
[110, 30],
[110.2, 30],
[110.4, 30.2],
[110.8, 30.4],
[111, 31],
[111.3, 31],
[111.6, 31],
[111.9, 31],
[112, 31],
[112.3, 31],
[112.5, 31],
[112.8, 31],
[113, 31],
[114, 31],
[115.3, 32],
[115.5, 32],
[115.8, 31.8],
[116, 31.4],
[116.2, 31.1],
[116.5, 30.5],
[115, 30.2],
[114, 29.8],
[113, 29.6],
[112, 29.4],
[111, 30.2],
[110, 30.4],
[109, 30.6],
[108, 31]
];
const iconFeature = new Feature({
geometry: new Point(trace.value[0])
});
const icon = new Icon({
crossOrigin: 'anonymous',
src: iconSrc, // 或者new URL('../../assets/svg/truck.svg',import.meta.url).href
color: 'red',
opacity: 1,
width: 30,
height: 30
});
iconFeature.setStyle(
new Style({
image: icon
})
);
source.value?.addFeature(iconFeature);
let i = 0;
const interval = setInterval(() => {
if (trace.value[i + 1]) {
iconFeature.setGeometry(new Point(trace.value[i + 1]));
let arc = 0;
if (
(trace.value[i + 1][0] - trace.value[i][0] >= 0 &&
trace.value[i + 1][1] - trace.value[i][1] >= 0) ||
(trace.value[i + 1][0] - trace.value[i][0] < 0 &&
trace.value[i + 1][1] - trace.value[i][1] > 0)
) {
arc = Math.atan(
(trace.value[i + 1][0] - trace.value[i][0]) / (trace.value[i + 1][1] - trace.value[i][1])
);
} else if (
trace.value[i + 1][0] - trace.value[i][0] > 0 &&
trace.value[i + 1][1] - trace.value[i][1] < 0 ||
trace.value[i + 1][0] - trace.value[i][0] < 0 &&
trace.value[i + 1][1] - trace.value[i][1] < 0
) {
arc =
Math.PI +
Math.atan(
(trace.value[i + 1][0] - trace.value[i][0]) /
(trace.value[i + 1][1] - trace.value[i][1])
);
}
icon.setRotation(arc);//设置航向角(与正北的夹角)
source.value?.addFeature(
new Feature({
geometry: new LineString([trace.value[i], trace.value[i + 1]])
})
);
i++;
} else {
clearInterval(interval);
}
}, 200);
};
</script>
<style scoped lang="scss">
.map {
width: 100%;
height: 100%;
}
</style>