From 8a7fb9a519cae32d38ba66058d243aa7e6b6aeb2 Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Tue, 27 Jan 2026 08:57:49 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'

---
 applications/drone-command/src/components/map-container/device-map-container.vue |  122 +++++++++++++++++++++++++++++-----------
 1 files changed, 89 insertions(+), 33 deletions(-)

diff --git a/applications/drone-command/src/components/map-container/device-map-container.vue b/applications/drone-command/src/components/map-container/device-map-container.vue
index a8f8566..f5813aa 100644
--- a/applications/drone-command/src/components/map-container/device-map-container.vue
+++ b/applications/drone-command/src/components/map-container/device-map-container.vue
@@ -2,8 +2,9 @@
 	<div class="map-shell">
 		<CommonCesiumMap ref="mapRef" class="command-cesium map-container" :dom-id="props.containerId" :active="true"
 			:flat-mode="false" :terrain="true" :layer-mode="4" :contour="false" :boundary="false"
-			:show-admin-boundary="true" :zoom-to-boundary="true" :enable-stage-emit="true" :cluster-height="100000"
-			@ready="handleMapReady" @stage-change="handleStageChange" />
+			:show-admin-boundary="true" :zoom-to-boundary="true" :enable-stage-emit="true"
+			:cluster-height="CLUSTER_HEIGHT" :detail-height="DETAIL_HEIGHT" @ready="handleMapReady"
+			@stage-change="handleStageChange" />
 		<div v-if="props.showLayerControl" class="layer-control-root" :class="{ collapsed: props.leftCollapsed }">
 			<div class="layer-control-wrap" ref="layerWrapRef">
 				<div class="layer-control" @click="toggleLayerPanel">
@@ -63,6 +64,9 @@
 	createRadialGradientMaterial,
 	getTexturedVertexFormat,
 } from './device-map-materials'
+
+const CLUSTER_HEIGHT = 100000
+const DETAIL_HEIGHT = 10000
 
 const props = defineProps({
 	onlineDevices: {
@@ -157,6 +161,14 @@
 	const latitude = Number(latitudeRaw)
 	if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) return null
 	return { longitude, latitude }
+}
+
+const getDeviceRange = item => {
+	if (!item) return null
+	const rawRange = item.effectiveRangeKm ?? item.range ?? item.coverRadiusM
+	const range = Number(rawRange)
+	if (!Number.isFinite(range) || range <= 0) return null
+	return range
 }
 
 const ensureCockpitPrimitiveLayer = () => {
@@ -305,6 +317,20 @@
 	if (aggregationSource) aggregationSource.show = visible
 }
 
+const setDroneVisibility = visible => {
+	if (droneTrackBillboardCollection) droneTrackBillboardCollection.show = visible
+	if (droneTrackPolylineCollection) droneTrackPolylineCollection.show = visible
+	if (!visible && selectedTargetType.value === 'drone') {
+		closePopup()
+	}
+}
+const getStageByHeight = height => {
+	if (height == null) return 'detail'
+	if (height >= CLUSTER_HEIGHT) return 'cluster'
+	if (height <= DETAIL_HEIGHT) return 'detail'
+	return 'mid'
+}
+
 const ensureCountyCenterMap = () => {
 	if (countyCenterMap.size) return
 	const geojson = JSON.parse(jaGeojsonRaw)
@@ -316,24 +342,44 @@
 	})
 }
 
