当前位置: 首页 > news >正文

Cesium 军事标绘入门:用 Cesium-Plot-JS 快速实现标绘功能

在军工、应急指挥、国土安全等项目中,常常需要在三维场景中标记 “攻击路线”“防御区域”“集结点” 等具有军事含义的要素 —— 这就是军事标绘。它本质是 “三维 GIS 绘制工具” 的专项延伸,也是GIS开发者从 “基础绘制” 进阶到 “行业专项功能” 的关键知识点。

image.png

军事标绘相比普通绘制(如画点、线、面),军事标绘有更严格的符号规范(如箭头角度、线条样式需符合军事标准)。因此如果直接用原生 Cesium 实现军事标绘,需要手动处理 “鼠标事件监听、坐标转换、几何算法计算、图元渲染” 等全流程,开发难度高、周期长。

这里我们可以用开源的Cesium插件(Cesium-Plot-JS)来实现,需要注意的是,这个插件适配的cesium版本为1.99,但是我们的1.97也可以适配。

图片

本系列文章将从 “实战使用” 到 “原理拆解”,分三篇带你全面掌握 Cesium 军事标绘:

  • 第一篇聚焦 “Cesium-Plot-JS 基础”,快速实现军事标绘功能;

  • 第二篇深入 “数据驱动与功能拓展”,用已有数据生成对应的军事标绘

  • 第三篇剖析 “底层原理与架构设计”,让你从 “会用” 升级为 “理解并能自定义”。

一、环境搭建步骤

1. 1 安装依赖

首先通过包管理器安装 Cesium-Plot-JS,同时需确保项目已引入 Cesium 核心库(1.97/1.99 版本)、dat.GUI(用于调试界面)与 cesium-navigation-es6(用于罗盘 / 比例尺控件):

// 使用pnpm安装(npm/yarn同理)
pnpm i cesium-plot-js

1. 2 配置 Cesium Token

Cesium 加载底图需依赖 Ion Token,需先在Cesium Ion 官网申请 Token,再在代码中配置:

// 引入核心库
import * as Cesium from "cesium";
import * as dat from "dat.gui";
import CesiumNavigation from "cesium-navigation-es6";
import CesiumPlot from "cesium-plot-js";
// 配置Cesium Token(建议通过环境变量注入,避免硬编码)
Cesium.Ion.defaultAccessToken = import.meta.env.VITE_CESIUM_TOKEN;

二、标绘工具激活

本节将逐步实现 “Cesium 场景搭建→标绘工具集成→事件监听” 的完整流程,以最常用的 “细箭头” 标绘为例,演示基础用法。

2.1 初始化 Cesium Viewer

首先创建 Viewer 实例,并配置界面控件(隐藏不必要的时间轴、底图切换等控件,聚焦标绘功能):

//使用cesium默认配置 初始化viewer
const viewer = new Cesium.Viewer("cesiumContainer", {timeline: false, //设置默认的时间轴不显示animation: false, //隐藏动画控件baseLayerPicker: false, //隐藏底图切换geocoder: false, //隐藏导航功能homeButton: false, //复位按钮sceneModePicker: false, //二三维切换按钮navigationHelpButton: false, //隐藏帮助按钮scene3DOnly: true, // 如果是三维的系统,最好加上这个配置shouldAnimate: true, //最好设置动画为true
});
// 快速实现比例尺,罗盘
new CesiumNavigation(viewer, {defaultResetView: Cesium.Cartesian3.fromDegrees(116.39, 39.9, 20000000),enableCompass: true,enableZoomControls: true,enableDistanceLegend: true,
});
// 1.97版本加载3dtiles
const tileset = new Cesium.Cesium3DTileset({url: new Cesium.IonResource.fromAssetId(69380),
});
// 将模型加入场景中
viewer.scene.primitives.add(tileset);
// 监听模型加载完成的回调,将视角注视到模型
tileset.readyPromise.then((res) => {viewer.zoomTo(tileset);
});

2.2 激活标绘工具(以细箭头为例)

然后添加一个gui工具,我们点击按钮可以激活对应的绘制工具,这里拿官网上的demo测试一下

