之前已经实现了在 Cesium 地图上加载模型# 数字孪生大屏必看:Cesium 3D 模型选中交互,3 种高亮效果拿来就用!,但是不可能所有的设备都以模型呈现出来,所以这里一些小的设备采用图标的形式进行展示。

最常见的就是监控类的图标,这里也是以监控类的图标为例,代码一把梭实现一下。
渲染图标的代码我这里也进行了简单的封装。
实现方案
Cesium 中实现自定义图标展示,核心依赖 Entity API + Billboard 组件:
Entity用来封装地理空间数据,适合业务层点位管理。

另外Billboard 是Entity的子组件,专门用于渲染屏幕对齐的图像/图标。
使用Billboard能够实现无论相机如何旋转,图标始终面向屏幕。
实现代码
建议图标使用 PNG 图片,而不是 SVG,在Cesium中png表现更好。
另外图标需要放在 public 文件夹下,避免出现打包丢失的情况。
有条件可以直接使用在线图标,更稳定。
javascript
/**
* 地图生成图标公共方法(封装)
* @param {Object} options - 图标配置项
* @param {Number} options.longitude - 图标经度
* @param {Number} options.latitude - 图标纬度
* @param {Number} options.height - 图标高度 (米)
* @param {String} options.iconWidth - 图标宽度 (像素),默认值为32
* @param {String} options.iconHeight - 图标高度 (像素),默认值为32
* @param {String} options.iconUrl - 图标 URL
* @param {Number} [options.scale=1.0] - 图标缩放比例
* @param {String} [options.label] - 图标标签
* @param {String} [options.id] - 图标唯一标识符(可选)
* @returns {Cesium.Entity} 创建的图标实体
*/
const createIcon = (options) => {
// 校验Viewer实例是否存在
if (!viewer.value) {
console.warn('Viewer 实例不存在,无法创建图标')
return null
}
// 图标路径自动处理(兼容相对路径/绝对路径)
let fullIconUrl = iconUrl
if (!iconUrl.startsWith('http') && !iconUrl.startsWith('/')) {
fullIconUrl = `/${iconUrl}`
}
const entity = viewer.value.entities.add({
// 唯一ID
id: id || `icon_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
// 经纬度+高度转笛卡尔坐标
position: Cesium.Cartesian3.fromDegrees(longitude, latitude, height),
billboard: {
image: fullIconUrl, // 图标图片路径
scale: scale, // 缩放比例
width: options.iconWidth || 32, // 图标宽度
height: options.iconHeight || 32, // 图标高度
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 底部对齐坐标点
pixelOffset: new Cesium.Cartesian2(0, 0), // 像素偏移
eyeOffset: new Cesium.Cartesian3(0, 0, 0), // 视点偏移
// 距离自适应缩放
scaleByDistance: new Cesium.NearFarScalar(1.5e2, 1.0, 1.5e7, 0.5),
translucencyByDistance: new Cesium.NearFarScalar(1.5e2, 1.0, 1.5e7, 0.0),
// 关闭深度检测,图标始终显示在最前面
disableDepthTestDistance: Number.POSITIVE_INFINITY
}
})
// 图标标签(可选)
if (label) {
entity.label = {
text: label, // 标签文字
font: '14px sans-serif', // 字体样式
fillColor: Cesium.Color.WHITE, // 文字填充色
outlineColor: Cesium.Color.BLACK,// 文字描边色
outlineWidth: 2, // 描边宽度
style: Cesium.LabelStyle.FILL_AND_OUTLINE, // 填充+描边
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -40), // 标签向上偏移
showBackground: true, // 显示背景底板
backgroundColor: new Cesium.Color(0.165, 0.165, 0.165, 0.8), // 背景色
backgroundPadding: new Cesium.Cartesian2(7, 5),
disableDepthTestDistance: Number.POSITIVE_INFINITY // 标签置顶
}
}
return entity
};
注意:这里其实我是建议将生成图标的方法写入 cesium-view 这种 cesium地图 自己封装的组件中的。
如果将生成图标的放在放在组件外,需要考虑加载时机等问题。
javascript
// 业务层调用
const loadCameraIcon = () => {
// 摄像头点位数据(可替换为后端接口返回数据)
const cameraList = [
{
longitude: 117.106447,
latitude: 36.436758,
height: 3,
label: '摄像头1',
iconUrl: '/images/枪形摄像头.png',
},
{
longitude: 117.106590,
latitude: 36.437983,
height: 3,
label: '摄像头2',
iconUrl: '/images/球形摄像头.png',
},
];
cameraList.forEach((camera) => {
cesiumViewRef.value.createIcon(camera);
});
};
可优化部分
还可以增加 距离自适应 效果。避免出现远距离图标密集重叠、渲染性能损耗大等情况。
javascript
// 缩放:150米内正常大小,1500万米外缩小为0.5倍
scaleByDistance: new Cesium.NearFarScalar(150, 1.0, 15000000, 0.5)
// 透明度:150米内完全显示,1500万米外完全透明
translucencyByDistance: new Cesium.NearFarScalar(150, 1.0, 15000000, 0.0)
除此之外有需求的话可以添加图标置顶显示。让图标始终显示在地形、三维模型前方,不会被遮挡。
javascript
disableDepthTestDistance: Number.POSITIVE_INFINITY
总结
地图上实现图标是常见需求,毕竟不可能每一个设备一个模型,加载就是个大问题。
更不要说模型本身也是需要花钱的。
如果有需要可以增加距离自适应效果,或者可以等一下后期我实现的图标聚合效果。