吉安感知网项目-前端
chenyao
2026-01-29 f083281ab75f61390a04daa2484f330f8df4178f
applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditRectangleTool.js
New file
@@ -0,0 +1,198 @@
import * as Cesium from 'cesium'
import { MapTooltip } from '../Tooltip'
import { ToolBase } from '../ToolBase'
import { getBoundsFromLngLats, rectanglePositionsFromBounds } from '../shapeUtils'
const DEFAULT_STYLE = {
   fill: Cesium.Color.fromBytes(45, 140, 240, 99),
   outline: Cesium.Color.fromBytes(45, 140, 240, 255),
}
const resolveStyle = style => ({
   fill: style?.fill || DEFAULT_STYLE.fill,
   outline: style?.outline || DEFAULT_STYLE.outline,
})
const normalizePoints = points =>
   (points || []).map(point => ({
      lng: point.lng ?? point.longitude,
      lat: point.lat ?? point.latitude,
   }))
export class EditRectangleTool extends ToolBase {
   constructor(viewer, options = {}) {
      super(viewer)
      this.tooltip = new MapTooltip(viewer)
      this.dataSource = null
      this.bounds = null
      this.rectangleEntity = null
      this.outlineEntity = null
      this.cornerEntities = []
      this.dragIndex = null
      this.fixedCorner = null
      this.style = resolveStyle(options?.style)
   }
   start(points = []) {
      const normalized = normalizePoints(points)
      this.bounds = getBoundsFromLngLats(normalized)
      if (!this.bounds) return
      this.dataSource = new Cesium.CustomDataSource('edit-rectangle')
      this.viewer.dataSources.add(this.dataSource)
      this.createEntities()
      this.initHandler()
   }
   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 || picked.customType !== 'rect-corner') return
      this.dragIndex = picked.customData.index
      const corners = this.getCornerCartographics()
      this.fixedCorner = corners[(this.dragIndex + 2) % 4]
      this.disableMapControl()
   }
   handleMouseMove(evt) {
      this.tooltip.move(evt.endPosition)
      if (this.dragIndex == null) return
      const position = this.getPositionFromScreen(evt.endPosition)
      if (!position) return
      const moved = Cesium.Cartographic.fromCartesian(position)
      this.bounds = {
         west: Math.min(moved.longitude, this.fixedCorner.longitude),
         east: Math.max(moved.longitude, this.fixedCorner.longitude),
         south: Math.min(moved.latitude, this.fixedCorner.latitude),
         north: Math.max(moved.latitude, this.fixedCorner.latitude),
      }
      this.updateCorners()
   }
   handleLeftUp() {
      if (this.dragIndex == null) return
      this.dragIndex = null
      this.fixedCorner = null
      this.enableMapControl()
      const positions = rectanglePositionsFromBounds(this.bounds)
      this.notify('getPolygonPositions', positions)
   }
   createEntities() {
      const hierarchy = new Cesium.CallbackProperty(() => {
         const positions = rectanglePositionsFromBounds(this.bounds)
         return new Cesium.PolygonHierarchy(positions)
      }, false)
      this.rectangleEntity = this.dataSource.entities.add({
         polygon: {
            hierarchy,
            material: this.style.fill,
            outline: false,
            heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
         },
      })
      this.outlineEntity = this.dataSource.entities.add({
         polyline: {
            positions: new Cesium.CallbackProperty(() => {
               const positions = rectanglePositionsFromBounds(this.bounds)
               return positions.length ? [...positions, positions[0]] : []
            }, false),
            clampToGround: true,
            width: 2,
            material: this.style.outline,
         },
      })
      this.createCorners()
   }
   createCorners() {
      const positions = rectanglePositionsFromBounds(this.bounds)
      this.cornerEntities = positions.map((position, index) =>
         this.dataSource.entities.add({
            position,
            point: {
               pixelSize: 12,
               color: Cesium.Color.WHITE,
               heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
               disableDepthTestDistance: Number.POSITIVE_INFINITY,
            },
            customType: 'rect-corner',
            customData: { index },
         })
      )
   }
   updateCorners() {
      const positions = rectanglePositionsFromBounds(this.bounds)
      this.cornerEntities.forEach((entity, index) => {
         entity.position = positions[index]
      })
   }
   getPositions() {
      return rectanglePositionsFromBounds(this.bounds)
   }
   getCornerCartographics() {
      return rectanglePositionsFromBounds(this.bounds).map(position =>
         Cesium.Cartographic.fromCartesian(position)
      )
   }
   getPositionFromScreen(screenPosition) {
      const scene = this.viewer.scene
      const cartesian = scene.pickPosition(screenPosition)
      if (cartesian) return cartesian
      return scene.camera.pickEllipsoid(screenPosition, scene.globe.ellipsoid)
   }
   setStyle(style) {
      this.style = resolveStyle(style)
      if (this.rectangleEntity?.polygon) {
         this.rectangleEntity.polygon.material = this.style.fill
      }
      if (this.outlineEntity?.polyline) {
         this.outlineEntity.polyline.material = this.style.outline
      }
   }
   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.bounds = null
      this.dragIndex = null
      this.fixedCorner = null
      this.cornerEntities = []
   }
}