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

Cesium快速入门到精通系列教程九:Cesium 中高效添加和管理图标/标记的标准方式​​

Cesium中通过 ​​Primitive 高效添加 ​​点、线、多边形、圆、椭圆、球、模型​​ 等地理要素,以下是各类地理要素的高效添加方式:

一、公告板

1. 创建 BillboardCollection 并添加到场景​

const billboards = viewer.scene.primitives.add(new Cesium.BillboardCollection());
  • ​​new Cesium.BillboardCollection()​​

创建一个新的 ​​公告板集合​​(BillboardCollection),用于管理多个公告板(Billboard)。

  • ​​viewer.scene.primitives.add(...)​​

将这个公告板集合添加到 Cesium 的 ​​场景(Scene)​​ 中,使其能够被渲染。

作用​​:
类似于创建一个“容器”,用于高效管理多个公告板(比如多个图标、标记点等),而不是单独添加每个公告板(单独添加会有更高的性能开销)。 

2. 向 BillboardCollection 添加一个公告板​ 

billboards.add({position: Cesium.Cartesian3.fromDegrees(113.3244, 23.1049, 600),image: '/Assets/nav.svg',width: 32,height: 32,scaleByDistance: new Cesium.NearFarScalar(1e3, 1.0, 2e6, 0.2)
});
  • ​​position: Cesium.Cartesian3.fromDegrees(113.3244, 23.1049, 600)​​

定义公告板的 ​​3D 坐标位置​​:

113.3244, 23.1049 是经纬度(WGS84 坐标系)。

600 是高度(单位:米),表示公告板在地球表面上方 600 米处。

  • ​​image: '/Assets/nav.svg'​​

公告板显示的 ​​图片路径​​(这里是 /Assets/nav.svg,可以是一个 SVG 或 PNG 图标)。

  • ​​width: 32, height: 32​​

公告板的 ​​尺寸​​(宽度和高度均为 32 像素)。

  • ​​scaleByDistance: new Cesium.NearFarScalar(1e3, 1.0, 2e6, 0.2)​​

​​视距缩放优化​​(根据相机距离动态调整公告板大小):

当相机距离 ​​1000 米(1e3)​​ 时,公告板显示原始大小(1.0 倍)。

当相机距离 ​​2000000 米(2e6)​​ 时,公告板缩小到 0.2 倍(避免远处图标过大)。

作用​​:
在指定位置(广州附近,高度 600 米)添加一个 ​​导航图标​​(nav.svg),并优化其显示大小(近大远小)。 

3、整体作用​​

这段代码的 ​​核心功能​​ 是:

  1. ​​创建一个公告板集合​​(BillboardCollection),用于高效管理多个公告板。
  2. ​​向集合中添加一个公告板​​,指定其:
  • ​​位置​​(经纬度 + 高度)。
  • ​​显示的图片​​(nav.svg)。
  • ​​尺寸​​(32x32 像素)。
  • ​​视距缩放优化​​(近大远小,避免远处图标过大)。

4、优化点​​

  • ​​使用 BillboardCollection 而不是单独添加 Billboard​​

批量管理多个公告板时,性能更高(减少 GPU 调用次数)。

  • ​​scaleByDistance 优化​​

避免远处图标过大,提升视觉效果。

  • ​​支持 3D 位置(含高度)​​

不仅能在地表放置图标,还能在 3D 空间(如空中)放置。

5、扩展用法​​

如果需要添加多个公告板,可以循环调用 billboards.add():

const positions = [{ lon: 113.3244, lat: 23.1049, height: 600 },{ lon: 113.3254, lat: 23.1059, height: 700 }
];
positions.forEach(pos => {billboards.add({position: Cesium.Cartesian3.fromDegrees(pos.lon, pos.lat, pos.height),image: '/Assets/nav.svg',width: 32,height: 32});
});

性能更好的方式:

