|
|
@ -44,14 +44,17 @@ |
|
|
<script setup> |
|
|
<script setup> |
|
|
import { computed, onMounted, onUnmounted, ref } from 'vue' |
|
|
import { computed, onMounted, onUnmounted, ref } from 'vue' |
|
|
import { useRoute, useRouter } from 'vue-router' |
|
|
import { useRoute, useRouter } from 'vue-router' |
|
|
import * as maptalks from 'maptalks' |
|
|
|
|
|
|
|
|
import mapboxgl from 'mapbox-gl' |
|
|
import { useUiStore } from '@/stores/modules/uiStore' |
|
|
import { useUiStore } from '@/stores/modules/uiStore' |
|
|
import request from '@/utils/http' |
|
|
import request from '@/utils/http' |
|
|
import * as urls from '@/config/urls' |
|
|
import * as urls from '@/config/urls' |
|
|
import commonRefs from '@/utils/commonRefs' |
|
|
import commonRefs from '@/utils/commonRefs' |
|
|
import mapHelper from '@/core/mapHelper' |
|
|
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 route = useRoute() |
|
|
const router = useRouter() |
|
|
const router = useRouter() |
|
|
@ -62,7 +65,6 @@ const points = ref([]) |
|
|
const loaded = ref(false) |
|
|
const loaded = ref(false) |
|
|
|
|
|
|
|
|
let mapInstance = null |
|
|
let mapInstance = null |
|
|
let trajLayer = null |
|
|
|
|
|
|
|
|
|
|
|
const recordName = computed(() => execution.value?.taskId || '执行记录') |
|
|
const recordName = computed(() => execution.value?.taskId || '执行记录') |
|
|
const deviceName = computed(() => execution.value?.droneSn || execution.value?.dockId || '--') |
|
|
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)) |
|
|
.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) |
|
|
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) { |
|
|
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, |
|
|
|
|
|
|
|
|
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, |
|
|
}, |
|
|
}, |
|
|
}).addTo(trajLayer) |
|
|
|
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
if (!coords.length) return |
|
|
if (coords.length === 1) { |
|
|
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 |
|
|
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() { |
|
|
async function setupMap() { |
|
|
const map = await commonRefs.getRef('map') |
|
|
const map = await commonRefs.getRef('map') |
|
|
if (!map) { |
|
|
if (!map) { |
|
|
ui.toast('地图未初始化(缺少天地图 token)') |
|
|
|
|
|
|
|
|
ui.toast('地图未初始化(缺少 Mapbox token)') |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
mapInstance = map |
|
|
mapInstance = map |
|
|
mapHelper.toggleMapMode({ is2D: true }) |
|
|
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() { |
|
|
function teardownMap() { |
|
|
if (mapInstance) { |
|
|
|
|
|
const layer = mapInstance.getLayer(LAYER_ID) |
|
|
|
|
|
if (layer) layer.remove() |
|
|
|
|
|
} |
|
|
|
|
|
trajLayer = null |
|
|
|
|
|
|
|
|
clearReplayLayers(mapInstance) |
|
|
mapInstance = null |
|
|
mapInstance = null |
|
|
commonRefs.clearPendingList('map') |
|
|
commonRefs.clearPendingList('map') |
|
|
} |
|
|
} |
|
|
|