From 8049ae0a3c7ea0cf8e3fe0b4adf32f98439b8591 Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Fri, 16 Jan 2026 14:48:17 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'
---
applications/task-work-order/src/utils/cesium/DrawPolygon.js | 301 ++++++++++++++++++++++++++++++++++++++++---------
1 files changed, 243 insertions(+), 58 deletions(-)
diff --git a/applications/task-work-order/src/views/layerManagement/components/utils.js b/applications/task-work-order/src/utils/cesium/DrawPolygon.js
similarity index 65%
rename from applications/task-work-order/src/views/layerManagement/components/utils.js
rename to applications/task-work-order/src/utils/cesium/DrawPolygon.js
index 7001570..6c37a35 100644
--- a/applications/task-work-order/src/views/layerManagement/components/utils.js
+++ b/applications/task-work-order/src/utils/cesium/DrawPolygon.js
@@ -1,7 +1,8 @@
import * as Cesium from 'cesium'
-import _, { cloneDeep, throttle } from 'lodash'
import * as turf from '@turf/turf'
import { boxTransformScale } from '@/utils/turfFunc'
+import { ElMessage } from 'element-plus'
+import { flyVisual, getPointPositionsHeight } from '@/utils/cesium/mapUtil'
/**
* 多边形绘制与编辑工具类
@@ -14,8 +15,6 @@
*/
export class DrawPolygon {
constructor() {
- // 是否绘制
- this.whetherToDraw =false
// 图斑预览模式标记
this.isPureSpotPreview = false
// 是否删除测区
@@ -39,6 +38,8 @@
// 存储端点的 DataSource
this.editPolygonDataSource = null
this.editPolygonPointDataSource = null
+ // 存储中点(边中点)的 DataSource:用于编辑态插入新端点
+ this.editPolygonMidPointDataSource = null
// 鼠标事件处理器
this.handler = null
// 右键菜单 DOM
@@ -63,15 +64,17 @@
this.delPoint = this.delPoint.bind(this)
// 外部订阅者
this.listeners = []
-
}
// 实体命名常量
static ENTITY_NAMES = {
POLYGON: '区域-面',
POINT: '区域-端点',
+ // 编辑态中点(用于快速插入新端点)
+ MID_POINT: '区域-中点',
POLYGON_ID: 'planar-route-polygon',
POINT_ID_PREFIX: 'planar-route-point',
+ MID_POINT_ID_PREFIX: 'planar-route-mid-point',
}
// 颜色常量
@@ -98,10 +101,20 @@
// 编辑图斑
editThePatch(data) {
this.isPreviewMode = data
- }
- // 绘制
- drawTheArea(data){
- this.whetherToDraw = data
+ // 关闭编辑能力时隐藏端点/中点,并清空中点,避免残留交互
+ if (!this.isPreviewMode) {
+ this.editPolygonPointDataSource && (this.editPolygonPointDataSource.entities.show = false)
+ this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = false)
+ this.editPolygonMidPointDataSource?.entities?.removeAll?.()
+ return
+ }
+
+ if (this.editingMode) {
+ this.editPolygonPointDataSource && (this.editPolygonPointDataSource.entities.show = true)
+ this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = true)
+ this.rebuildEditPoints()
+ this.rebuildMidPoints()
+ }
}
// 删除测区
deleteTheArea(data) {
@@ -110,12 +123,6 @@
// 开始绘制
startDrawing() {
- if (!this.whetherToDraw) {
-
- return;
- }
-
-
this.drawingMode = true
this.curPolygon = new Cesium.PolygonHierarchy()
@@ -130,9 +137,16 @@
this.viewer?.dataSources.add(this.editPolygonPointDataSource)
}
+ // 中点数据源:编辑模式下用于“边上插点”
+ if (!this.editPolygonMidPointDataSource) {
+ this.editPolygonMidPointDataSource = new Cesium.CustomDataSource('editPolygonMidPointDataSource')
+ this.viewer?.dataSources.add(this.editPolygonMidPointDataSource)
+ }
+
// 清空之前的点
this.editPolygonDataSource?.entities.removeAll()
this.editPolygonPointDataSource?.entities.removeAll()
+ this.editPolygonMidPointDataSource?.entities.removeAll()
}
// 创建多边形实体(含边界线)
@@ -177,10 +191,162 @@
ind: pointIndex, // 索引记录
},
})
-
-
}
+ createMidPointEntity(startInd, position) {
+ // 使用 CallbackProperty 让中点位置随端点拖拽实时计算
+ // startInd 表示该中点属于边:(startInd) -> (startInd + 1)
+ const updatePosition = () => {
+ const positions = this.curPolygon?.positions
+ if (!positions || positions.length < 2) return position
+
+ const n = positions.length
+ const p1 = positions[startInd]
+ const p2 = positions[(startInd + 1) % n]
+ if (!p1 || !p2) return position
+
+ return Cesium.Cartesian3.midpoint(p1, p2, new Cesium.Cartesian3())
+ }
+
+ this.editPolygonMidPointDataSource.entities.add({
+ name: DrawPolygon.ENTITY_NAMES.MID_POINT,
+ id: `${DrawPolygon.ENTITY_NAMES.MID_POINT_ID_PREFIX}${startInd}`,
+ position: new Cesium.CallbackProperty(updatePosition, false),
+ point: {
+ pixelSize: 10,
+ color: Cesium.Color.fromCssColorString('rgba(255, 255, 255, .7)'),
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
+ },
+ customData: {
+ startInd,
+ },
+ })
+ }
+
+ rebuildEditPoints() {
+ // 插入/删除端点后:端点实体 id 与 customData.ind 需要全量重建,保证索引与 positions 一致
+ if (!this.isPreviewMode) return
+ if (!this.editPolygonPointDataSource || !this.curPolygon?.positions) return
+
+ this.editPolygonPointDataSource.entities.removeAll()
+ this.curPolygon.positions.forEach((position, index) => {
+ this.editPolygonPointDataSource.entities.add({
+ name: DrawPolygon.ENTITY_NAMES.POINT,
+ id: `${DrawPolygon.ENTITY_NAMES.POINT_ID_PREFIX}${index}`,
+ position: position.clone(),
+ point: {
+ pixelSize: 14,
+ color: Cesium.Color.WHITE,
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
+ },
+ customData: {
+ ind: index,
+ },
+ })
+ })
+ }
+
+ rebuildMidPoints() {
+ // 仅在“顶点数量变化/切换编辑态”时维护中点实体数量
+ // 拖拽过程中中点位置由 CallbackProperty 自动更新,不需要重建
+ if (!this.isPreviewMode) return
+ if (!this.editPolygonMidPointDataSource || !this.curPolygon?.positions) return
+
+ const positions = this.curPolygon.positions
+ const n = positions.length
+ const entities = this.editPolygonMidPointDataSource.entities
+
+ if (!this.editingMode || n < 2) {
+ entities.removeAll()
+ return
+ }
+
+ const neededIds = new Set()
+ for (let i = 0; i < n; i += 1) {
+ const id = `${DrawPolygon.ENTITY_NAMES.MID_POINT_ID_PREFIX}${i}`
+ neededIds.add(id)
+ // 缺少则补齐:保证每条边都有一个中点实体
+ if (!entities.getById(id)) {
+ const p1 = positions[i]
+ const p2 = positions[(i + 1) % n]
+ if (!p1 || !p2) continue
+ const mid = Cesium.Cartesian3.midpoint(p1, p2, new Cesium.Cartesian3())
+ this.createMidPointEntity(i, mid)
+ }
+ }
+
+ entities.values.slice().forEach(entity => {
+ if (entity?.name !== DrawPolygon.ENTITY_NAMES.MID_POINT) return
+ // 多余则移除:例如删除端点导致边数量减少
+ if (!neededIds.has(entity.id)) {
+ entities.remove(entity)
+ }
+ })
+ }
+
+ insertPointFromMidPoint(midPointEntity) {
+ // 点击中点:在对应边上插入一个新端点(白点),并立即进入拖拽态
+ if (!this.isPreviewMode) return
+ if (!this.editingMode) return
+ if (!midPointEntity?.customData) return
+
+ const startInd = midPointEntity.customData.startInd
+ // 中点位置可能是 CallbackProperty,需要取当前时刻的值
+ const positionProperty = midPointEntity.position
+ const entityPosition = positionProperty?.getValue
+ ? positionProperty.getValue(Cesium.JulianDate.now())
+ : positionProperty
+ if (!entityPosition) return
+
+ // 插入到 startInd 与 startInd+1 之间
+ const insertIndex = Math.min(Math.max(startInd + 1, 0), this.curPolygon.positions.length)
+ this.curPolygon.positions.splice(insertIndex, 0, entityPosition.clone ? entityPosition.clone() : entityPosition)
+
+ this.rebuildEditPoints()
+ this.rebuildMidPoints()
+
+ const newPointEntity = this.editPolygonPointDataSource?.entities?.getById(
+ `${DrawPolygon.ENTITY_NAMES.POINT_ID_PREFIX}${insertIndex}`
+ )
+ if (newPointEntity) {
+ this.isDragging = true
+ this.draggedEntity = newPointEntity
+ this.currentDragPointPosition = this.curPolygon.positions[insertIndex]
+ this.disableMapControl()
+ }
+ this.notify('getPolygonPositions', this.curPolygon.positions)
+ }
+ // 清除不在范围内的点
+ removeLastInvalidPoint() {
+ const posLen = this.curPolygon.positions.length
+ if (posLen === 0) return
+
+ let targetPointId = ''
+ let removeCount = 0
+
+ if (posLen === 2) {
+ removeCount = 2
+ targetPointId = `${DrawPolygon.ENTITY_NAMES.POINT_ID_PREFIX}0`
+ } else if (posLen > 2) {
+ removeCount = 1
+ const pointIndex = posLen - 2
+ targetPointId = `${DrawPolygon.ENTITY_NAMES.POINT_ID_PREFIX}${pointIndex}`
+ }
+
+ this.curPolygon.positions.splice(posLen - removeCount, removeCount)
+
+ const invalidPoint = this.editPolygonPointDataSource.entities.getById(targetPointId)
+ if (invalidPoint) {
+ this.editPolygonPointDataSource.entities.remove(invalidPoint)
+ }
+
+ if (this.curPolygon.positions.length === 0 && this.polygonEntity) {
+ this.editPolygonDataSource.entities.remove(this.polygonEntity)
+ this.polygonEntity = null
+ }
+ }
// 添加一个点
addPosition(position, isAdd = true) {
// 第一个点要重复压入一次,形成动态绘制效果
@@ -211,12 +377,18 @@
const pickedEntity = this.viewer.scene.pick(movement.position)?.id
const isPoint = pickedEntity?.name === DrawPolygon.ENTITY_NAMES.POINT
+ const isMidPoint = pickedEntity?.name === DrawPolygon.ENTITY_NAMES.MID_POINT
if (pickedEntity && isPoint) {
this.isDragging = true
this.draggedEntity = pickedEntity
this.currentDragPointPosition = this.curPolygon.positions[this.draggedEntity?.customData.ind]
this.disableMapControl() // 禁止地图交互
+ return
+ }
+
+ if (pickedEntity && isMidPoint) {
+ this.insertPointFromMidPoint(pickedEntity)
}
}
@@ -249,6 +421,7 @@
this.isDragging = false
this.draggedEntity = null
this.enableMapControl()
+ this.rebuildMidPoints()
}
}
@@ -262,7 +435,7 @@
// 编辑模式下,拖拽点实时更新
if (this.editingMode && this.draggedEntity?.customData) {
this.draggedEntity.position = cartesian
- this.curPolygon.positions[this.draggedEntity?.customData.ind] = cartesian
+ this.curPolygon.positions[this.draggedEntity?.customData.ind] = cartesian
}
// 绘制模式下,实时更新最后一个点
@@ -293,27 +466,28 @@
// 鼠标左键点击
handleLeftClick(click) {
this.removeMenuPopup()
+
const pickedAllEntity = this.viewer.scene.drillPick(click.position).filter(i => i.id)
+ const isStartPoint = pickedAllEntity.find(i => i.id.name === '起飞点')
const isPolygonPoint = pickedAllEntity.find(i => i.id.name === DrawPolygon.ENTITY_NAMES.POINT)
const isPolygon = pickedAllEntity.find(i => i.id.name === DrawPolygon.ENTITY_NAMES.POLYGON)
- if (this.drawingMode && !this.whetherToDraw) {
-
- return; // 阻断后续绘制逻辑
- }
+
+ if (isStartPoint) return
+
// 如果不是绘制模式
if (!this.drawingMode) {
if (!isPolygon) {
-
-
this.editingMode = false
this.editPolygonPointDataSource.entities.show = false
+ this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = false)
return
}
if (isPolygon) {
-
this.editingMode = true
this.editPolygonPointDataSource.entities.show = true
+ this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = true)
+ this.rebuildMidPoints()
}
return
}
@@ -334,13 +508,22 @@
const cartesian = this.viewer.scene.pickPosition(click.position)
if (!cartesian) return
+ let arr = [...this.curPolygon.positions]
+ arr.pop()
+ // 校验多边形是否自交
+ if (arr.length > 2 && !this.curDragPointIsValid([...arr, cartesian])) {
+ ElMessage.warning('测区不支持交叉绘制,请重新绘制')
+ return
+ }
+
this.addPosition(cartesian)
}
// 鼠标右键点击(弹出菜单)
- handleRightClick(click) {
+ handleRightClick(click) {
const that = this
if (that.drawingMode) return
+
that.removeMenuPopup()
const pickedAllEntity = that.viewer.scene.drillPick(click.position).filter(i => i.id)
@@ -351,16 +534,14 @@
} = click
let pickedEntity, tooltipEvent, menuType
- if (isPolygon) {
- pickedEntity = isPolygon
- tooltipEvent = that.delPolygon
- menuType = 'polygon'
-
- } else if (isEditPoint) {
+ if (isEditPoint) {
pickedEntity = isEditPoint
tooltipEvent = that.delPoint
menuType = 'edit-point'
-
+ } else if (isPolygon) {
+ pickedEntity = isPolygon
+ tooltipEvent = that.delPolygon
+ menuType = 'polygon'
}
if (pickedEntity && this.isDeleteTheArea) {
@@ -385,6 +566,11 @@
this.editPolygonPointDataSource.entities.removeAll()
this.editPolygonPointDataSource = null
}
+
+ if (this.editPolygonMidPointDataSource) {
+ this.editPolygonMidPointDataSource.entities.removeAll()
+ this.editPolygonMidPointDataSource = null
+ }
this.editingMode = false
this.polygonEntity = null
this.curPolygon = null
@@ -398,6 +584,8 @@
this.drawingMode = false
this.editingMode = true
this.editPolygonPointDataSource.entities.show = true
+ this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = true)
+ this.rebuildMidPoints()
if (!this.curDragPointIsValid(this.curPolygon.positions)) {
this.isShowWaringTip = true
@@ -421,7 +609,12 @@
this.notify('getPolygonPositions', [])
this.startDrawing()
}
-
+ // 删除图斑
+ delSpot() {
+ this.removeEntities()
+ this.isPreviewMode = true
+ this.startDrawing()
+ }
// 删除端点
delPoint() {
if (this.curPolygon.positions.length <= 3) {
@@ -431,13 +624,9 @@
if (!this.delPolygonPoint) return
this.curPolygon.positions.splice(this.delPolygonPoint.customData.ind, 1)
- this.editPolygonPointDataSource.entities.remove(this.delPolygonPoint)
this.removeMenuPopup()
-
- // 更新剩余点索引
- this.editPolygonPointDataSource.entities.values.forEach((item, index) => {
- item.customData.ind = index
- })
+ this.rebuildEditPoints()
+ this.rebuildMidPoints()
this.notify('getPolygonPositions', this.curPolygon.positions)
}
@@ -454,7 +643,6 @@
menuPopup.id = 'planarPolygonEditMenu'
menuPopup.className = 'planar-polygon-edit-menu'
-
const menuItems =
type === 'polygon'
? [{ title: '删除测区', class: 'del-planar-polygon' }]
@@ -466,7 +654,6 @@
titleDiv.className = item.class
menuPopup.appendChild(titleDiv)
})
-
this.isPreviewMode = true
menuPopupVBox.appendChild(menuPopup)
return menuPopupVBox
@@ -522,23 +709,18 @@
return intersections.features.length === 0
}
-
// 初始化已有多边形
- initPolygon(viewer, positions, isPurePreview = false) {
+ async initPolygon(viewer, positions, isPurePreview = false) {
this.initHandler(viewer)
-
- if (this.whetherToDraw) {
- this.startDrawing();
- } else {
- // 新增:不允许绘制时,确保绘制模式为 false,避免误触发
- this.drawingMode = false;
-
- }
-
+ this.isPureSpotPreview = isPurePreview
+ this.startDrawing()
let newPosition = positions.map(item => {
- return Cesium.Cartesian3.fromDegrees(Number(item.lng), Number(item.lat), Number(item.height))
+ return Cesium.Cartesian3.fromDegrees(Number(item.lng), Number(item.lat), Number(item?.height || 0))
})
-
+ // 预览航线的时候调用
+ if (!this.isPureSpotPreview) {
+ this.notify('getPolygonPositions', newPosition)
+ }
newPosition.forEach(item => {
this.addPosition(item, false)
@@ -555,17 +737,20 @@
duration: 0.5,
})
+ let pointList = await getPointPositionsHeight(positions, viewer)
+ flyVisual({ positionsData: pointList.map(item => [item.lng, item.lat, item.ASL]), viewer })
+
this.drawingMode = false
this.editingMode = true
+ this.editPolygonPointDataSource && (this.editPolygonPointDataSource.entities.show = true)
+ this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = true)
+ this.rebuildMidPoints()
}
// 初始化事件处理器
initHandler(viewer) {
this.viewer = viewer
-
- if(this.whetherToDraw){
- this.startDrawing()
- }
+ this.startDrawing()
if (!this.handler) {
this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
--
Gitblit v1.9.3