shuishen
5 days ago f67d43a43f6c3e58922e3c2f457dd9caafebfc67
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
import {
  Color,
  ColorMaterialProperty,
  ConstantProperty,
  GeoJsonDataSource,
  GridImageryProvider,
  HeightReference,
  PointGraphics,
  Viewer
} from "cesium";
 
import { artifactUrl } from "@/api/artifacts";
 
const props = defineProps<{ caseId: "difficult" | "normal" }>();
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;
 
function eventColor(type: string) {
  if (type === "route_deviation") return Color.fromCssColorString("#9a5da8");
  if (type === "restricted_zone") return Color.fromCssColorString("#bc861a");
  if (type === "gathering") return Color.fromCssColorString("#176b50");
  return Color.fromCssColorString("#c6533f");
}
 
async function drawCase() {
  if (!viewer) return;
  try {
    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}`;
    trajectorySource = await GeoJsonDataSource.load(artifactUrl(`${root}/trajectories.geojson`), { clampToGround: false });
    trajectorySource.entities.values.forEach((entity) => {
      if (entity.polyline) {
        entity.polyline.width = new ConstantProperty(4);
        entity.polyline.material = new ColorMaterialProperty(Color.fromCssColorString("#176b50"));
      }
    });
    eventSource = await GeoJsonDataSource.load(artifactUrl(`${root}/events.geojson`), { clampToGround: false });
    eventSource.entities.values.forEach((entity) => {
      const type = String(entity.properties?.event_type?.getValue() ?? "stop");
      entity.point = new PointGraphics({
        pixelSize: new ConstantProperty(12),
        color: new ConstantProperty(eventColor(type)),
        outlineColor: new ConstantProperty(Color.WHITE),
        outlineWidth: new ConstantProperty(2),
        heightReference: new ConstantProperty(HeightReference.NONE)
      });
    });
    await viewer.dataSources.add(trajectorySource);
    await viewer.dataSources.add(eventSource);
    await viewer.flyTo(trajectorySource, { duration: 0 });
  } catch (error) {
    mapError.value = error instanceof Error ? error.message : "Cesium 地图加载失败";
  }
}
 
onMounted(async () => {
  if (!host.value) return;
  viewer = new Viewer(host.value, {
    animation: false,
    baseLayerPicker: false,
    fullscreenButton: false,
    geocoder: false,
    homeButton: false,
    infoBox: false,
    navigationHelpButton: false,
    sceneModePicker: false,
    selectionIndicator: false,
    timeline: false
  });
  viewer.imageryLayers.removeAll();
  viewer.imageryLayers.addImageryProvider(new GridImageryProvider({ cells: 8, glowWidth: 0 }));
  await drawCase();
});
 
watch(() => props.caseId, 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>
</template>