From f083281ab75f61390a04daa2484f330f8df4178f Mon Sep 17 00:00:00 2001
From: chenyao <1219716595@qq.com>
Date: Thu, 29 Jan 2026 08:44:20 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'

---
 applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditBufferCircleTool.js |  314 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 314 insertions(+), 0 deletions(-)

diff --git a/applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditBufferCircleTool.js b/applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditBufferCircleTool.js
new file mode 100644
index 0000000..1808125
--- /dev/null
+++ b/applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditBufferCircleTool.js
@@ -0,0 +1,314 @@
+import * as Cesium from 'cesium'
+import { MapTooltip } from '../Tooltip'
+import { ToolBase } from '../ToolBase'
+import { buildEllipsePositions, cartesianToLocal } from '../shapeUtils'
+
+const normalizePoints = points =>
+	(points || []).map(point => ({
+		lng: point.lng ?? point.longitude,
+		lat: point.lat ?? point.latitude,
+	}))
+
+export class EditBufferCircleTool extends ToolBase {
+	constructor(viewer) {
+		super(viewer)
+		this.tooltip = new MapTooltip(viewer)
+		this.dataSource = null
+		this.centerCartesian = null
+		this.radiusLevel1 = 0
+		this.radiusLevel2 = 0
+		this.radiusLevel3 = 0
+		this.dragType = null
+		this.circleLevel1Entity = null
+		this.circleLevel2Entity = null
+		this.circleLevel3Entity = null
+		this.circleLevel1OutlineEntity = null
+		this.circleLevel2OutlineEntity = null
+		this.circleLevel3OutlineEntity = null
+		this.centerEntity = null
+		this.radiusLevel1Entity = null
+		this.radiusLevel2Entity = null
+		this.radiusLevel3Entity = null
+	}
+
+	start(data = []) {
+		const inputPoints = Array.isArray(data?.points) ? data.points : data
+		const normalized = normalizePoints(inputPoints)
+		if (!normalized.length) return
+		const centerLng = normalized.reduce((sum, p) => sum + p.lng, 0) / normalized.length
+		const centerLat = normalized.reduce((sum, p) => sum + p.lat, 0) / normalized.length
+		this.centerCartesian = Cesium.Cartesian3.fromDegrees(centerLng, centerLat)
+		const radii = Array.isArray(data?.meta?.bufferRadii) ? data.meta.bufferRadii : null
+		if (radii?.length) {
+			const [r1, r2, r3] = radii
+			this.radiusLevel1 = Number.isFinite(r1) && r1 > 0 ? r1 : 1
+			this.radiusLevel2 = Number.isFinite(r2) && r2 > 0 ? Math.max(r2, this.radiusLevel1) : this.radiusLevel1
+			this.radiusLevel3 = Number.isFinite(r3) && r3 > 0 ? Math.max(r3, this.radiusLevel2) : this.radiusLevel2
+		} else {
+			this.radiusLevel3 = this.estimateRadius(normalized)
+			this.radiusLevel2 = this.radiusLevel3
+			this.radiusLevel1 = this.radiusLevel3
+		}
+
+		this.dataSource = new Cesium.CustomDataSource('edit-buffer-circle')
+		this.viewer.dataSources.add(this.dataSource)
+		this.createEntities()
+		this.initHandler()
+	}
+
+	estimateRadius(points) {
+		const center = Cesium.Cartesian3.clone(this.centerCartesian)
+		let max = 1
+		points.forEach(point => {
+			const cartesian = Cesium.Cartesian3.fromDegrees(point.lng, point.lat)
+			const distance = Cesium.Cartesian3.distance(center, cartesian)
+			max = Math.max(max, distance)
+		})
+		return max
+	}
+
+	initHandler() {
+		this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas)
+		this.handler.setInputAction(evt => this.handleLeftDown(evt), Cesium.ScreenSpaceEventType.LEFT_DOWN)
+		this.handler.setInputAction(evt => this.handleMouseMove(evt), Cesium.ScreenSpaceEventType.MOUSE_MOVE)
+		this.handler.setInputAction(() => this.handleLeftUp(), Cesium.ScreenSpaceEventType.LEFT_UP)
+		this.tooltip.show('拖动控制点编辑', { x: 0, y: 0 })
+	}
+
+	handleLeftDown(evt) {
+		const picked = this.viewer.scene.pick(evt.position)?.id
+		if (!picked) return
+		if (picked.customType === 'circle-center') this.dragType = 'center'
+		if (picked.customType === 'circle-radius-1') this.dragType = 'radius-1'
+		if (picked.customType === 'circle-radius-2') this.dragType = 'radius-2'
+		if (picked.customType === 'circle-radius-3') this.dragType = 'radius-3'
+		if (this.dragType) this.disableMapControl()
+	}
+
+	handleMouseMove(evt) {
+		this.tooltip.move(evt.endPosition)
+		if (!this.dragType) return
+		const position = this.getPositionFromScreen(evt.endPosition)
+		if (!position) return
+
+		if (this.dragType === 'center') {
+			this.centerCartesian = position
+			this.updateRadiusPoints()
+			return
+		}
+
+		const local = cartesianToLocal(this.centerCartesian, position)
+		const radius = Math.max(1, Math.sqrt(local.x * local.x + local.y * local.y))
+		if (this.dragType === 'radius-1') {
+			this.radiusLevel1 = radius
+			if (this.radiusLevel2 < this.radiusLevel1) this.radiusLevel2 = this.radiusLevel1
+			if (this.radiusLevel3 < this.radiusLevel2) this.radiusLevel3 = this.radiusLevel2
+		}
+		if (this.dragType === 'radius-2') {
+			this.radiusLevel2 = Math.max(radius, this.radiusLevel1)
+			if (this.radiusLevel3 < this.radiusLevel2) this.radiusLevel3 = this.radiusLevel2
+		}
+		if (this.dragType === 'radius-3') {
+			this.radiusLevel3 = Math.max(radius, this.radiusLevel2)
+		}
+		this.updateRadiusPoints()
+	}
+
+	handleLeftUp() {
+		if (!this.dragType) return
+		this.dragType = null
+		this.enableMapControl()
+		const positions = buildEllipsePositions(this.centerCartesian, this.radiusLevel3, this.radiusLevel3)
+		this.notify('getPolygonPositions', {
+			positions,
+			meta: {
+				center: this.centerCartesian,
+				bufferRadii: [this.radiusLevel1, this.radiusLevel2, this.radiusLevel3],
+			},
+		})
+	}
+
+	createEntities() {
+		this.circleLevel1Entity = this.dataSource.entities.add({
+			position: new Cesium.CallbackProperty(() => this.centerCartesian, false),
+			ellipse: {
+				semiMajorAxis: new Cesium.CallbackProperty(() => this.radiusLevel1, false),
+				semiMinorAxis: new Cesium.CallbackProperty(() => this.radiusLevel1, false),
+				material: Cesium.Color.fromBytes(247, 20, 20, 128),
+				outline: false,
+				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+			},
+		})
+		this.circleLevel1OutlineEntity = this.dataSource.entities.add({
+			polyline: {
+				positions: new Cesium.CallbackProperty(() => {
+					if (!this.centerCartesian || this.radiusLevel1 <= 0) return []
+					const positions = buildEllipsePositions(this.centerCartesian, this.radiusLevel1, this.radiusLevel1)
+					return positions.length ? [...positions, positions[0]] : positions
+				}, false),
+				clampToGround: true,
+				width: 2,
+				material: Cesium.Color.fromBytes(255, 0, 0, 255),
+			},
+		})
+
+		this.circleLevel2Entity = this.dataSource.entities.add({
+			position: new Cesium.CallbackProperty(() => this.centerCartesian, false),
+			ellipse: {
+				semiMajorAxis: new Cesium.CallbackProperty(() => this.radiusLevel2, false),
+				semiMinorAxis: new Cesium.CallbackProperty(() => this.radiusLevel2, false),
+				material: Cesium.Color.fromBytes(255, 235, 59, 128),
+				outline: false,
+				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+			},
+		})
+		this.circleLevel2OutlineEntity = this.dataSource.entities.add({
+			polyline: {
+				positions: new Cesium.CallbackProperty(() => {
+					if (!this.centerCartesian || this.radiusLevel2 <= 0) return []
+					const positions = buildEllipsePositions(this.centerCartesian, this.radiusLevel2, this.radiusLevel2)
+					return positions.length ? [...positions, positions[0]] : positions
+				}, false),
+				clampToGround: true,
+				width: 2,
+				material: Cesium.Color.fromBytes(255, 235, 59, 255),
+			},
+		})
+
+		this.circleLevel3Entity = this.dataSource.entities.add({
+			position: new Cesium.CallbackProperty(() => this.centerCartesian, false),
+			ellipse: {
+				semiMajorAxis: new Cesium.CallbackProperty(() => this.radiusLevel3, false),
+				semiMinorAxis: new Cesium.CallbackProperty(() => this.radiusLevel3, false),
+				material: Cesium.Color.fromBytes(25, 178, 230, 128),
+				outline: false,
+				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+			},
+		})
+		this.circleLevel3OutlineEntity = this.dataSource.entities.add({
+			polyline: {
+				positions: new Cesium.CallbackProperty(() => {
+					if (!this.centerCartesian || this.radiusLevel3 <= 0) return []
+					const positions = buildEllipsePositions(this.centerCartesian, this.radiusLevel3, this.radiusLevel3)
+					return positions.length ? [...positions, positions[0]] : positions
+				}, false),
+				clampToGround: true,
+				width: 2,
+				material: Cesium.Color.fromBytes(0, 251, 255, 255),
+			},
+		})
+
+		this.centerEntity = this.dataSource.entities.add({
+			position: new Cesium.CallbackProperty(() => this.centerCartesian, false),
+			point: {
+				pixelSize: 12,
+				color: Cesium.Color.WHITE,
+				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+				disableDepthTestDistance: Number.POSITIVE_INFINITY,
+			},
+			customType: 'circle-center',
+		})
+
+		this.radiusLevel1Entity = this.dataSource.entities.add({
+			position: new Cesium.CallbackProperty(() => this.getRadiusPoint(this.radiusLevel1), false),
+			point: {
+				pixelSize: 10,
+				color: Cesium.Color.fromBytes(255, 255, 255, 200),
+				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+				disableDepthTestDistance: Number.POSITIVE_INFINITY,
+			},
+			customType: 'circle-radius-1',
+		})
+
+		this.radiusLevel2Entity = this.dataSource.entities.add({
+			position: new Cesium.CallbackProperty(() => this.getRadiusPoint(this.radiusLevel2), false),
+			point: {
+				pixelSize: 10,
+				color: Cesium.Color.fromBytes(255, 255, 255, 200),
+				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+				disableDepthTestDistance: Number.POSITIVE_INFINITY,
+			},
+			customType: 'circle-radius-2',
+		})
+
+		this.radiusLevel3Entity = this.dataSource.entities.add({
+			position: new Cesium.CallbackProperty(() => this.getRadiusPoint(this.radiusLevel3), false),
+			point: {
+				pixelSize: 10,
+				color: Cesium.Color.fromBytes(255, 255, 255, 200),
+				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+				disableDepthTestDistance: Number.POSITIVE_INFINITY,
+			},
+			customType: 'circle-radius-3',
+		})
+	}
+
+	updateRadiusPoints() {
+		if (this.radiusLevel1Entity) this.radiusLevel1Entity.position = this.getRadiusPoint(this.radiusLevel1)
+		if (this.radiusLevel2Entity) this.radiusLevel2Entity.position = this.getRadiusPoint(this.radiusLevel2)
+		if (this.radiusLevel3Entity) this.radiusLevel3Entity.position = this.getRadiusPoint(this.radiusLevel3)
+	}
+
+	getPositions() {
+		if (!this.centerCartesian) return []
+		return {
+			positions: buildEllipsePositions(this.centerCartesian, this.radiusLevel3, this.radiusLevel3),
+			meta: {
+				center: this.centerCartesian,
+				bufferRadii: [this.radiusLevel1, this.radiusLevel2, this.radiusLevel3],
+			},
+		}
+	}
+
+	getRadiusPoint(radius) {
+		const frame = Cesium.Transforms.eastNorthUpToFixedFrame(this.centerCartesian)
+		const local = new Cesium.Cartesian3(radius, 0, 0)
+		return Cesium.Matrix4.multiplyByPoint(frame, local, new Cesium.Cartesian3())
+	}
+
+	getPositionFromScreen(screenPosition) {
+		const scene = this.viewer.scene
+		const cartesian = scene.pickPosition(screenPosition)
+		if (cartesian) return cartesian
+		return scene.camera.pickEllipsoid(screenPosition, scene.globe.ellipsoid)
+	}
+
+	disableMapControl() {
+		const controller = this.viewer.scene.screenSpaceCameraController
+		controller.enableRotate = false
+		controller.enableTranslate = false
+		controller.enableZoom = false
+	}
+
+	enableMapControl() {
+		const controller = this.viewer.scene.screenSpaceCameraController
+		controller.enableRotate = true
+		controller.enableTranslate = true
+		controller.enableZoom = true
+	}
+
+	destroy() {
+		if (this.dataSource) {
+			this.dataSource.entities.removeAll()
+			this.viewer.dataSources.remove(this.dataSource)
+			this.dataSource = null
+		}
+		if (this.handler) {
+			this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOWN)
+			this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE)
+			this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_UP)
+			this.handler.destroy()
+			this.handler = null
+		}
+		this.tooltip?.destroy()
+		this.tooltip = null
+		this.centerCartesian = null
+		this.radiusLevel1 = 0
+		this.radiusLevel2 = 0
+		this.radiusLevel3 = 0
+		this.circleLevel1OutlineEntity = null
+		this.circleLevel2OutlineEntity = null
+		this.circleLevel3OutlineEntity = null
+		this.dragType = null
+	}
+}

--
Gitblit v1.9.3