☰
Cesium@1.138中实现多边形编辑器
2026/10/16 18:32:19 网站建设 项目流程
/** * 多边形编辑器 * 功能:根据经纬度生成多边形,支持点击多边形显示控制点及虚线轮廓,拖拽控制点修改多边形并输出最新坐标 */ 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); }, });

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询