Cesium@1.138中实现多边形编辑器
/** * 多边形编辑器 * 功能根据经纬度生成多边形支持点击多边形显示控制点及虚线轮廓拖拽控制点修改多边形并输出最新坐标 */ class PolygonEditor { /** * 构造函数 * param {Object} Cesium - Cesium 对象 * param {Object} viewer - Cesium Viewer 实例 * param {Array} lngLatArray - 经纬度二维数组例如 [[lng1, lat1], [lng2, lat2], ...] * param {Object} options - 配置项 * param {String} options.id - 多边形实体的 ID * param {Object} options.properties - 多边形的自定义属性 * param {Function} options.onShapeClick - 点击多边形时的回调函数参数为属性 * param {Function} options.onEditEnd - 拖拽结束时的回调函数参数为(最新经纬度数组, 属性) */ constructor(Cesium, viewer, lngLatArray, options {}) { this.Cesium Cesium; this.viewer viewer; // 保存配置项 this.options options; this.shapeId options.id || polygon-shape- Cesium.createGuid(); this.shapeProperties options.properties || {}; // 存储控制点笛卡尔坐标多边形的顶点即控制点 this.controlPoints lngLatArray.map(lngLat Cesium.Cartesian3.fromDegrees(lngLat[0], lngLat[1]) ); // 存储拖拽点实体 this.controlPointEntities []; // 存储多边形实体 this.shapeEntity null; // 存储虚线轮廓实体 this.outlineEntity null; // 拖拽状态 this.isDragging false; this.draggingIndex -1; // 事件处理器 this.handler new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); this._init(); } /** * 初始化 */ _init() { this._createShapeEntity(); this._createOutlineEntity(); // 创建虚线轮廓 this._createControlPoints(); this._bindEvents(); } /** * 创建多边形实体使用 CallbackProperty 实现无闪烁的动态更新 */ _createShapeEntity() { const Cesium this.Cesium; this.shapeEntity this.viewer.entities.add({ id: this.shapeId, // 绑定自定义属性 properties: new Cesium.PropertyBag(this.shapeProperties), polygon: { // 核心优化使用 CallbackProperty 每帧返回多边形层级 hierarchy: new Cesium.CallbackProperty(() { return new Cesium.PolygonHierarchy(this.controlPoints); }, false), material: Cesium.Color.fromBytes(0, 255, 255, 80), // 半透明青色填充 outline: true, outlineColor: Cesium.Color.fromBytes(0, 255, 255, 255), outlineWidth: 2, // 默认贴地 classificationType: new Cesium.ConstantProperty(Cesium.ClassificationType.BOTH) } }); } /** * 创建用于编辑态显示的虚线轮廓初始隐藏 */ _createOutlineEntity() { const Cesium this.Cesium; const self this; this.outlineEntity this.viewer.entities.add({ polyline: { // 闭合线段将第一个点追加到末尾 positions: new Cesium.CallbackProperty(() { if (self.controlPoints.length 0) { return [...self.controlPoints, self.controlPoints[0]]; } return self.controlPoints; }, false), width: 2, material: new Cesium.PolylineDashMaterialProperty({ color: Cesium.Color.fromCssColorString(#FFFF00).withAlpha(1.0), // 黄色虚线 dashLength: 16.0 }), show: false // 初始隐藏 } }); } /** * 创建拖拽控制点实体初始隐藏 */ _createControlPoints() { const Cesium this.Cesium; this.controlPointEntities this.controlPoints.map((position, index) { return this.viewer.entities.add({ id: polygon-control-point- index - Cesium.createGuid(), position: position, point: { pixelSize: 12, color: Cesium.Color.fromCssColorString(#FF4500).withAlpha(1.0), outlineColor: Cesium.Color.WHITE, outlineWidth: 2, show: false } }); }); } /** * 绑定鼠标事件 */ _bindEvents() { const Cesium this.Cesium; const viewer this.viewer; // 鼠标左键按下 this.handler.setInputAction((movement) { const pickedObject viewer.scene.pick(movement.position); // 1. 判断是否点击到了控制点 if (Cesium.defined(pickedObject) Cesium.defined(pickedObject.id)) { const entityId pickedObject.id.id; const index this.controlPointEntities.findIndex(e e.id entityId); if (index ! -1) { this.isDragging true; this.draggingIndex index; // 拖拽时取消贴地防止地形异步重采样导致卡顿 this.shapeEntity.polygon.classificationType new Cesium.ConstantProperty(undefined); viewer.scene.screenSpaceCameraController.enableRotate false; viewer.scene.screenSpaceCameraController.enableTranslate false; viewer.scene.screenSpaceCameraController.enableZoom false; viewer.scene.screenSpaceCameraController.enableTilt false; return; } } // 2. 判断是否点击到了多边形本身 if (Cesium.defined(pickedObject) pickedObject.id this.shapeEntity) { // 显示控制点和虚线轮廓 this._showControlPoints(true); // 触发点击回调输出属性 if (this.options.onShapeClick) { const props this._getProperties(pickedObject.id); this.options.onShapeClick(props); } } else { // 3. 点击了空白区域隐藏控制点和虚线轮廓 this._showControlPoints(false); } }, Cesium.ScreenSpaceEventType.LEFT_DOWN); // 鼠标移动拖拽更新 this.handler.setInputAction((movement) { if (this.isDragging) { const cartesian viewer.camera.pickEllipsoid( movement.endPosition, viewer.scene.globe.ellipsoid ); if (cartesian) { this.controlPoints[this.draggingIndex] cartesian; this.controlPointEntities[this.draggingIndex].position new Cesium.ConstantProperty(cartesian); // 强制请求渲染CallbackProperty 会自动更新多边形顶点及虚线顶点 viewer.scene.requestRender(); } } }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); // 鼠标左键抬起结束拖拽 this.handler.setInputAction(() { if (this.isDragging) { this.isDragging false; this.draggingIndex -1; // 恢复贴地 this.shapeEntity.polygon.classificationType new Cesium.ConstantProperty(Cesium.ClassificationType.BOTH); viewer.scene.screenSpaceCameraController.enableRotate true; viewer.scene.screenSpaceCameraController.enableTranslate true; viewer.scene.screenSpaceCameraController.enableZoom true; viewer.scene.screenSpaceCameraController.enableTilt true; viewer.scene.requestRender(); // 触发编辑完成回调输出最新坐标和属性 if (this.options.onEditEnd) { const latestCoords this.getLngLatPositions(); const props this._getProperties(this.shapeEntity); this.options.onEditEnd(latestCoords, props); } } }, Cesium.ScreenSpaceEventType.LEFT_UP); } /** * 提取实体的自定义属性为普通对象 */ _getProperties(entity) { const props {}; const propertyNames entity.properties.propertyNames; propertyNames.forEach(name { props[name] entity.properties[name].getValue(); }); return props; } /** * 控制拖拽点与虚线轮廓的显示与隐藏 */ _showControlPoints(show) { this.controlPointEntities.forEach(entity { entity.point.show new this.Cesium.ConstantProperty(show); }); // 同步控制虚线轮廓显隐 if (this.outlineEntity) { this.outlineEntity.polyline.show new this.Cesium.ConstantProperty(show); } this.viewer.scene.requestRender(); } /** * 获取当前控制点的经纬度数组 */ getLngLatPositions() { const Cesium this.Cesium; const cartographics this.controlPoints.map(point Cesium.Cartographic.fromCartesian(point) ); return cartographics.map(carto [ Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude) ]); } /** * 销毁实例释放资源 */ destroy() { if (this.handler) { this.handler.destroy(); this.handler null; } if (this.shapeEntity) { this.viewer.entities.remove(this.shapeEntity); this.shapeEntity null; } if (this.outlineEntity) { this.viewer.entities.remove(this.outlineEntity); this.outlineEntity null; } if (this.controlPointEntities.length 0) { this.controlPointEntities.forEach(entity { this.viewer.entities.remove(entity); }); this.controlPointEntities []; } } } export default PolygonEditor;使用const coordsArray [ [118.4723743, 44.8237461], [118.4774253, 44.8220511], [118.4765083, 44.8226795], ]; const polygonEditor new PolygonEditor(Cesium, viewer, coordsArray, { id: my-custom-polygon-id, properties: { name: 测试区域, type: 禁区, }, onShapeClick: (props) { console.log(点击了多边形属性为:, props); }, onEditEnd: (newCoords, props) { console.log(拖拽结束最新顶点坐标为:, newCoords); console.log(多边形的属性为:, props); }, });