From 4092854b0bea9e1fc02f29222c6f7cd876e565a5 Mon Sep 17 00:00:00 2001
From: shuishen <1109946754@qq.com>
Date: Mon, 17 Aug 2026 09:43:20 +0800
Subject: [PATCH] feat:项目基础整理
---
apps/workbench-console/src/components/TrajectoryMap.vue | 93 +++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 86 insertions(+), 7 deletions(-)
diff --git a/apps/workbench-console/src/components/TrajectoryMap.vue b/apps/workbench-console/src/components/TrajectoryMap.vue
index 33c8f78..86c5558 100644
--- a/apps/workbench-console/src/components/TrajectoryMap.vue
+++ b/apps/workbench-console/src/components/TrajectoryMap.vue
@@ -1,6 +1,8 @@
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
+import { AimOutlined, BorderOutlined } from "@ant-design/icons-vue";
import {
+ ArcGisMapServerImageryProvider,
Color,
ColorMaterialProperty,
ConstantProperty,
@@ -8,17 +10,21 @@
GridImageryProvider,
HeightReference,
PointGraphics,
+ UrlTemplateImageryProvider,
Viewer
} from "cesium";
import { artifactUrl } from "@/api/artifacts";
-const props = defineProps<{ caseId: "difficult" | "normal" }>();
+const props = defineProps<{ artifactRoot: string; showSpatialContext: boolean; showFlyableZones: boolean }>();
const host = ref<HTMLElement>();
const mapError = ref<string | null>(null);
let viewer: Viewer | null = null;
let trajectorySource: GeoJsonDataSource | null = null;
let eventSource: GeoJsonDataSource | null = null;
+let routeSource: GeoJsonDataSource | null = null;
+let zoneSource: GeoJsonDataSource | null = null;
+let flyableZoneSource: GeoJsonDataSource | null = null;
function eventColor(type: string) {
if (type === "route_deviation") return Color.fromCssColorString("#9a5da8");
@@ -33,7 +39,15 @@
mapError.value = null;
if (trajectorySource) viewer.dataSources.remove(trajectorySource, true);
if (eventSource) viewer.dataSources.remove(eventSource, true);
- const root = `shared/outputs/15-trajectory-analysis/${props.caseId}`;
+ if (routeSource) viewer.dataSources.remove(routeSource, true);
+ if (zoneSource) viewer.dataSources.remove(zoneSource, true);
+ if (flyableZoneSource) viewer.dataSources.remove(flyableZoneSource, true);
+ trajectorySource = null;
+ eventSource = null;
+ routeSource = null;
+ zoneSource = null;
+ flyableZoneSource = null;
+ const root = props.artifactRoot;
trajectorySource = await GeoJsonDataSource.load(artifactUrl(`${root}/trajectories.geojson`), { clampToGround: false });
trajectorySource.entities.values.forEach((entity) => {
if (entity.polyline) {
@@ -52,12 +66,78 @@
heightReference: new ConstantProperty(HeightReference.NONE)
});
});
+ if (props.showSpatialContext) {
+ routeSource = await GeoJsonDataSource.load(artifactUrl(`${root}/reference_routes.geojson`), { clampToGround: false });
+ routeSource.entities.values.forEach((entity) => {
+ if (entity.polyline) {
+ entity.polyline.width = new ConstantProperty(3);
+ entity.polyline.material = new ColorMaterialProperty(Color.fromCssColorString("#444444"));
+ }
+ });
+ zoneSource = await GeoJsonDataSource.load(artifactUrl(`${root}/zones.geojson`), { clampToGround: false });
+ zoneSource.entities.values.forEach((entity) => {
+ if (entity.polygon) {
+ entity.polygon.material = new ColorMaterialProperty(Color.fromCssColorString("#e5c951").withAlpha(0.16));
+ entity.polygon.outline = new ConstantProperty(true);
+ entity.polygon.outlineColor = new ConstantProperty(Color.fromCssColorString("#9a7a00"));
+ }
+ });
+ if (props.showFlyableZones) {
+ flyableZoneSource = await GeoJsonDataSource.load(artifactUrl(`${root}/flyable_zones.geojson`), { clampToGround: false });
+ flyableZoneSource.entities.values.forEach((entity) => {
+ if (entity.polygon) {
+ entity.polygon.material = new ColorMaterialProperty(Color.fromCssColorString("#36a269").withAlpha(0.22));
+ entity.polygon.outline = new ConstantProperty(true);
+ entity.polygon.outlineColor = new ConstantProperty(Color.fromCssColorString("#167344"));
+ }
+ });
+ }
+ }
+ if (zoneSource) await viewer.dataSources.add(zoneSource);
+ if (flyableZoneSource) await viewer.dataSources.add(flyableZoneSource);
+ if (routeSource) await viewer.dataSources.add(routeSource);
await viewer.dataSources.add(trajectorySource);
await viewer.dataSources.add(eventSource);
- await viewer.flyTo(trajectorySource, { duration: 0 });
+ await viewer.flyTo(props.showSpatialContext && zoneSource ? zoneSource : trajectorySource, { duration: 0 });
} catch (error) {
mapError.value = error instanceof Error ? error.message : "Cesium 地图加载失败";
}
+}
+
+async function focusTrajectory() {
+ if (viewer && trajectorySource) await viewer.flyTo(trajectorySource, { duration: 0.4 });
+}
+
+async function focusZones() {
+ if (viewer && zoneSource) await viewer.flyTo(zoneSource, { duration: 0.4 });
+}
+
+async function addBaseLayers() {
+ if (!viewer) return;
+ viewer.imageryLayers.removeAll();
+ try {
+ const arcgis = await ArcGisMapServerImageryProvider.fromUrl(
+ "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer"
+ );
+ viewer.imageryLayers.addImageryProvider(arcgis);
+ } catch {
+ viewer.imageryLayers.addImageryProvider(new GridImageryProvider({ cells: 8, glowWidth: 0 }));
+ }
+ const token = (import.meta.env.VITE_TIANDITU_TOKEN ?? "").trim();
+ if (!token) return;
+ const subdomains = ["0", "1", "2", "3", "4", "5", "6", "7"];
+ viewer.imageryLayers.addImageryProvider(new UrlTemplateImageryProvider({
+ url: `https://t{s}.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=${token}`,
+ subdomains,
+ maximumLevel: 18,
+ credit: "Tianditu imagery"
+ }));
+ viewer.imageryLayers.addImageryProvider(new UrlTemplateImageryProvider({
+ url: `https://t{s}.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=${token}`,
+ subdomains,
+ maximumLevel: 18,
+ credit: "Tianditu labels"
+ }));
}
onMounted(async () => {
@@ -74,15 +154,14 @@
selectionIndicator: false,
timeline: false
});
- viewer.imageryLayers.removeAll();
- viewer.imageryLayers.addImageryProvider(new GridImageryProvider({ cells: 8, glowWidth: 0 }));
+ await addBaseLayers();
await drawCase();
});
-watch(() => props.caseId, drawCase);
+watch(() => [props.artifactRoot, props.showSpatialContext, props.showFlyableZones], drawCase);
onBeforeUnmount(() => viewer?.destroy());
</script>
<template>
- <div class="trajectory-map"><div ref="host" class="cesium-host"></div><a-alert v-if="mapError" class="map-error" type="warning" show-icon :message="mapError" /></div>
+ <div class="trajectory-map"><div ref="host" class="cesium-host"></div><div v-if="showSpatialContext" class="map-legend"><span><i class="legend-track"></i>实飞轨迹</span><span><i class="legend-route"></i>计划航线</span><span><i class="legend-restricted"></i>禁飞区</span><span v-if="showFlyableZones"><i class="legend-flyable"></i>适飞区</span></div><div v-if="showSpatialContext" class="map-tools"><a-tooltip title="聚焦轨迹"><a-button shape="circle" aria-label="聚焦轨迹" @click="focusTrajectory"><AimOutlined /></a-button></a-tooltip><a-tooltip title="查看禁飞区范围"><a-button shape="circle" aria-label="查看禁飞区范围" @click="focusZones"><BorderOutlined /></a-button></a-tooltip></div><a-alert v-if="mapError" class="map-error" type="warning" show-icon :message="mapError" /></div>
</template>
--
Gitblit v1.9.3