告别冗余代码!Cesium点位图标模糊、重叠?自适应参数调优攻略,一次封装终身复用!

之前已经实现了在 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

总结

地图上实现图标是常见需求,毕竟不可能每一个设备一个模型,加载就是个大问题。

更不要说模型本身也是需要花钱的。

如果有需要可以增加距离自适应效果,或者可以等一下后期我实现的图标聚合效果。

相关推荐
之歆1 天前
DAY_12JavaScript DOM 完全指南(二):实战与性能篇
开发语言·前端·javascript·ecmascript
发现一只大呆瓜1 天前
Vite凭什么这么快?3分钟带你彻底搞懂 Vite 热更新的幕后黑手
前端·面试·vite
Maimai108081 天前
React如何用 @microsoft/fetch-event-source 落地 SSE:比原生 EventSource 更灵活的实时推送方案
前端·javascript·react.js·microsoft·前端框架·reactjs·webassembly
kyriewen1 天前
产品经理把PRD写成“天书”,我用AI半小时重写了一遍,他当场愣住
前端·ai编程·cursor
humcomm1 天前
元框架的工作原理详解
前端·前端框架
canonical_entropy1 天前
Attractor Before Harness: AI 大规模开发的方法论
前端·aigc·ai编程
zhangxingchao1 天前
多 Agent 架构到底怎么选?从 Claude Agent Teams、Cognition/Devin 到工程落地原则
前端·人工智能·后端
IT_陈寒1 天前
SpringBoot那个自动配置的坑,害我排查到凌晨三点
前端·人工智能·后端
Honor丶Onlyou1 天前
VS Code 右键菜单修复记录
前端
PILIPALAPENG1 天前
Python 语法速成指南:前端开发者视角(JS 类比版)
前端·人工智能·python