-const buildSimulatedTrackPoints = base => {
-	ensureCountyCenterMap()
-	const anchor = base || { longitude: 116.397, latitude: 39.908 }
-	const offsets = [
-		[0, 0, 120],
-		[0.02, 0.012, 150],
-		[0.04, 0.006, 180],
-		[0.06, -0.008, 200],
-		[0.03, -0.02, 160],
-		[0.0, -0.015, 140],
-		[-0.02, -0.005, 120],
-		[-0.01, 0.01, 130],
+const buildSimulatedTrackPoints = () => {
+	return [
+		{
+			longitude: 114.963191,
+			latitude: 27.136716,
+			height: 120
+		},
+		{
+			longitude:114.957308,
+			latitude: 27.138452,
+			height: 120
+		},
+		{
+			longitude:114.952,
+			latitude: 27.136317,
+			height: 120
+		},
+		{
+			longitude:114.949293,
+			latitude: 27.133864,
+			height: 120
+		},
+		{
+			longitude:114.944666,
+			latitude: 27.130526,
+			height: 120
+		},
+		{
+			longitude:114.945909,
+			latitude:27.127845,
+			height: 120
+		},
+		{
+			longitude:114.962974,
+			latitude:27.136242,
+			height: 120
+		},
 	]
-	return offsets.map(item => ({
-		longitude: anchor.longitude + item[0],
-		latitude: anchor.latitude + item[1],
-		height: item[2],
-	}))
 }
 
 
@@ -422,13 +468,11 @@
 const renderSimulatedDroneTrack = () => {
 	if (!viewer) return
 	clearDroneTrackEntities()
-	ensureCountyCenterMap()
 	ensureDroneTrackCollections()
+	droneTrackBillboardCollection.show = detailVisible.value
+	droneTrackPolylineCollection.show = detailVisible.value
 	droneTrackRuntime = []
-	const centers = Array.from(countyCenterMap.entries())
-	const baseCenters = centers.length
-		? centers.slice(0, 4)
-		: [['默认区域', { longitude: 116.397, latitude: 39.908 }]]
+	const baseCenters = [['默认区域', { longitude:114.958541, latitude: 27.121917 }]]
 	const segmentDuration = 6
 	const baseTrackColor = Cesium.Color.fromCssColorString('red')
 	baseCenters.forEach(([centerName, center], trackIndex) => {
@@ -454,7 +498,7 @@
 		})
 		const droneId = `drone-track-${trackIndex}`
 		const billboard = droneTrackBillboardCollection.add({
-			position: positions[0],
+			position: positions[positions.length - 1],
 			image: droneIcon,
 			width: 36,
 			height: 36,
@@ -511,7 +555,8 @@
 		const position = getDevicePosition(item)
 		if (!position) return
 		const entityId = `online-device-${item.id ?? index}-${index}`
-		addDeviceRings(position, ringFillInstancesByStyle, ringOutlineInstancesByStyle)
+		const rangeMeters = getDeviceRange(item)
+		addDeviceRings(position, ringFillInstancesByStyle, ringOutlineInstancesByStyle, rangeMeters)
 		const billboard = deviceBillboardCollection.add({
 			position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
 			image: equipmentIcon,
@@ -528,9 +573,20 @@
 	ringFillInstancesByStyle.forEach((instances, index) => {
 		if (!instances.length) return
 		const ring = RING_STYLES[index]
+		const alpha = ring.alpha ?? 0.64
+		const innerRatio =
+			typeof ring.innerRatio === 'number'
+				? ring.innerRatio
+				: ring.inner && ring.outer
+					? Math.min(Math.max(ring.inner / ring.outer, 0), 0.9)
+					: 0
 		const material = createRadialGradientMaterial(
-			Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(0.64),
-			Cesium.Color.fromCssColorString(ring.gradient[1]).withAlpha(0.64)
+			Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(alpha),
+			Cesium.Color.fromCssColorString(ring.gradient[1]).withAlpha(alpha),
+			{
+				gamma: 1.7,
+				innerCutoff: innerRatio,
+			}
 		)
 		const primitive = new Cesium.GroundPrimitive({
 			geometryInstances: instances,
@@ -629,7 +685,7 @@
 const renderDefenseZones = zones => {
 	if (!viewer) return
 	clearDefenseZoneEntities()
-	const result = buildZonePrimitives(zones, '#19D266', '#2AEDBF', '#012B11')
+	const result = buildZonePrimitives(zones, '#72F3FF', '#72F3FF', '#0A7C88')
 	defenseZonePrimitive = result.primitive
 	defenseZoneOutlinePrimitive = result.outlinePrimitive
 	if (defenseZonePrimitive) defenseZonePrimitive.show = detailVisible.value
@@ -640,7 +696,7 @@
 const renderPartitions = zones => {
 	if (!viewer) return
 	clearPartitionEntities()
-	const result = buildZonePrimitives(zones, '#FFCD2A', '#FFC609', '#583300')
+	const result = buildZonePrimitives(zones, '#FFD772', '#FFD772', '#A86B00')
 	partitionPrimitive = result.primitive
 	partitionOutlinePrimitive = result.outlinePrimitive
 	if (partitionPrimitive) partitionPrimitive.show = detailVisible.value
@@ -693,7 +749,7 @@
 				width: 66.73,
 				height: 43,
 				verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
-				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+				// disableDepthTestDistance: Number.POSITIVE_INFINITY,
 			},
 			label: {
 				text: `${count}`,
@@ -706,7 +762,6 @@
 
 				pixelOffset: new Cesium.Cartesian2(0, -35),
 				disableDepthTestDistance: Number.POSITIVE_INFINITY,
-				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
 			},
 		})
 	})
@@ -855,13 +910,14 @@
 	const showCluster = stage === 'cluster'
 	setClusterVisibility(showCluster)
 	setDetailVisibility(!showCluster)
+	setDroneVisibility(!showCluster)
 }
 
 const handleMapReady = ({ viewer: mapViewer }) => {
 	viewer = mapViewer
 	ensureCockpitPrimitiveLayer()
 	const height = viewer?.camera?.positionCartographic?.height
-	const stage = height != null && height >= 100000 ? 'cluster' : 'detail'
+	const stage = getStageByHeight(height)
 	updateStageDisplay(stage)
 	renderDeviceEntities(props.onlineDevices)
 	loadDefenseZones()

--
Gitblit v1.9.3