吉安感知网项目-前端
shuishen
3 days ago 6e88705bd5b443a259b24c17c8a299765d059d96
applications/drone-command/src/utils/cesium/shapeTools/draw/DrawPolygonTool.js
@@ -11,6 +11,69 @@
   fill: style?.fill || DEFAULT_STYLE.fill,
   outline: style?.outline || DEFAULT_STYLE.outline,
})
const resolveColorWithAlpha = (baseColor, alphaSource) => {
   if (!baseColor) return alphaSource
   const alpha = typeof alphaSource?.alpha === 'number' ? alphaSource.alpha : 1
   return baseColor.withAlpha(alpha)
}
const normalizeRing = points => {
   if (!Array.isArray(points)) return []
   const ring = points.filter(Boolean)
   if (ring.length < 2) return ring
   const first = ring[0]
   const last = ring[ring.length - 1]
   if (Cesium.Cartesian3.equals(first, last)) {
      return ring.slice(0, -1)
   }
   return ring
}
const isSelfIntersecting = positions => {
   const ring = normalizeRing(positions)
   if (ring.length < 4) return false
   const epsilon = 1e-12
   const toPoint = cartesian => {
      const carto = Cesium.Cartographic.fromCartesian(cartesian)
      return { x: carto.longitude, y: carto.latitude }
   }
   const pts = ring.map(toPoint)
   const orientation = (a, b, c) => {
      const value = (b.y - a.y) * (c.x - b.x) - (b.x - a.x) * (c.y - b.y)
      if (Math.abs(value) < epsilon) return 0
      return value > 0 ? 1 : 2
   }
   const onSegment = (a, b, c) =>
      b.x <= Math.max(a.x, c.x) + epsilon &&
      b.x + epsilon >= Math.min(a.x, c.x) &&
      b.y <= Math.max(a.y, c.y) + epsilon &&
      b.y + epsilon >= Math.min(a.y, c.y)
   const segmentsIntersect = (p1, q1, p2, q2) => {
      const o1 = orientation(p1, q1, p2)
      const o2 = orientation(p1, q1, q2)
      const o3 = orientation(p2, q2, p1)
      const o4 = orientation(p2, q2, q1)
      if (o1 !== o2 && o3 !== o4) return true
      if (o1 === 0 && onSegment(p1, p2, q1)) return true
      if (o2 === 0 && onSegment(p1, q2, q1)) return true
      if (o3 === 0 && onSegment(p2, p1, q2)) return true
      if (o4 === 0 && onSegment(p2, q1, q2)) return true
      return false
   }
   const count = pts.length
   for (let i = 0; i < count; i += 1) {
      const p1 = pts[i]
      const q1 = pts[(i + 1) % count]
      for (let j = i + 1; j < count; j += 1) {
         const isAdjacent = j === i || j === i + 1 || (i === 0 && j === count - 1)
         if (isAdjacent) continue
         const p2 = pts[j]
         const q2 = pts[(j + 1) % count]
         if (segmentsIntersect(p1, q1, p2, q2)) return true
      }
   }
   return false
}
export class DrawPolygonTool extends ToolBase {
   constructor(viewer, options = {}) {
@@ -23,10 +86,14 @@
      this.floatPosition = null
      this.isDrawing = false
      this.lastMousePosition = null
      this.isIntersecting = false
      this.style = resolveStyle(options?.style)
   }
   start() {
      if (!this.tooltip) {
         this.tooltip = new MapTooltip(this.viewer)
      }
      this.dataSource = new Cesium.CustomDataSource('draw-polygon')
      this.viewer.dataSources.add(this.dataSource)
      this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas)
@@ -34,7 +101,7 @@
      this.handler.setInputAction(click => this.handleRightClick(click), Cesium.ScreenSpaceEventType.RIGHT_CLICK)
      this.handler.setInputAction(movement => this.handleMouseMove(movement), Cesium.ScreenSpaceEventType.MOUSE_MOVE)
      this.isDrawing = true
      this.tooltip.show(this.getTipText(), { x: 0, y: 0 })
      this.tooltip.hide()
   }
   handleLeftClick(click) {
@@ -49,6 +116,13 @@
      const position = this.getPositionFromScreen(click.position)
      if (!position) return
      const preview = [...this.positions, position]
      if (isSelfIntersecting(preview)) {
         this.isIntersecting = true
         this.tooltip.show(this.getTipText(), click.position)
         return
      }
      this.addPoint(position)
      this.tooltip.show(this.getTipText(), click.position)
@@ -66,7 +140,13 @@
   handleMouseMove(movement) {
      if (!this.isDrawing) return
      this.tooltip.show(this.getTipText(), movement.endPosition)
      const tipText = this.getTipText()
      if (!this.tooltip?.isVisible) {
         this.tooltip?.show(tipText, movement.endPosition)
      } else {
         this.tooltip?.show(tipText)
         this.tooltip?.move(movement.endPosition)
      }
      if (this.positions.length === 0) return
      const position = this.getPositionFromScreen(movement.endPosition)
      if (!position) return
@@ -75,6 +155,11 @@
   }
   getTipText() {
      const preview = this.getPreviewPositions()
      this.isIntersecting = isSelfIntersecting(preview)
      if (this.isIntersecting) {
         return '区域存在交叉,请调整位置'
      }
      const count = this.positions.length
      if (count === 0) {
         return '单击增加点'
@@ -116,27 +201,41 @@
      }
   }
   getPreviewPositions() {
      if (this.positions.length === 0) return []
      if (!this.isDrawing || !this.floatPosition) return this.positions
      return [...this.positions, this.floatPosition]
   }
   createEntities() {
      const getPreviewPositions = () => {
         if (this.positions.length === 0) return []
         if (!this.isDrawing || !this.floatPosition) return this.positions
         return [...this.positions, this.floatPosition]
      const getIntersecting = () => {
         const preview = this.getPreviewPositions()
         this.isIntersecting = isSelfIntersecting(preview)
         return this.isIntersecting
      }
      this.polygonEntity = this.dataSource.entities.add({
         polygon: {
            hierarchy: new Cesium.CallbackProperty(() => new Cesium.PolygonHierarchy(getPreviewPositions()), false),
            material: this.style.fill,
            hierarchy: new Cesium.CallbackProperty(
               () => new Cesium.PolygonHierarchy(this.getPreviewPositions()),
               false
            ),
            material: new Cesium.ColorMaterialProperty(
               new Cesium.CallbackProperty(() => {
                  if (getIntersecting()) return resolveColorWithAlpha(Cesium.Color.RED, this.style.fill)
                  return this.style.fill
               }, false)
            ),
            outline: false,
            heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
            show: new Cesium.CallbackProperty(() => getPreviewPositions().length >= 3, false),
            show: new Cesium.CallbackProperty(() => this.getPreviewPositions().length >= 3, false),
         },
      })
      this.polylineEntity = this.dataSource.entities.add({
         polyline: {
            positions: new Cesium.CallbackProperty(() => {
               const positions = getPreviewPositions()
               const positions = this.getPreviewPositions()
               if (positions.length < 2) return positions
               if (positions.length >= 3) {
                  return [...positions, positions[0]]
@@ -145,8 +244,13 @@
            }, false),
            clampToGround: true,
            width: 2,
            material: this.style.outline,
            show: new Cesium.CallbackProperty(() => getPreviewPositions().length >= 2, false),
            material: new Cesium.ColorMaterialProperty(
               new Cesium.CallbackProperty(() => {
                  if (getIntersecting()) return resolveColorWithAlpha(Cesium.Color.RED, this.style.outline)
                  return this.style.outline
               }, false)
            ),
            show: new Cesium.CallbackProperty(() => this.getPreviewPositions().length >= 2, false),
         },
      })
   }
@@ -180,19 +284,14 @@
      if (this.positions.length < 3) return
      this.isDrawing = false
      this.floatPosition = null
      this.isIntersecting = false
      this.notify('getPolygonPositions', this.positions)
      this.tooltip.hide()
      this.tooltip?.hide()
      this.clearPreviewEntities()
   }
   setStyle(style) {
      this.style = resolveStyle(style)
      if (this.polygonEntity?.polygon) {
         this.polygonEntity.polygon.material = this.style.fill
      }
      if (this.polylineEntity?.polyline) {
         this.polylineEntity.polyline.material = this.style.outline
      }
      if (this.dataSource) {
         this.dataSource.entities.values
            .filter(entity => entity?.name === POINT_ENTITY_NAME)
@@ -209,11 +308,13 @@
      this.dataSource.entities.removeAll()
      this.polygonEntity = null
      this.polylineEntity = null
      this.isIntersecting = false
   }
   getPositionFromScreen(screenPosition) {
      const scene = this.viewer.scene
      const cartesian = scene.pickPosition(screenPosition)
      const ray = scene.camera.getPickRay(screenPosition)
      const cartesian = ray ? scene.globe.pick(ray, scene) : null
      if (cartesian) return cartesian
      return scene.camera.pickEllipsoid(screenPosition, scene.globe.ellipsoid)
   }
@@ -237,5 +338,6 @@
      this.floatPosition = null
      this.isDrawing = false
      this.lastMousePosition = null
      this.isIntersecting = false
   }
}