做电子杂志用什么网站,app开发公司怎么查看,快站网如何开始建站,品牌网站建设小蝌蚪#x1f4cb; 文章目录
引言功能概述环境准备核心实现步骤 地图初始化多边形绘制顶点编辑功能颜色与透明度自定义面积计算与显示 常见问题解决方案 多边形颜色显示异常面积标签不可见控制台alpha类型错误地图交互无法恢复 完整代码总结与扩展
引言
Cesium作为一款强大的3D地… 文章目录
引言功能概述环境准备核心实现步骤 地图初始化多边形绘制顶点编辑功能颜色与透明度自定义面积计算与显示 常见问题解决方案 多边形颜色显示异常面积标签不可见控制台alpha类型错误地图交互无法恢复 完整代码总结与扩展
引言
Cesium作为一款强大的3D地理信息可视化库在WebGIS开发中有着广泛的应用。本文将详细介绍如何在Vue框架下基于Cesium实现交互式多边形绘制与编辑功能包括颜色自定义、透明度调整以及面积实时计算等实用功能并解决开发过程中遇到的各种常见问题。
无论是GIS应用、智慧城市还是工程测量系统多边形绘制都是核心功能之一。通过本文的实现方案你将能够快速集成专业的多边形编辑工具到自己的Cesium项目中并掌握Cesium事件处理和状态管理的最佳实践。
功能概述
本文实现的多边形工具具有以下特性
✅ 交互式绘制左键点击添加顶点右键结束绘制 ✅ 顶点编辑拖拽顶点实时调整多边形形状 ✅ 样式自定义支持颜色选择和透明度调整 ✅ 面积计算自动计算并显示多边形面积平方公里 ✅ 交互控制绘制/编辑时禁用地图默认交互完成后恢复
环境准备
在开始之前请确保你的项目中已安装以下依赖
# 安装Cesium
npm install cesium --save# 安装Turf.js用于面积计算
npm install turf/turf --save
核心实现步骤
1. 地图初始化
首先我们需要初始化Cesium地图实例。在Vue组件的mounted钩子中完成地图的加载
import initMap from /config/initMap.js;
import { mapConfig } from /config/mapConfig;export default {data() {return {viewer: null,// 绘制状态管理isDrawing: false,isEditing: false,currentPolygon: null,polygonPoints: [],dynamicPoints: [],vertexEntities: [],vertexHandlers: [], // 顶点事件处理器数组handler: null,polygonColor: #0000FF, // 默认蓝色polygonAlpha: 0.5, // 默认透明度areaLabel: null // 面积标签实体};},mounted() {// 初始化地图this.viewer initMap(mapConfig.gaode.url3, false);// 初始化绘制处理器this.initDrawHandler();}
}
2. 多边形绘制
多边形绘制是通过监听鼠标事件实现的主要分为三个阶段
左键点击添加顶点并更新多边形鼠标移动动态显示多边形轮廓右键点击结束绘制并创建最终多边形
核心代码实现
// 开始绘制多边形
startDrawPolygon() {if (this.isEditing) this.stopEditPolygon();this.isDrawing true;this.polygonPoints [];this.dynamicPoints [];this.disableMapInteraction();// 左键点击添加顶点this.handler.setInputAction(this.handleLeftClick, Cesium.ScreenSpaceEventType.LEFT_CLICK);// 鼠标移动更新动态轮廓this.handler.setInputAction(this.handleMouseMove, Cesium.ScreenSpaceEventType.MOUSE_MOVE);// 右键结束绘制this.handler.setInputAction(this.handleRightClick, Cesium.ScreenSpaceEventType.RIGHT_CLICK);// 创建动态多边形实体this.createDynamicPolygon();
},// 创建动态多边形绘制过程中的临时多边形
createDynamicPolygon() {// 移除已存在的动态多边形if (this.currentPolygon) {this.viewer.entities.remove(this.currentPolygon);}this.currentPolygon this.viewer.entities.add({polygon: {hierarchy: new Cesium.CallbackProperty(() {return new Cesium.PolygonHierarchy(this.dynamicPoints);}, false),material: Cesium.Color.RED.withAlpha(0.3),outline: true,outlineColor: Cesium.Color.RED}});
}
3. 顶点编辑功能
编辑功能允许用户通过拖拽顶点来调整多边形形状实现思路是为每个顶点创建可交互的点实体并监听其拖拽事件
// 为每个顶点添加拖拽事件
addVertexDragHandler(vertexEntity, index) {const vertexHandler new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);// 将处理器存储到数组以便后续销毁this.vertexHandlers.push(vertexHandler);vertexHandler.setInputAction((event) {const pick this.viewer.scene.pick(event.position);if (Cesium.defined(pick) pick.id vertexEntity) {// 开始拖拽const moveHandler new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);moveHandler.setInputAction((moveEvent) {const newCartesian this.getCartesianFromMouse(moveEvent.endPosition);if (newCartesian) {// 更新顶点位置this.polygonPoints[index] newCartesian;vertexEntity.position.setValue(newCartesian);// 更新多边形this.currentPolygon.polygon.hierarchy.setValue(new Cesium.PolygonHierarchy(this.polygonPoints));// 更新面积显示this.calculateAndShowArea();}}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);// 结束拖拽vertexHandler.setInputAction(() {moveHandler.destroy();}, Cesium.ScreenSpaceEventType.LEFT_UP);}}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
}
4. 颜色与透明度自定义
通过添加颜色选择器和透明度滑块允许用户自定义多边形样式
// 更新多边形样式
updatePolygonStyle() {if (!this.currentPolygon) return;try {const color Cesium.Color.fromCssColorString(this.polygonColor).withAlpha(this.polygonAlpha);this.currentPolygon.polygon.material color;} catch (e) {console.error(颜色解析错误:, e);// 使用默认颜色作为fallbackthis.currentPolygon.polygon.material Cesium.Color.BLUE.withAlpha(0.5);}
}
5. 面积计算与显示
使用Turf.js库计算多边形面积并在多边形中心显示面积标签
// 计算并显示多边形面积
calculateAndShowArea() {try {if (this.polygonPoints.length 3) return;// 将笛卡尔坐标转换为经纬度const coordinates this.polygonPoints.map(cartesian {const cartographic Cesium.Cartographic.fromCartesian(cartesian);return [Cesium.Math.toDegrees(cartographic.longitude),Cesium.Math.toDegrees(cartographic.latitude)];});// 闭合多边形coordinates.push(coordinates[0]);// 使用Turf.js计算面积const polygon turf.polygon([coordinates]);const areaSquareMeters turf.area(polygon);const areaSquareKm areaSquareMeters / 1000000;const areaText 面积: ${areaSquareKm.toFixed(4)} 平方公里;// 显示面积标签this.showAreaLabel(areaText);} catch (e) {console.error(面积计算错误:, e);this.showAreaLabel(面积计算失败);}
},// 在多边形中心显示面积标签
showAreaLabel(text) {// 移除旧标签if (this.areaLabel) {this.viewer.entities.remove(this.areaLabel);}// 计算多边形中心点const center this.calculatePolygonCenter();// 创建新标签this.areaLabel this.viewer.entities.add({position: center,label: {text: text,font: 16px sans-serif,fillColor: Cesium.Color.YELLOW,backgroundColor: Cesium.Color.BLACK.withAlpha(0.7),padding: new Cesium.Cartesian2(12, 8),horizontalOrigin: Cesium.HorizontalOrigin.CENTER,verticalOrigin: Cesium.VerticalOrigin.CENTER,disableDepthTestDistance: Number.POSITIVE_INFINITY,outline: true,outlineColor: Cesium.Color.WHITE,outlineWidth: 1}});
}
常见问题解决方案
在开发过程中我们可能会遇到以下问题
1. 多边形颜色显示异常
问题设置颜色后多边形显示为白色或不生效。
解决方案
确保颜色值是有效的CSS颜色字符串检查透明度值是否为数字类型添加错误处理和默认颜色 fallback
// 安全的颜色设置方法
try {const color Cesium.Color.fromCssColorString(this.polygonColor).withAlpha(Number(this.polygonAlpha) || 0.5); // 确保alpha为数字this.currentPolygon.polygon.material color;
} catch (e) {console.error(颜色解析错误:, e);// 使用默认颜色作为fallbackthis.currentPolygon.polygon.material Cesium.Color.BLUE.withAlpha(0.5);
}
2. 面积标签不可见
问题面积标签不显示或被地形遮挡。
解决方案
提升标签高度避免被地形遮挡使用醒目颜色和背景提高可见性设置disableDepthTestDistance确保标签始终显示在最前面
// 计算中心点时提升高度
calculatePolygonCenter() {// ... 经纬度计算逻辑 ...// 将中心点转换回笛卡尔坐标并提升高度return Cesium.Cartesian3.fromDegrees(centerLon, centerLat, 50); // 提升50米高度
}
3. 控制台alpha类型错误
问题Expected alpha to be typeof number, actual typeof was string
解决方案
确保透明度值为数字类型在Vue中使用.number修饰符或手动转换类型
!-- Vue模板中 --
input typerange v-model.numberpolygonAlpha min0 max1 step0.1
4. 地图交互无法恢复
问题结束绘制或编辑后地图依然不能缩放或移动。
解决方案
增强交互恢复方法确保所有控制参数被正确设置管理所有事件处理器的生命周期确保完全销毁在所有退出路径调用交互恢复方法 // 恢复地图交互增强版
enableMapInteraction() {const controller this.viewer.scene.screenSpaceCameraController;// 确保所有输入被启用controller.enableInputs true;// 恢复所有相机控制controller.enableRotate true;controller.enableZoom true;controller.enableTranslate true;controller.enableTilt true;controller.enableLook true;console.log(地图交互已完全恢复);
},// 停止编辑多边形修复版
stopEditPolygon() {this.isEditing false;// 销毁所有顶点拖拽事件处理器this.vertexHandlers.forEach(handler {if (handler !handler.isDestroyed()) {handler.destroy();}});this.vertexHandlers []; // 清空处理器数组this.enableMapInteraction();
}
完整代码
Vue组件代码
templatediv idcesiumContainer stylewidth: 100%; height: 100vhdiv classpolygon-controlslabel多边形颜色:/labelinput typecolor v-modelpolygonColor inputupdatePolygonStyle /label透明度:/labelinputtyperangemin0max1step0.1v-model.numberpolygonAlphainputupdatePolygonStyle//divdiv class结束绘制/div/div
/templatescript
import initMap from /config/initMap.js;
import { mapConfig } from /config/mapConfig;
// import * as Cesium from cesium;
import * as turf from turf/turf;export default {data() {return {viewer: null,// 绘制状态管理isDrawing: false,isEditing: false,currentPolygon: null,polygonPoints: [],dynamicPoints: [],vertexEntities: [],handler: null,polygonColor: #0000FF, // 默认蓝色polygonAlpha: 0.5, // 默认透明度areaLabel: null, // 面积标签实体vertexHandlers: [], // 新增存储顶点拖拽事件处理器};},mounted() {this.viewer initMap(mapConfig.gaode.url3, false);this.initDrawHandler();},methods: {// 初始化绘制事件处理器initDrawHandler() {this.handler new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);this.startDrawPolygon();},// 开始绘制多边形startDrawPolygon() {console.log(开始绘制多边形);if (this.isEditing) this.stopEditPolygon();this.isDrawing true;this.polygonPoints [];this.dynamicPoints [];this.disableMapInteraction();// 左键点击添加顶点this.handler.setInputAction(this.handleLeftClick,Cesium.ScreenSpaceEventType.LEFT_CLICK);// 鼠标移动更新动态轮廓this.handler.setInputAction(this.handleMouseMove,Cesium.ScreenSpaceEventType.MOUSE_MOVE);// 右键结束绘制this.handler.setInputAction(this.handleRightClick,Cesium.ScreenSpaceEventType.RIGHT_CLICK);// 创建动态多边形实体this.createDynamicPolygon();},// 处理左键点击添加顶点handleLeftClick(event) {const cartesian this.getCartesianFromMouse(event.position);if (!cartesian) return;this.polygonPoints.push(cartesian);this.dynamicPoints.push(cartesian);// 添加顶点标记this.addVertexMarker(cartesian);},// 处理鼠标移动更新轮廓handleMouseMove(event) {if (this.polygonPoints.length 0 || !this.isDrawing) return;const cartesian this.getCartesianFromMouse(event.endPosition);if (!cartesian) return;// 更新动态点if (this.dynamicPoints.length this.polygonPoints.length) {this.dynamicPoints.pop();}this.dynamicPoints.push(cartesian);},// 处理右键结束绘制handleRightClick() {if (this.polygonPoints.length 3) {alert(至少需要3个顶点才能形成多边形);this.clearDrawing();return;}// 移除动态点this.dynamicPoints.pop();this.isDrawing false;// 保存最终多边形this.saveFinalPolygon();// 清除临时事件this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);this.handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);// 开启编辑模式this.startEditPolygon();// 确保交互已恢复双重保险setTimeout(() {this.enableMapInteraction();}, 100);},// 开始编辑多边形startEditPolygon() {this.isEditing true;this.disableMapInteraction();// 为每个顶点添加拖拽事件this.vertexEntities.forEach((vertex, index) {this.addVertexDragHandler(vertex, index);});},// 添加顶点拖拽事件addVertexDragHandler(vertexEntity, index) {const vertexHandler new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);// 将处理器存储到数组以便后续销毁this.vertexHandlers.push(vertexHandler);vertexHandler.setInputAction((event) {const pick this.viewer.scene.pick(event.position);if (Cesium.defined(pick) pick.id vertexEntity) {// 开始拖拽const moveHandler new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);moveHandler.setInputAction((moveEvent) {const newCartesian this.getCartesianFromMouse(moveEvent.endPosition);if (newCartesian) {// 更新顶点位置this.polygonPoints[index] newCartesian;vertexEntity.position.setValue(newCartesian);// 更新多边形this.currentPolygon.polygon.hierarchy.setValue(new Cesium.PolygonHierarchy(this.polygonPoints));// 更新面积显示this.calculateAndShowArea();}}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);// 结束拖拽vertexHandler.setInputAction(() {moveHandler.destroy();}, Cesium.ScreenSpaceEventType.LEFT_UP);}}, Cesium.ScreenSpaceEventType.LEFT_DOWN);},// 停止编辑多边形stopEditPolygon() {this.isEditing false;// 销毁所有顶点拖拽事件处理器this.vertexHandlers.forEach((handler) {if (handler !handler.isDestroyed()) {handler.destroy();}});this.vertexHandlers []; // 清空处理器数组this.enableMapInteraction();},// 创建动态多边形createDynamicPolygon() {// 移除已存在的动态多边形if (this.currentPolygon) {this.viewer.entities.remove(this.currentPolygon);}this.currentPolygon this.viewer.entities.add({polygon: {hierarchy: new Cesium.CallbackProperty(() {return new Cesium.PolygonHierarchy(this.dynamicPoints);}, false),material: Cesium.Color.RED.withAlpha(0.3), // 绘制过程中使用红色outline: true,outlineColor: Cesium.Color.RED,},});},// 保存最终多边形修复重复定义问题saveFinalPolygon() {// 移除动态多边形this.viewer.entities.remove(this.currentPolygon);try {// 验证颜色和透明度console.log(当前颜色值:,this.polygonColor,透明度:,this.polygonAlpha);const color Cesium.Color.fromCssColorString(this.polygonColor).withAlpha(Number(this.polygonAlpha) || 0.5);// 创建最终多边形应用自定义颜色和透明度this.currentPolygon this.viewer.entities.add({polygon: {hierarchy: new Cesium.PolygonHierarchy(this.polygonPoints),material: color,outline: true,outlineColor: Cesium.Color.BLACK,outlineWidth: 2,},});// 计算并显示面积this.calculateAndShowArea();} catch (e) {console.error(创建多边形失败:, e);// 使用默认颜色作为fallbackthis.currentPolygon this.viewer.entities.add({polygon: {hierarchy: new Cesium.PolygonHierarchy(this.polygonPoints),material: Cesium.Color.BLUE.withAlpha(0.5),outline: true,outlineColor: Cesium.Color.BLACK,},});}},// 添加顶点标记addVertexMarker(position) {const vertexEntity this.viewer.entities.add({position: position,point: {pixelSize: 10,color: Cesium.Color.YELLOW,outlineColor: Cesium.Color.BLACK,outlineWidth: 2,disableDepthTestDistance: Number.POSITIVE_INFINITY,},});this.vertexEntities.push(vertexEntity);},// 从鼠标位置获取笛卡尔坐标getCartesianFromMouse(position) {const ray this.viewer.camera.getPickRay(position);if (!ray) return null;return this.viewer.scene.globe.pick(ray, this.viewer.scene);},// 清除绘制状态clearDrawing() {this.isDrawing false;this.polygonPoints [];this.dynamicPoints [];if (this.currentPolygon) {this.viewer.entities.remove(this.currentPolygon);}this.vertexEntities.forEach((vertex) this.viewer.entities.remove(vertex));this.vertexEntities [];if (this.areaLabel) {this.viewer.entities.remove(this.areaLabel);this.areaLabel null;}this.enableMapInteraction();},// 更新多边形样式updatePolygonStyle() {if (!this.currentPolygon) return;try {const color Cesium.Color.fromCssColorString(this.polygonColor).withAlpha(Number(this.polygonAlpha) || 0.5);this.currentPolygon.polygon.material color;console.log(样式更新成功:, color);} catch (e) {console.error(颜色解析错误:, e);this.currentPolygon.polygon.material Cesium.Color.BLUE.withAlpha(0.5);}},// 计算并显示多边形面积calculateAndShowArea() {try {if (this.polygonPoints.length 3) return;// 将笛卡尔坐标转换为经纬度const coordinates this.polygonPoints.map((cartesian) {const cartographic Cesium.Cartographic.fromCartesian(cartesian);return [Cesium.Math.toDegrees(cartographic.longitude),Cesium.Math.toDegrees(cartographic.latitude),];});// 确保多边形闭合if (coordinates.length 0 !(coordinates[0][0] coordinates[coordinates.length - 1][0] coordinates[0][1] coordinates[coordinates.length - 1][1])) {coordinates.push([...coordinates[0]]);}// 使用Turf.js计算面积const polygon turf.polygon([coordinates]);const areaSquareMeters turf.area(polygon);const areaSquareKm areaSquareMeters / 1000000;const areaText 面积: ${areaSquareKm.toFixed(4)} 平方公里;console.log(计算面积:, areaText);// 显示面积标签this.showAreaLabel(areaText);} catch (e) {console.error(面积计算错误:, e);this.showAreaLabel(面积计算失败);}},// 在多边形中心显示面积标签优化版本showAreaLabel(text) {// 移除旧标签if (this.areaLabel) {this.viewer.entities.remove(this.areaLabel);}// 计算多边形中心点优化算法const center this.calculatePolygonCenter();// 创建新标签增强可见性this.areaLabel this.viewer.entities.add({position: center,label: {text: text,font: 16px sans-serif,fillColor: Cesium.Color.YELLOW, // 使用醒目颜色backgroundColor: Cesium.Color.BLACK.withAlpha(0.8), // 增强对比度padding: new Cesium.Cartesian2(12, 8),horizontalOrigin: Cesium.HorizontalOrigin.CENTER,verticalOrigin: Cesium.VerticalOrigin.CENTER,disableDepthTestDistance: Number.POSITIVE_INFINITY, // 始终显示在最前面pixelOffset: new Cesium.Cartesian2(0, 0),outline: true,outlineColor: Cesium.Color.WHITE,outlineWidth: 1,},});},// 计算多边形中心点更可靠的方法calculatePolygonCenter() {if (this.polygonPoints.length 0) return Cesium.Cartesian3.ZERO;// 计算经纬度平均值let totalLon 0,totalLat 0;const cartographics this.polygonPoints.map((cartesian) Cesium.Cartographic.fromCartesian(cartesian));cartographics.forEach((cartographic) {totalLon Cesium.Math.toDegrees(cartographic.longitude);totalLat Cesium.Math.toDegrees(cartographic.latitude);});const centerLon totalLon / cartographics.length;const centerLat totalLat / cartographics.length;// 将中心点转换回笛卡尔坐标并提升高度避免被地形遮挡return Cesium.Cartesian3.fromDegrees(centerLon, centerLat, 50);},// 禁用地图交互disableMapInteraction() {const controller this.viewer.scene.screenSpaceCameraController;controller.enableRotate false;controller.enableZoom false;controller.enableTranslate false;controller.enableTilt false;controller.enableLook false;},// 恢复地图交互enableMapInteraction() {const controller this.viewer.scene.screenSpaceCameraController;// 确保所有输入被启用controller.enableInputs true;// 恢复所有相机控制controller.enableRotate true;controller.enableZoom true;controller.enableTranslate true;controller.enableTilt true;controller.enableLook true;// 重置鼠标事件处理if (this.handler) {this.handler.destroy();this.handler new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);}console.log(地图交互已完全恢复);},},beforeDestroy() {if (this.viewer) {this.viewer.destroy();}if (this.handler) {this.handler.destroy();}// 移除控制面板const controlPanel document.querySelector(.polygon-controls);if (controlPanel) {controlPanel.remove();}},
};
/scriptCSS样式
style langscss scoped
#cesiumContainer {width: 100%;height: 100vh;touch-action: none;.polygon-controls {position: absolute;top: 20px;right: 20px;z-index: 1000;background: white;padding: 15px;border-radius: 5px;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);}.polygon-controls label {display: block;margin: 10px 0 5px;font-weight: bold;}.polygon-controls input {width: 100%;margin-bottom: 10px;}
}
/style
总结与扩展
本文详细介绍了如何基于Cesium和Vue实现交互式多边形绘制与编辑功能包括核心功能实现、样式自定义和面积计算等关键技术点。特别解决了开发过程中常见的颜色显示异常、标签不可见、类型错误和交互无法恢复等问题。
通过事件监听、动态属性更新和地理空间计算我们构建了一个功能完善的多边形编辑工具。重点强调了事件处理器生命周期管理和状态控制的最佳实践这些经验对于开发复杂Cesium交互功能具有普遍参考价值。
功能扩展方向
添加删除功能允许用户删除多边形或单个顶点支持多个多边形管理多个多边形图层导入导出支持GeoJSON格式导入导出测量工具添加距离测量、角度测量等功能样式库预设多种多边形样式供选择撤销/重做实现操作历史记录功能
希望本文能帮助你快速掌握Cesium多边形绘制技术如果有任何问题或建议欢迎在评论区留言讨论
原创不易转载请注明出处 如果本文对你有帮助别忘了点赞和关注哦