Browse Source

feat: Replay 航迹改为 Mapbox GeoJSON 图层

main
xiaosi 3 weeks ago
parent
commit
9329b6180e
  1. 120
      src/views/ReplayView/ReplayView.vue

120
src/views/ReplayView/ReplayView.vue

@ -44,14 +44,17 @@
<script setup>
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import * as maptalks from 'maptalks'
import mapboxgl from 'mapbox-gl'
import { useUiStore } from '@/stores/modules/uiStore'
import request from '@/utils/http'
import * as urls from '@/config/urls'
import commonRefs from '@/utils/commonRefs'
import mapHelper from '@/core/mapHelper'
const LAYER_ID = 'replay-trajectory'
const LAYER_ID = 'replay-traj'
const SOURCE_ID = 'replay-traj'
const LINE_LAYER = 'replay-traj-line'
const POINT_LAYER = 'replay-traj-points'
const route = useRoute()
const router = useRouter()
@ -62,7 +65,6 @@ const points = ref([])
const loaded = ref(false)
let mapInstance = null
let trajLayer = null
const recordName = computed(() => execution.value?.taskId || '执行记录')
const deviceName = computed(() => execution.value?.droneSn || execution.value?.dockId || '--')
@ -100,77 +102,97 @@ function toCoords(list) {
.filter(([lon, lat]) => Number.isFinite(lon) && Number.isFinite(lat))
}
function drawTrajectory(map, list) {
const existing = map.getLayer(LAYER_ID)
if (existing) existing.remove()
function clearReplayLayers(map) {
if (!map) return
for (const id of [POINT_LAYER, LINE_LAYER]) {
if (map.getLayer(id)) map.removeLayer(id)
}
if (map.getSource(SOURCE_ID)) map.removeSource(SOURCE_ID)
}
function drawTrajectory(map, list) {
clearReplayLayers(map)
const coords = toCoords(list)
trajLayer = new maptalks.VectorLayer(LAYER_ID, [], { zIndex: 130, forceRenderOnMoving: true })
trajLayer.addTo(map)
if (!coords.length) return
const lineFeatures = coords.length >= 2
? [{
type: 'Feature',
geometry: { type: 'LineString', coordinates: coords },
properties: {},
}]
: []
const pointFeatures = coords.map((c, i) => ({
type: 'Feature',
properties: {
role: i === 0 ? 'start' : i === coords.length - 1 ? 'end' : 'mid',
color: i === 0 ? '#21a06a' : i === coords.length - 1 ? '#c56c28' : '#2b79c8',
radius: i === 0 || i === coords.length - 1 ? 6 : 3.5,
},
geometry: { type: 'Point', coordinates: c },
}))
map.addSource(SOURCE_ID, {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [...lineFeatures, ...pointFeatures],
},
})
if (coords.length >= 2) {
new maptalks.LineString(coords, {
symbol: {
lineColor: '#2b79c8',
lineWidth: 3,
lineDasharray: [8, 6],
lineOpacity: 0.95,
map.addLayer({
id: LINE_LAYER,
type: 'line',
source: SOURCE_ID,
filter: ['==', ['geometry-type'], 'LineString'],
paint: {
'line-color': '#2b79c8',
'line-width': 3,
'line-dasharray': [2, 1.5],
'line-opacity': 0.95,
},
}).addTo(trajLayer)
})
}
coords.forEach((c, i) => {
const isStart = i === 0
const isEnd = i === coords.length - 1
const color = isStart ? '#21a06a' : isEnd ? '#c56c28' : '#2b79c8'
const markerType = isStart || isEnd ? 'ellipse' : 'ellipse'
const size = isStart || isEnd ? 12 : 7
new maptalks.Marker(c, {
symbol: {
markerType,
markerFill: color,
markerLineColor: '#fff',
markerLineWidth: 2,
markerWidth: size,
markerHeight: size,
},
}).addTo(trajLayer)
map.addLayer({
id: POINT_LAYER,
type: 'circle',
source: SOURCE_ID,
filter: ['==', ['geometry-type'], 'Point'],
paint: {
'circle-radius': ['get', 'radius'],
'circle-color': ['get', 'color'],
'circle-stroke-color': '#fff',
'circle-stroke-width': 2,
},
})
if (!coords.length) return
if (coords.length === 1) {
map.animateTo({ center: coords[0], zoom: Math.max(map.getZoom(), 16) }, { duration: 400 })
map.easeTo({ center: coords[0], zoom: Math.max(map.getZoom(), 16), duration: 400 })
return
}
const extent = new maptalks.Extent(
Math.min(...coords.map((c) => c[0])),
Math.min(...coords.map((c) => c[1])),
Math.max(...coords.map((c) => c[0])),
Math.max(...coords.map((c) => c[1])),
const bounds = coords.reduce(
(b, c) => b.extend(c),
new mapboxgl.LngLatBounds(coords[0], coords[0]),
)
map.fitExtent(extent, 0, { animation: true, duration: 400, padding: [80, 80, 80, 80] })
map.fitBounds(bounds, { padding: 80, duration: 400 })
}
async function setupMap() {
const map = await commonRefs.getRef('map')
if (!map) {
ui.toast('地图未初始化(缺少天地图 token)')
ui.toast('地图未初始化(缺少 Mapbox token)')
return
}
mapInstance = map
mapHelper.toggleMapMode({ is2D: true })
drawTrajectory(map, points.value)
const run = () => drawTrajectory(map, points.value)
if (map.isStyleLoaded()) run()
else map.once('load', run)
}
function teardownMap() {
if (mapInstance) {
const layer = mapInstance.getLayer(LAYER_ID)
if (layer) layer.remove()
}
trajLayer = null
clearReplayLayers(mapInstance)
mapInstance = null
commonRefs.clearPendingList('map')
}

Loading…
Cancel
Save