const billboards = viewer.scene.primitives.add(new Cesium.BillboardCollection({scene: viewer.scene,debugShowBoundingVolume: false // 关闭调试框,提升性能[4](@ref)})
);const positions = [{ lon: 116.40, lat: 39.91, image: "icon1.png" },{ lon: 121.47, lat: 31.23, image: "icon2.png" }
];const billboardList = positions.map(pos => {return {image: pos.image,position: Cesium.Cartesian3.fromDegrees(pos.lon, pos.lat),scale: 0.8,color: Cesium.Color.WHITE.withAlpha(0.9),horizontalOrigin: Cesium.HorizontalOrigin.CENTER,verticalOrigin: Cesium.VerticalOrigin.BOTTOM};
});// 批量添加(减少渲染调用)
billboards.add(billboardList);

6、动态更新策略

直接修改属性

const billboard = billboards.get(0); // 获取第一个广告牌
billboard.scale = 1.2; // 修改缩放比例
billboard.position = Cesium.Cartesian3.fromDegrees(120.0, 30.0); // 更新位置

动态效果(旋转/闪烁)​​ 

// 通过 preRender 事件实现旋转动画[9](@ref)
viewer.scene.preRender.addEventListener(() => {const time = Date.now() * 0.001;billboard.rotation = time % (Math.PI * 2); // 持续旋转billboard.color.alpha = 0.5 + 0.5 * Math.sin(time); // 透明度闪烁
});

按需更新​​

// 仅当广告牌可见时更新
if (billboard.show) {billboard.scale = calculateScaleBasedOnDistance();
}

7、性能优化技巧

7.1 ​​GPU 合并渲染​​

    ​​批量添加​​:单次 billboards.add() 提交多个广告牌,触发 GPU 实例化渲染。

    ​​纹理复用​​:相同图片自动合并纹理,减少 Draw Call。

7.2 距离动态控制​​

billboard.scaleByDistance = new Cesium.NearFarScalar(1e3, 1.0, 1e5, 0.2);
billboard.translucencyByDistance = new Cesium.NearFarScalar(1e4, 1.0, 2e5, 0.1);

近距离正常显示,远距离缩小并渐隐,降低渲染负载。 

7.3 ​​视锥体裁剪​​

viewer.scene.frustumCulling = true; // 默认开启,自动剔除视野外广告牌

8、内存管理机制

8.1 移除单个广告牌​​

billboards.remove(billboard); // 移除指定对象

8.2 ​​批量清理​​

// 移除所有广告牌
billboards.removeAll();
// 或从场景中移除整个集合
viewer.scene.primitives.remove(billboards);

8.3 避免内存泄漏​​

// 销毁时释放资源
viewer.scene.primitives.destroyPrimitives = true;

9、总结​

代码部分作用
new Cesium.BillboardCollection()创建公告板集合(高效管理多个 Billboard)
viewer.scene.primitives.add(...)将集合添加到场景(使其可渲染)
billboards.add({...})添加一个公告板,指定位置、图片、尺寸和缩放优化

这段代码是 ​​Cesium 中高效添加和管理 3D 图标/标记的标准方式​​,适用于地图、仿真、游戏等场景。 

二、文本

使用 Primitive API

  // 初始化Viewerconst viewer = new Cesium.Viewer('cesiumContainer', {terrainProvider: Cesium.createWorldTerrain(),sceneMode: Cesium.SceneMode.SCENE3D});// 创建LabelCollection图元const labelCollection = viewer.scene.primitives.add(new Cesium.LabelCollection({show: true,// 启用深度测试避免被地形遮挡(需权衡性能)depthTest: false}));// 批量添加文本标签const positions = [{ lon: 116.404, lat: 39.915, text: "北京" },{ lon: 121.47, lat: 31.23, text: "上海" },// 更多位置数据...];positions.forEach(pos => {labelCollection.add({position: Cesium.Cartesian3.fromDegrees(pos.lon, pos.lat),text: pos.text,font: '14px sans-serif', // 字体优化:避免过大字号fillColor: Cesium.Color.WHITE,outlineColor: Cesium.Color.BLACK,outlineWidth: 2,// 垂直对齐:文本位于坐标点下方verticalOrigin: Cesium.VerticalOrigin.BOTTOM,// 像素偏移:微调位置pixelOffset: new Cesium.Cartesian2(0, -15)});});

三. 点(Point)​ 

使用 Primitive API(高性能,适合大量点)​

const pointPrimitiveCollection = viewer.scene.primitives.add(new Cesium.PointPrimitiveCollection());
pointPrimitiveCollection.add({position: Cesium.Cartesian3.fromDegrees(113.3244, 23.1049, 0),color: Cesium.Color.RED,pixelSize: 10
});

四、线(Polyline)​

使用 Primitive API(Primitive 方式需手动构建 Geometry)​

const polylineCollection = viewer.scene.primitives.add(new Cesium.PolylineCollection());
polylineCollection.add({positions: Cesium.Cartesian3.fromDegreesArray([113.3244, 23.1049,113.3254, 23.1059]),width: 2,material: new Cesium.ColorMaterialProperty(Cesium.Color.BLUE)
});

五、多边形(Polygon)​

使用 Primitive API(Primitive 方式需手动构建 Geometry)​

const polygonCollection = viewer.scene.primitives.add(new Cesium.PolygonCollection());
polygonCollection.add({hierarchy: Cesium.Cartesian3.fromDegreesArray([113.3244, 23.1049,113.3254, 23.1059,113.3264, 23.1039]),material: new Cesium.ColorMaterialProperty(Cesium.Color.GREEN.withAlpha(0.5))
});

六、圆(Circle)​

使用 Primitive API(需手动计算圆周点)​

// 初始化Cesium
const viewer = new Cesium.Viewer('cesiumContainer', {terrainProvider: Cesium.createWorldTerrain(),baseLayerPicker: false, // 禁用底图选择器geocoder: false, // 禁用地理编码器homeButton: false, // 禁用主页按钮infoBox: false, // 禁用信息框sceneModePicker: false, // 禁用场景模式选择器selectionIndicator: false, // 禁用选择指示器navigationHelpButton: false, // 禁用导航帮助按钮animation: false, // 禁用动画控件timeline: false, // 禁用时间轴fullscreenButton: false // 禁用全屏按钮
});// 定义圆的中心点和半径
const centerLon = 113.3244;
const centerLat = 23.1049;
const radiusInMeters = 1000;// 将圆心转换为 Cartesian3 坐标
const centerCartesian = Cesium.Cartesian3.fromDegrees(centerLon, centerLat);// 计算圆的 Cartesian3 点集(近似采样)
const granularity = Cesium.Math.RADIANS_PER_DEGREE; // 采样精度(弧度/度)
const positions = [];
for (let angle = 0; angle < 360; angle += granularity) {const radians = Cesium.Math.toRadians(angle);// 计算圆周上的点(基于球面坐标)const x = radiusInMeters * Math.cos(radians);const y = radiusInMeters * Math.sin(radians);// 将局部坐标转换为全局 Cartesian3const point = Cesium.Cartesian3.fromDegrees(centerLon + x / 111320, // 经度偏移(1度≈111320米)centerLat + y / (111320 * Math.cos(Cesium.Math.toRadians(centerLat))), // 纬度偏移(考虑纬度缩放)0 // 高度(与圆心相同));positions.push(point);
}// 闭合圆(首尾相连)
positions.push(positions[0]);// 使用 Primitive API 添加圆
const primitiveCollection = viewer.scene.primitives.add(new Cesium.PrimitiveCollection());
primitiveCollection.add(new Cesium.Primitive({geometryInstances: new Cesium.GeometryInstance({geometry: new Cesium.PolygonGeometry({polygonHierarchy: new Cesium.PolygonHierarchy(positions),perPositionHeight: false // 固定高度}),attributes: {color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.BLUE.withAlpha(0.5) // 半透明蓝色)}}),appearance: new Cesium.PerInstanceColorAppearance({outline: true,outlineColor: Cesium.Color.RED,outlineWidth: 2})})
);// 定位相机到圆的位置
viewer.camera.flyTo({destination: Cesium.Cartesian3.fromDegrees(centerLon, centerLat, 5000),orientation: {heading: Cesium.Math.toRadians(0),pitch: Cesium.Math.toRadians(-30)}
});

七、椭圆(Ellipse)​

使用 Primitive API(需手动计算椭圆点集)​

const viewer = new Cesium.Viewer('cesiumContainer', {terrainProvider: Cesium.createWorldTerrain(),baseLayerPicker: false, // 禁用底图选择器geocoder: false, // 禁用地理编码器homeButton: false, // 禁用主页按钮infoBox: false, // 禁用信息框sceneModePicker: false, // 禁用场景模式选择器selectionIndicator: false, // 禁用选择指示器navigationHelpButton: false, // 禁用导航帮助按钮animation: false, // 禁用动画控件timeline: false, // 禁用时间轴fullscreenButton: false // 禁用全屏按钮
});// 定义椭圆的中心点、半长轴、半短轴和旋转角度const centerLon = 113.3244;const centerLat = 23.1049;const semiMajorAxis = 2000; // 半长轴(米)const semiMinorAxis = 1000; // 半短轴(米)const rotation = Cesium.Math.toRadians(45); // 旋转角度(弧度)// 将椭圆中心转换为 Cartesian3 坐标const centerCartesian = Cesium.Cartesian3.fromDegrees(centerLon, centerLat);// 计算椭圆的 Cartesian3 点集(近似采样)const granularity = Cesium.Math.RADIANS_PER_DEGREE; // 采样精度(弧度/度)const positions = [];for (let angle = 0; angle < 360; angle += granularity) {const radians = Cesium.Math.toRadians(angle);// 计算椭圆上的点(基于参数方程)const x = semiMajorAxis * Math.cos(radians);const y = semiMinorAxis * Math.sin(radians);// 旋转椭圆const rotatedX = x * Math.cos(rotation) - y * Math.sin(rotation);const rotatedY = x * Math.sin(rotation) + y * Math.cos(rotation);// 将局部坐标转换为全局 Cartesian3const point = Cesium.Cartesian3.fromDegrees(centerLon + rotatedX / 111320, // 经度偏移(1度≈111320米)centerLat + rotatedY / (111320 * Math.cos(Cesium.Math.toRadians(centerLat))), // 纬度偏移(考虑纬度缩放)0 // 高度(与椭圆中心相同));positions.push(point);}// 闭合椭圆(首尾相连)positions.push(positions[0]);// 使用 Primitive API 添加椭圆const primitiveCollection = viewer.scene.primitives.add(new Cesium.PrimitiveCollection());primitiveCollection.add(new Cesium.Primitive({geometryInstances: new Cesium.GeometryInstance({geometry: new Cesium.PolygonGeometry({polygonHierarchy: new Cesium.PolygonHierarchy(positions),perPositionHeight: false // 固定高度}),attributes: {color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.BLUE.withAlpha(0.5) // 半透明蓝色)}}),appearance: new Cesium.PerInstanceColorAppearance({outline: true,outlineColor: Cesium.Color.RED,outlineWidth: 2})}));// 定位相机到椭圆位置viewer.camera.flyTo({destination: Cesium.Cartesian3.fromDegrees(centerLon, centerLat, 5000),orientation: {heading: Cesium.Math.toRadians(0),pitch: Cesium.Math.toRadians(-30)}});

八、球(Sphere)​

使用 Primitive API(Primitive 方式需手动构建 Geometry)​

const sphereCollection = viewer.scene.primitives.add(new Cesium.PrimitiveCollection());
sphereCollection.add(new Cesium.Primitive({geometryInstances: new Cesium.GeometryInstance({geometry: new Cesium.EllipsoidGeometry({vertexFormat: Cesium.VertexFormat.POSITION_AND_NORMAL,radii: new Cesium.Cartesian3(100, 100, 100)})}),appearance: new Cesium.PerInstanceColorAppearance()})
);

九、3D 模型(Model)​

使用 Primitive API(Primitive 方式需手动加载模型)​

性能优化建议​

场景推荐方式原因
​少量要素​Entity API代码简洁,开发效率高
​大量要素(>1000)​Primitive API性能更高,减少 CPU-GPU 通信开销
​动态更新(如轨迹动画)​Entity API支持更简单的属性动画
​自定义渲染(如特殊着色器)​Primitive API可深度定制渲染逻辑

总结​

要素类型推荐 API示例代码
​点​Entity 或 PointPrimitiveCollectionviewer.entities.add({ point: {...} })
​线​Entity 或 PolylineCollectionviewer.entities.add({ polyline: {...} })
​多边形​Entity 或 PolygonCollectionviewer.entities.add({ polygon: {...} })
​圆/椭圆​Entity API(更简单)viewer.entities.add({ circle: {...} })
​球​Entity API(更简单)viewer.entities.add({ ellipsoid: {...} })
​模型​Entity API(更简单)viewer.entities.add({ model: {...} })

相关文章:

  • 【Linux】进程优先级和切换调度
  • android关于native中Thread类的使用
  • C++ 环境配置
  • Visual studio 中 使用QT插件 编辑UI文件打开 Qt Designer 报错 问题解决方案
  • 论文精读Lami-Detr:Open-Vocabulary Detection with Language Model Instruction
  • 【量化】策略交易之动量策略(Momentum)
  • YOLOv2 中非极大值抑制(NMS)机制详解与实现
  • n8n 从 Docker 到 Node.js 本地环境迁移指南
  • Qt .pro配置gcc相关命令(三):-W1、-L、-rpath和-rpath-link
  • std::shared_ptr引起内存泄漏的例子
  • [CVPR 2025] DeformCL:基于可变形中心线的3D血管提取新范式
  • AI应用:计算机视觉相关技术总结
  • 蓝桥杯国赛前一晚知识点准备(十六届python)
  • 灵敏度分析
  • Codeforces Round 1030 (Div. 2)
  • STM32项目---汽车氛围灯
  • Flutter JSON解析全攻略:使用json_serializable实现高效序列化
  • MySQL 调优笔记
  • 项目拓展-简易SQL监控,P6SPY拦截所有jdbc连接并打印执行SQL
  • 第1章 C# 和 .NET 框架 笔记
  • php网站管理系统/产品关键词
  • 哪家专门做特卖的网站?/网络营销的特点包括
  • 在线销售型的网站/百度公司总部在哪里
  • java做网站用的是什么/软文范例大全
  • 158百事通做网站是诈骗吗/百度搜索引擎排行榜
  • 网站开发应该学哪门语言/手机系统优化软件