// 创建dat.GUI调试面板
const gui = new dat.GUI();
// 添加“激活细箭头”按钮
gui.add({  
fn() {    
// 初始化细箭头标绘,配置样式    
const fineArrow = new CesiumPlot.FineArrow(Cesium, viewer, {      material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"), // 填充色(半透明蓝)      
outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"), // 轮廓色(纯蓝)      
outlineWidth: 3, // 轮廓宽度    });  
}
},"fn").name("激活细箭头标绘");

在new了CesiumPlot.FineArrow之后会自动触发sse事件来绘制军标

image.png

我们还可以通过事件来监听绘制的结果和编辑的结果,这个api设计和我们的绘制工具很相似

geometry.on("drawStart", () => {
console.log("开始绘制");
});
geometry.on("drawUpdate", (data) => {
console.log("绘制中", data);
});
geometry.on("drawEnd", (data) => {
console.log("结束绘制", data);
});
geometry.on("editStart", (data) => {
console.log("开始编辑", data);
});
geometry.on("editEnd", (data) => {
console.log("编辑结束", data);
});

可以看到在绘制过程中,返回了当前鼠标所在的位置;在结束绘制的时候,会将攻击直箭头的起点和终点返回。在编辑的时候也是一样的。

image.png

接下来使用gui将这个库适配的所有绘制类型都尝试一下

const plotTypes = [{name: "圆形",type: "Circle",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "多边形",type: "Polygon",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "矩形",type: "Reactangle",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "三角形",type: "Triangle",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "细箭头",type: "FineArrow",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "攻击箭头",type: "AttackArrow",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "燕尾攻击箭头",type: "SwallowtailAttackArrow",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "分队战斗",type: "SquadCombat",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "燕尾分队战斗",type: "SwallowtailSquadCombat",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "直箭头",type: "StraightArrow",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "突击方向",type: "AssaultDirection",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "曲箭头",type: "CurvedArrow",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "双箭头",type: "DoubleArrow",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "自由线",type: "FreehandLine",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "曲线",type: "Curve",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "椭圆",type: "Ellipse",options:{material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "弓形",type: "Lune",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},{name: "自由多边形",type: "FreehandPolygon",options: {material: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 0.5)"),outlineMaterial: Cesium.Color.fromCssColorString("rgba(59, 178, 208, 1)"),outlineWidth: 3,},},
];let geometry = null;
const plotParams = {绘制类型: plotTypes[0].name,
};const gui = new dat.GUI();
gui.add(plotParams, "绘制类型", plotTypes.map((item) => item.name)).onChange((val) => {if (geometry) {geometry.remove();geometry = null;}const selected = plotTypes.find((item) => item.name === val);if (selected) {geometry = new CesiumPlot[selected.type](Cesium, viewer, selected.options);geometry.on("drawStart", () => {console.log("开始绘制");});geometry.on("drawUpdate", (data) => {console.log("绘制中", data);});geometry.on("drawEnd", (data) => {console.log("结束绘制", data);});geometry.on("editStart", (data) => {console.log("开始编辑", data);});geometry.on("editEnd", (data) => {console.log("编辑结束", data);});}
});
// 默认初始化第一个类型
geometry = new CesiumPlot[plotTypes[0].type](Cesium, viewer, plotTypes[0].options);
geometry.on("drawStart", () => {console.log("开始绘制");
});
geometry.on("drawUpdate", (data) => {console.log("绘制中", data);
});
geometry.on("drawEnd", (data) => {console.log("结束绘制", data);
});
geometry.on("editStart", (data) => {console.log("开始编辑", data);
});
geometry.on("editEnd", (data) => {console.log("编辑结束", data);
});

测试之后都没有任何问题

图片

http://www.dtcms.com/a/461873.html

相关文章:

  • 【ROS2快速学习】
  • Vue3源码runtime-core运行时核心模块之provide依赖和inject注入详解
  • 网站开发个人简历word下载陕西网站建设设计
  • P2P技术
  • 面试真实经历某节跳动大厂Java和算法问答以及答案总结(一)
  • Python全栈(基础篇)——Day08:后端内容(切片+迭代+实战演示+每日一题)
  • 各大网站头条凡科免费网站可以做推广吗
  • 技术速递|GitHub 如何保护开发者免受版权执法过度影响
  • LLAVA-MINI论文阅读
  • OpenAI Agents 并行化实现
  • CNN卷计计算
  • 腾讯云服务器做网站可以吗徐州网站建设
  • 上市公司协会网站建设汇报wordpress接入qq互联
  • 前端 = [...this.orderList] (深拷贝)和this.orderList (引用赋值)
  • 部门管理|“删除部门”功能实现(Django5零基础Web平台)
  • 从 0 到 1 搭建 Python 语言 Web UI自动化测试学习系列 12--日志模块设计
  • 服务器网站源码在哪七牛云配置wordpress
  • SQL-多对多关系
  • PostgreSQL 18 异步 I/O(AIO)调优指南
  • 购物网站名字大全云虚拟主机 多个网站
  • 使用DuckDB SQL求三阶六角幻方
  • 电子商务网站建设一般流程无忧代理 在线
  • 一文了解Function Calling、MCP、Agent联系与区别
  • 存储芯片核心产业链主营产品:兆易创新、北京君正、澜起科技、江波龙、长电科技、佰维存储,6家龙头公司主营产品深度数据
  • Git 常用命令完整指南
  • 网站维护入口房子装修设计软件
  • MySQL 延时从库的作用与意义
  • h5网站价格wordpress footer.php添加qq悬浮
  • 【脚本升级】银河麒麟V10一键安装MySQL9.3.0
  • android pdf框架-15,mupdf工具与其它