绘制图形并导出geojson ·Draw and export geojson· ▶ 在线运行案例案例合集三维可视化功能案例threehub.cn开源仓库github地址https://github.com/z2586300277/three-cesium-examples400个案例代码:网盘链接你将学到什么Cesium Entity 高层实体 APIGeoJSON 矢量面/线/点加载效果说明本案例演示绘制图形并导出geojson效果Cesium绘图并导出GeoJSON工具本工具提供交互式点、线、面绘制功能并能导出为标准GeoJSON格式核心用到 Cesium、GeoJSON。建议先打开文首在线案例查看动态画面再对照下方源码逐步理解。核心概念Viewer聚合 Scene、Camera、Clock 与渲染循环是 Cesium 应用入口。Entity面向点线面/模型/标签的高层 API与 Primitive 相比更适合交互与属性驱动。阅读下方完整源码时建议从init/load/animate三条主线入手再深入 shader 与工具函数。实现步骤创建 Viewer配置地形/影像若案例需要并设置初始相机在requestAnimationFrame循环中更新状态并 renderCesium 为viewer.render或自动渲染代码要点/**Cesium绘图并导出GeoJSON工具本工具提供交互式点、线、面绘制功能并能导出为标准GeoJSON格式* 功能说明1. 用户点击绘制点/线/面按钮开始交互式绘制2. 在地图上点击选择点位置3. 点击导出为Geojson按钮将绘制内容导出为GeoJSON文件* module DrawAndExportGeojsonauthor z2586300277version 1.0.0since 2024*/// 导入模块和初始化 /**导入Cesium库用于3D地球可视化和地理空间数据处理external Cesiumsee {link https://cesium.com/}*/ import * as Cesium from cesium/**导入dat.GUI库用于创建轻量级的图形用户界面控制面板external GUIsee {link https://github.com/dataarts/dat.gui}*/ import { GUI } from dat.gui/**获取HTML中用于挂载Cesium Viewer的地图容器元素type {HTMLElement}constant*/ const box document.getElementById(box)// GUI控制面板 /**定义图形绘制操作对象包含所有GUI控制面板的功能按钮namespace objproperty {Function} 绘制点 - 启动交互式点绘制模式property {Function} 绘制线 - 启动交互式线绘制模式property {Function} 绘制面 - 启动交互式面绘制模式property {Function} 全部清除 - 清除所有已绘制的实体property {Function} 导出为Geojson - 将绘制内容导出为GeoJSON格式文件*/ const obj { /**绘制点功能 - 启动交互式点绘制模式当用户点击此按钮时激活点绘制模式允许用户在地图上点击添加点标记functionmemberof objexample// 点击按钮后用户可以在地图上点击添加点// 每次点击都会创建一个新的点实体*/ 绘制点: () { drawPoint() }, /**绘制线功能 - 启动交互式线绘制模式当用户点击此按钮时激活线绘制模式允许用户通过连续点击创建线段functionmemberof objexample// 点击按钮后用户可以通过连续点击创建折线// 右键结束绘制操作*/ 绘制线: () { drawLine() }, /**绘制面功能 - 启动交互式面绘制模式当用户点击此按钮时激活面绘制模式允许用户通过连续点击创建多边形functionmemberof objexample// 点击按钮后用户可以通过连续点击创建多边形// 至少需要3个点才能形成面右键结束绘制操作*/ 绘制面: () { drawPlane() }, /**全部清除功能 - 清除所有已绘制的实体移除地图上所有通过本工具创建的点、线、面等实体functionmemberof obj*/ 全部清除: () { viewer.entities.removeAll(); }, /**导出为Geojson功能 - 将绘制内容导出为GeoJSON格式文件将当前地图上所有绘制的实体转换为GeoJSON格式并提供下载functionmemberof objexample// 点击后会生成一个GeoJSON文件供用户下载// 文件包含所有点、线、面的地理坐标信息*/ 导出为Geojson: () { exportGeojson() } };/**创建图形用户界面控制面板实例使用dat.GUI库创建一个可折叠的控制面板用于访问绘图工具的各种功能type {GUI}see {link https://github.com/dataarts/dat.gui}*/ const gui new GUI(); for (const key in obj) gui.add(obj, key)// Cesium Viewer初始化 /**初始化Cesium Viewer实例这是整个应用的核心组件Viewer是Cesium的主要_widget_提供了完整的3D地球可视化功能type {Cesium.Viewer}constantsee {link https://cesium.com/learn/cesiumjs/ref-doc/Viewer.html}*/ const viewer new Cesium.Viewer(box, { animation: false, // 不显示动画控件时间轴左侧的播放按钮 baseLayerPicker: false, // 不显示图层选择器右上角的图层切换按钮 baseLayer: Cesium.ImageryLayer.fromProviderAsync( Cesium.ArcGisMapServerImageryProvider.fromUrl(https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer) ), // 设置基础影像图层为ArcGIS世界影像图 fullscreenButton: false, // 不显示全屏按钮 timeline: false, // 不显示时间线控件 infoBox: false, // 不显示信息框点击实体时的详细信息弹窗 })/**隐藏Cesium默认的Logo信息和版权信息通过修改CSS样式隐藏右下角的Cesium Ion logo和相关版权信息*/ viewer._cesiumWidget._creditContainer.style.display none;// 全局变量定义 /**绘图事件处理器实例用于处理鼠标交互事件*/ let drawHandler null;/**存储当前正在绘制的点坐标数组在绘制线或面时存储用户已点击确认的点坐标type {Array }*/ let activePoints [];// 配置参数 const CONFIG { /**点样式配置memberof CONFIGproperty {number} pixelSize - 点的像素大小property {Cesium.Color} color - 点的填充颜色property {Cesium.Color} outlineColor - 点的边框颜色property {number} outlineWidth - 点的边框宽度*/ point: { pixelSize: 10, color: Cesium.Color.YELLOW, outlineColor: Cesium.Color.BLACK, outlineWidth: 1, }, /**线样式配置memberof CONFIGproperty {number} width - 线的宽度像素property {Cesium.Color} material - 线的材质颜色*/ line: { width: 5, material: Cesium.Color.RED.withAlpha(1), }, /**面样式配置memberof CONFIGproperty {Cesium.Color} material - 面的填充材质property {boolean} outline - 是否显示边框property {Cesium.Color} outlineColor - 边框颜色property {number} outlineWidth - 边框宽度*/ polygon: { material: Cesium.Color.GREEN.withAlpha(0.7), outline: true, outlineColor: Cesium.Color.GREEN, outlineWidth: 2 }, /**临时线样式配置用于绘制过程中的预览线memberof CONFIGproperty {number} width - 线的宽度像素property {Cesium.Color} material - 线的材质颜色带透明度*/ tempLine: { width: 3, material: Cesium.Color.RED.withAlpha(0.5), } };// 图形创建函数 /**创建点标记实体在指定的地理坐标位置创建一个点标记*/ function createPointMarker(position) { return viewer.entities.add({ position: position, point: { pixelSize: CONFIG.point.pixelSize, color: CONFIG.point.color, outlineColor: CONFIG.point.outlineColor, outlineWidth: CONFIG.point.outlineWidth } }); }/**创建线段实体根据给定的点坐标数组创建线段实体*/ function createLine(positions, isTemp false) { return viewer.entities.add({ polyline: { positions: positions, width: isTemp ? CONFIG.tempLine.width : CONFIG.line.width, material: isTemp ? CONFIG.tempLine.material : CONFIG.line.material } }); }/**创建多边形实体根据给定的点坐标数组创建多边形实体*/ function createPolygon(positions) { return viewer.entities.add({ polygon: { hierarchy: new Cesium.PolygonHierarchy(positions), material: CONFIG.polygon.material, outline: CONFIG.polygon.outline, outlineColor: CONFIG.polygon.outlineColor, outlineWidth: CONFIG.polygon.outlineWidth } }); }// 绘图控制函数 /**清除当前绘制状态销毁当前的绘图事件处理器并重置活动点数组在开始新的绘制操作前调用此函数以确保状态干净*/ function clearDrawing() { // 如果存在之前的绘图处理器销毁它以释放资源 if (drawHandler) { drawHandler.destroy(); drawHandler null; } // 清空活动点数组 activePoints []; }/**初始化绘图处理器创建并配置用于处理鼠标交互事件的处理器param {Function} clickHandler - 鼠标左键点击事件处理函数param {Function} moveHandler - 鼠标移动事件处理函数param {Function} endHandler - 结束绘制事件处理函数通常是右键点击example// 初始化一个只处理点击事件的处理器initDrawingHandler(clickFunction, null, null);* // 初始化处理点击、移动和结束事件的完整处理器initDrawingHandler(clickFunction, moveFunction, endFunction);*/ function initDrawingHandler(clickHandler, moveHandler, endHandler) { // 清除之前的绘制内容确保状态干净 clearDrawing();// 创建事件处理器实例绑定到Viewer的canvas元素 drawHandler new Cesium.ScreenSpaceEventHandler(viewer.canvas);// 注册鼠标左键点击事件处理函数用于添加点 if (clickHandler) { drawHandler.setInputAction(clickHandler, Cesium.ScreenSpaceEventType.LEFT_CLICK); }// 注册鼠标移动事件处理函数用于实时预览 if (moveHandler) { drawHandler.setInputAction(moveHandler, Cesium.ScreenSpaceEventType.MOUSE_MOVE); }// 注册鼠标右键点击事件处理函数用于结束绘制 if (endHandler) { drawHandler.setInputAction(endHandler, Cesium.ScreenSpaceEventType.RIGHT_CLICK); } }/**绘制点功能实现激活点绘制模式用户每次左键点击都会在地图上创建一个点标记此模式不需要结束操作可以连续点击创建多个点*/ function drawPoint() { /**初始化点绘制处理器只需要处理点击事件不需要处理移动和结束事件*/ initDrawingHandler( // 点击处理函数 function (movement) { // 将屏幕坐标转换为笛卡尔坐标 const cartesian viewer.scene.pickPosition(movement.position); if (cartesian) { // 创建点实体 createPointMarker(cartesian); } }, null, // 无需鼠标移动处理 null // 无需结束处理 ); }/**绘制线功能实现激活线绘制模式用户通过连续左键点击创建折线右键结束绘制绘制过程中会显示临时线段预览*/ function drawLine() { /**临时线段实体用于在绘制过程中显示预览效果type {Cesium.Entity|null}*/ let tempLineEntity null;/**初始化线绘制处理器需要处理点击、移动和结束事件*/ initDrawingHandler( // 点击处理函数 - 添加新的点到线中 function (movement) { // 将屏幕坐标转换为笛卡尔坐标 const cartesian viewer.scene.pickPosition(movement.position); if (cartesian) { // 将新点添加到活动点数组 activePoints.push(cartesian);// 如果至少有两个点创建线段连接最后两个点 if (activePoints.length 2) { createLine([ activePoints[activePoints.length - 2], activePoints[activePoints.length - 1] ]); } } }, // 鼠标移动处理函数 - 实时更新临时线段预览 function (movement) { // 获取鼠标当前位置的笛卡尔坐标 const cartesian viewer.scene.pickPosition(movement.endPosition); // 如果已有至少一个点且能获取到当前位置 if (cartesian activePoints.length 0) { if (tempLineEntity) { // 更新临时线段的端点 tempLineEntity.polyline.positions [activePoints[activePoints.length - 1], cartesian]; } else { // 创建新的临时线段 tempLineEntity createLine([activePoints[activePoints.length - 1], cartesian], true); } } }, // 右键结束处理函数 - 完成线绘制操作 function () { // 清除临时实体 if (tempLineEntity) { viewer.entities.remove(tempLineEntity); tempLineEntity null; }// 销毁事件处理器 if (drawHandler) { drawHandler.destroy(); drawHandler null; } // 重置活动点数组 activePoints []; } ); }/**绘制面功能实现激活面绘制模式用户通过连续左键点击创建多边形右键结束绘制绘制过程中会显示临时面预览*/ function drawPlane() { /**临时面实体用于在绘制过程中显示预览效果type {Cesium.Entity|null}*/ let tempPlaneEntity null; /**临时点标记数组存储绘制过程中创建的点标记type {Array }*/ let tempPoints []; /**初始化面绘制处理器需要处理点击、移动和结束事件*/ initDrawingHandler( // 点击处理函数 - 添加新的顶点到面中 function (movement) { // 将屏幕坐标转换为笛卡尔坐标 const cartesian viewer.scene.pickPosition(movement.position); if (cartesian) { // 添加点到活动点数组 activePoints.push(cartesian);// 创建点标记以可视化顶点位置 tempPoints.push(createPointMarker(cartesian)); // 如果已经有面实体则更新它 if (tempPlaneEntity) { // 更新面的顶点 tempPlaneEntity.polygon.hierarchy new Cesium.PolygonHierarchy(activePoints); } else if (activePoints.length 3) { // 创建新的面实体至少需要3个点 tempPlaneEntity createPolygon(activePoints); } } }, // 鼠标移动处理函数 - 实时更新临时面预览 function (movement) { // 获取鼠标当前位置的笛卡尔坐标 const cartesian viewer.scene.pickPosition(movement.endPosition); // 如果已有至少两个点且能获取到当前位置 if (cartesian activePoints.length 2) { // 创建临时点数组包含所有已确认的点和当前鼠标位置 const tempPositions [...activePoints, cartesian];if (tempPlaneEntity) { // 更新面的顶点 tempPlaneEntity.polygon.hierarchy new Cesium.PolygonHierarchy(tempPositions); } else if (tempPositions.length 3) { // 创建新的面实体至少需要3个点 tempPlaneEntity createPolygon(tempPositions); } } }, // 右键结束处理函数 - 完成面绘制操作 function () { // 如果点数少于3个移除临时实体无法构成面 if (activePoints.length 3 tempPlaneEntity) { viewer.entities.remove(tempPlaneEntity); tempPlaneEntity null; }// 更新面实体的最终顶点 if (tempPlaneEntity) { tempPlaneEntity.polygon.hierarchy new Cesium.PolygonHierarchy(activePoints); } // 移除所有临时点标记 tempPoints.forEach(element { viewer.entities.remove(element) }); // 销毁事件处理器 if (drawHandler) { drawHandler.destroy(); drawHandler null; } // 重置临时数组 tempPoints []; activePoints []; } ); }// GeoJSON导出功能 /**导出为GeoJSON格式文件将当前地图上所有绘制的实体转换为GeoJSON格式并提供下载支持点、线、面三种几何类型的导出*/ function exportGeojson() { // 创建GeoJSON对象结构 const geojson { type: FeatureCollection, // GeoJSON类型要素集合 features: [] // 要素数组将包含所有转换后的实体 };// 遍历所有绘制的实体并转换为GeoJSON要素 viewer.entities.values.forEach(entity { const feature convertEntityToGeoJsonFeature(entity); if (feature) { geojson.features.push(feature); } }); // 如果有可导出的要素则创建下载文件 if (geojson.features.length 0) { // 将GeoJSON对象转换为格式化的JSON字符串 const dataStr data:text/json;charsetutf-8, encodeURIComponent(JSON.stringify(geojson, null, 2)); // 创建临时下载链接元素 const downloadAnchorNode document.createElement(a); downloadAnchorNode.setAttribute(href, dataStr); downloadAnchorNode.setAttribute(download, cesium_drawing.geojson); // 设置下载文件名 document.body.appendChild(downloadAnchorNode); // 添加到DOM中 // 触发点击事件开始下载 downloadAnchorNode.click(); downloadAnchorNode.remove(); // 下载完成后移除临时元素 } else { // 如果没有可导出的实体提示用户 alert(没有可导出的实体) } }/**将Cesium实体转换为GeoJSON要素根据实体类型点、线、面提取相应的地理坐标信息并构造成GeoJSON格式param {Cesium.Entity} entity - 需要转换的Cesium实体returns {Object|null} GeoJSON要素对象如果无法转换则返回nullexampleconst entity viewer.entities.getById(point1);const feature convertEntityToGeoJsonFeature(entity);// 返回类似:// {// type: Feature,// properties: { id: point1, name: },// geometry: {// type: Point,// coordinates: [-122.0, 37.5]// }// }*/ function convertEntityToGeoJsonFeature(entity) { // 创建GeoJSON要素的基本结构 const feature { type: Feature, properties: { id: entity.id, // 实体ID name: entity.name || // 实体名称如果没有则为空字符串 }, geometry: null // 几何信息占位符 };// 处理点实体 if (entity.position entity.point) { // 获取实体当前位置考虑时间动态性 const position entity.position.getValue(Cesium.JulianDate.now()); // 将笛卡尔坐标转换为大地坐标弧度制 const cartographic Cesium.Cartographic.fromCartesian(position); if (cartographic) { // 构造点几何信息转换为度数 feature.geometry { type: Point, coordinates: [ Cesium.Math.toDegrees(cartographic.longitude), // 经度 Cesium.Math.toDegrees(cartographic.latitude) // 纬度 ] }; return feature; // 返回构造好的点要素 } }// 处理线实体 if (entity.polyline) { // 获取线的所有顶点坐标 const positions entity.polyline.positions.getValue(Cesium.JulianDate.now()); // 确保有有效的坐标数据 if (positions positions.length 0) { // 将所有笛卡尔坐标转换为经纬度坐标 const coordinates positions.map(position { const cartographic Cesium.Cartographic.fromCartesian(position); return [ Cesium.Math.toDegrees(cartographic.longitude), // 经度 Cesium.Math.toDegrees(cartographic.latitude) // 纬度 ]; });// 构造线几何信息 feature.geometry { type: LineString, coordinates: coordinates }; return feature; // 返回构造好的线要素 } }// 处理面实体 if (entity.polygon) { // 获取面的层级结构包含顶点信息 const hierarchy entity.polygon.hierarchy.getValue(Cesium.JulianDate.now()); // 确保有有效的顶点数据 if (hierarchy hierarchy.positions hierarchy.positions.length 0) { // 构造面的坐标数组GeoJSON面需要嵌套数组 const coordinates [[]]; // 转换所有顶点坐标 hierarchy.positions.forEach(position { const cartographic Cesium.Cartographic.fromCartesian(position); coordinates[0].push([ Cesium.Math.toDegrees(cartographic.longitude), // 经度 Cesium.Math.toDegrees(cartographic.latitude) // 纬度 ]); });// 闭合多边形将第一个点添加到末尾以闭合面 if (hierarchy.positions.length 0) { const firstPos hierarchy.positions[0]; const cartographic Cesium.Cartographic.fromCartesian(firstPos); coordinates[0].push([ Cesium.Math.toDegrees(cartographic.longitude), // 经度 Cesium.Math.toDegrees(cartographic.latitude) // 纬度 ]); }// 构造面几何信息 feature.geometry { type: Polygon, coordinates: coordinates }; return feature; // 返回构造好的面要素 } }// 如果实体不是点、线或面则返回null return null; }完整源码GitHub小结本文提供绘制图形并导出geojson完整 Cesium.js 源码与在线 Demo建议先运行案例再改 uniform/参数做二次实验更多 Cesium.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库