Cesium@1.138中实现交互式折线绘制与编辑工具类

Cesium@1.138中实现交互式折线绘制与编辑工具类 /** * Cesium 交互式折线绘制与编辑工具类 */ class PolylineDrawer { /** * param {Object} Cesium - Cesium 命名空间对象 * param {Cesium.Viewer} viewer - Cesium Viewer 实例 * param {Object} options - 可选配置项 * param {Function} options.onFinish - 绘制完成时的回调函数 * param {Function} options.onEdit - 编辑完成拖拽结束时的回调函数接收最新坐标数组 * param {Cesium.Color} options.lineColor - 折线颜色 * param {Cesium.Color} options.pointColor - 顶点颜色 */ constructor(Cesium, viewer, options {}) { this.Cesium Cesium; this.viewer viewer; this.onFinish options.onFinish || (() {}); this.onEdit options.onEdit || (() {}); this.lineColor options.lineColor || this.Cesium.Color.YELLOW.withAlpha(0.8); this.pointColor options.pointColor || this.Cesium.Color.RED; // 内部状态 this.positions []; this.floatingPoint null; this.pointEntities []; this.polylineEntity null; this.handler null; this.isDrawing false; this.isEditing false; // 拖拽状态变量 this.draggedPointIndex null; this.editHandler null; } /** * 开始绘制 */ start() { if (this.isDrawing) return; // 重置状态 this.positions []; this.pointEntities []; this.floatingPoint null; this.isDrawing true; this.isEditing false; // 禁用 Cesium 默认的双击聚焦行为 this.viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction( this.Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK ); // 创建动态折线 Entity this.polylineEntity this.viewer.entities.add({ polyline: { positions: new this.Cesium.CallbackProperty(() { if (this.positions.length 0) return []; return this.floatingPoint ? this.positions.concat([this.floatingPoint]) : this.positions; }, false), width: 2, material: this.lineColor, clampToGround: true, }, }); // 绑定绘制事件 this.handler new this.Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas); this._bindDrawEvents(); } /** * 绑定绘制阶段的交互事件 * private */ _bindDrawEvents() { // 左键点击添加顶点 this.handler.setInputAction((click) { const cartesian this.viewer.scene.pickPosition(click.position); if (!cartesian) return; this.positions.push(cartesian); const point this.viewer.entities.add({ position: cartesian, point: { pixelSize: 6, color: this.pointColor, outlineColor: this.Cesium.Color.WHITE, outlineWidth: 2, disableDepthTestDistance: Number.POSITIVE_INFINITY, }, }); this.pointEntities.push(point); }, this.Cesium.ScreenSpaceEventType.LEFT_CLICK); // 鼠标移动更新预览点 this.handler.setInputAction((movement) { if (this.positions.length 0) return; const p this.viewer.scene.pickPosition(movement.endPosition); if (p) this.floatingPoint p; }, this.Cesium.ScreenSpaceEventType.MOUSE_MOVE); // 结束绘制逻辑双击左键 / 单击右键 const finish () this.finish(); this.handler.setInputAction(finish, this.Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK); this.handler.setInputAction(finish, this.Cesium.ScreenSpaceEventType.RIGHT_CLICK); } /** * 结束绘制并自动进入编辑模式 */ finish() { if (!this.isDrawing) return; this.floatingPoint null; this.isDrawing false; // 销毁绘制事件监听 if (this.handler) { this.handler.destroy(); this.handler null; } console.log(✅ 绘制完成最终坐标点数量:, this.positions.length); this.onFinish(this.positions); // 绘制完成后自动进入编辑模式 this._startEditing(); } /** * 开启拖拽编辑模式 * private */ _startEditing() { if (this.isEditing || this.pointEntities.length 0) return; this.isEditing true; this.editHandler new this.Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas); // 1. 鼠标按下判断是否点中了顶点并【禁用相机交互】 this.editHandler.setInputAction((click) { const picked this.viewer.scene.pick(click.position); if (this.Cesium.defined(picked) picked.id) { const index this.pointEntities.indexOf(picked.id); if (index ! -1) { this.draggedPointIndex index; this.viewer.scene.canvas.style.cursor move; // 核心修复拖拽开始时禁用默认的屏幕空间相机控制器 this.viewer.scene.screenSpaceCameraController.enableInputs false; } } }, this.Cesium.ScreenSpaceEventType.LEFT_DOWN); // 2. 鼠标移动如果正在拖拽则实时更新顶点坐标 this.editHandler.setInputAction((movement) { if (this.draggedPointIndex null) return; // 使用 globe.pick 获取地球表面的三维坐标配合 clampToGround 效果更好 const ray this.viewer.camera.getPickRay(movement.endPosition); const cartesian this.viewer.scene.globe.pick(ray, this.viewer.scene); if (cartesian) { // 更新内存中的坐标数组 this.positions[this.draggedPointIndex] cartesian; // 核心优化使用 CallbackProperty 动态更新位置避免拖拽时的视觉卡顿 this.pointEntities[this.draggedPointIndex].position new this.Cesium.CallbackProperty( () cartesian, false ); } }, this.Cesium.ScreenSpaceEventType.MOUSE_MOVE); // 3. 鼠标抬起结束拖拽【恢复相机交互】触发编辑回调 this.editHandler.setInputAction(() { if (this.draggedPointIndex ! null) { this.draggedPointIndex null; this.viewer.scene.canvas.style.cursor ; // 核心修复拖拽结束时恢复默认的屏幕空间相机控制器 this.viewer.scene.screenSpaceCameraController.enableInputs true; console.log(✏️ 顶点编辑完成最新坐标:, this.positions); this.onEdit(this.positions); } }, this.Cesium.ScreenSpaceEventType.LEFT_UP); } /** * 清除当前绘制的所有实体 */ clear() { this.pointEntities.forEach((e) this.viewer.entities.remove(e)); this.pointEntities []; this.positions []; this.floatingPoint null; } /** * 完全销毁工具实例 */ destroy() { // 退出编辑模式 this.isEditing false; if (this.editHandler) { this.editHandler.destroy(); this.editHandler null; } this.viewer.scene.canvas.style.cursor ; // 确保相机控制器被恢复 this.viewer.scene.screenSpaceCameraController.enableInputs true; // 退出绘制模式 if (this.handler) { this.handler.destroy(); this.handler null; } this.clear(); if (this.polylineEntity) { this.viewer.entities.remove(this.polylineEntity); this.polylineEntity null; } } } export default PolylineDrawer;使用const d new PolylineDrawer(Cesium, viewer); d.start();