FreeXGIS + Cesium 实现三维场景多标记点与自定义交互标牌开发

在三维地理信息系统(GIS)开发中,标记点(Marker)是最基础也最常用的可视化元素,而结合自定义交互标牌能极大提升用户体验。本文将基于 FreeXGIS(FreeXEarth)和 Cesium 引擎,详细讲解如何实现三维场景中多标记点的创建、自定义交互式标牌的开发,以及标记点的管理与交互优化。

一、技术背景与核心依赖

1. FreeXGIS(FreeXEarth)

FreeXGIS 是一款轻量级的 WebGIS 开发框架,封装了 Cesium 引擎的核心能力,简化了三维地球的初始化、图层管理、要素操作等流程,适合快速搭建三维 GIS 应用。

2. Cesium 引擎

Cesium 是一款开源的三维地理空间可视化引擎,支持高精度地形、影像、矢量要素的渲染,提供了丰富的空间坐标转换、相机控制、实体管理等 API,是 Web 端三维 GIS 开发的主流选择。

二、核心功能实现步骤

1. 三维地球初始化配置

首先完成 FreeXEarth 的初始化,配置基础图层(WMTS 格式),并获取 Cesium Viewer 实例(FreeXEarth 实例可直接兼容 Cesium Viewer 核心 API)。

javascript 复制代码
// 初始化 FreeXEarth 实例
var FreeEarth = new FreeXEarth.FeMap($('#editEarthContainer')[0]);
var options = {
    layerManagerConfig: {
        baseLayers: [{
            "url": "dataserver/htc/service/wmts",
            "name": "World_30m_Local_6dm",
            "title": "HGT",
            "format": "image/jpeg",
            "visible": true,
            "maxVisibleLevel": 21,
            "iconUrl": "/static/img/geolayer/hgt.png",
            "mobileIconUrl": "static/img/geolayer/mobile_hgt.png",
            "type": "WMTS"
        }],
        "terrains": [],
    },
    dataSources: []
};
FreeEarth.loadConfig(options);

// 获取 Cesium Viewer 实例(FreeXEarth 实例兼容 Cesium Viewer API)
var viewer = FreeEarth;
// 存储所有创建的实体,便于后续管理
var entities = [];

2. 多标记点创建(Cesium Billboard)

通过 Cesium 原生的 Billboard 实现标记点渲染,支持自定义图标、尺寸、颜色等样式,并批量创建多个位置的标记点。

javascript 复制代码
// 添加多个marker的函数
function addMultipleMarkers() {
    // 定义多个位置坐标 [经度, 纬度, 高度]
    var positions = [
        [115, 33, 1000],
        [116, 33.5, 1500],
        [117, 32.8, 2000],
        [115.5, 32.5, 1200],
        [116.5, 33.2, 1800]
    ];
    
    // 为每个位置创建marker和自定义标牌
    positions.forEach(function(position, index) {
        // 1. 创建Cesium原生marker(Billboard)
        var entity = viewer.entities.add({
            position: Cesium.Cartesian3.fromDegrees(position[0], position[1], position[2]),
            billboard: {
                image: 'femap/Data/images/marker/point.png', // 自定义标记图标
                width: 32,
                height: 32,
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 锚点在底部
                scale: 1.0,
                color: Cesium.Color.WHITE
            },
            name: '标记点 ' + (index + 1)
        });
        
        // 2. 创建自定义div标牌
        createCustomLabel(position, index);
        
        entities.push(entity); // 存入实体数组,便于后续管理
    });
}

3. 自定义交互式标牌开发

原生 Cesium Label 样式定制能力有限,因此通过 HTML/CSS 自定义 div 标牌,结合 Cesium 坐标转换 API 实现标牌与三维空间位置的绑定,并添加鼠标交互、相机联动更新等功能。

3.1 创建自定义标牌 DOM 元素
javascript 复制代码
// 创建自定义div标牌
function createCustomLabel(position, index) {
    // 创建div元素
    var labelDiv = document.createElement('div');
    labelDiv.className = 'custom-label';
    // 填充标牌内容(经度、纬度、高度等信息)
    labelDiv.innerHTML = `
        <div class="label-content">
            <div class="label-title">标牌 ${index + 1}</div>
            <div class="label-info">经度: ${position[0].toFixed(4)}</div>
            <div class="label-info">纬度: ${position[1].toFixed(4)}</div>
            <div class="label-info">高度: ${position[2]}米</div>
        </div>
    `;
    
    // 基础样式设置
    labelDiv.style.cssText = `
        position: absolute;
        background: rgba(0, 0, 0, 0.7);
        color: white;
        padding: 8px 12px;
        border-radius: 4px;
        font-size: 12px;
        border: 1px solid #00a0e9;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
        white-space: nowrap;
        pointer-events: auto;
        cursor: pointer;
        transform: translate(-50%, -100%);
        margin-top: -40px;
    `;
    
    // 添加到viewer的容器中
    viewer.container.appendChild(labelDiv);
    
    // 初始化更新标牌位置
    updateLabelPosition(labelDiv, position);
    
    // 鼠标悬停样式交互
    labelDiv.addEventListener('mouseenter', function() {
        this.style.background = 'rgba(0, 100, 200, 0.9)';
    });
    labelDiv.addEventListener('mouseleave', function() {
        this.style.background = 'rgba(0, 0, 0, 0.7)';
    });
    
    // 点击标牌飞行到对应标记点
    labelDiv.addEventListener('click', function() {
        viewer.flyTo(entities[index], {
            duration: 1.5,
            offset: new Cesium.HeadingPitchRange(0, -0.5, 1000)
        });
    });
    
    // 监听相机变化,实时更新标牌位置
    viewer.scene.postRender.addEventListener(function() {
        updateLabelPosition(labelDiv, position);
    });
}
3.2 标牌位置实时更新

通过 Cesium 的 cartesianToCanvasCoordinates API 将三维空间坐标转换为画布像素坐标,实现标牌随相机视角、地球旋转实时更新位置;当标记点超出视口时,隐藏标牌。

javascript 复制代码
// 更新标牌位置
function updateLabelPosition(labelDiv, position) {
    var canvasPosition = viewer.scene.cartesianToCanvasCoordinates(
        Cesium.Cartesian3.fromDegrees(position[0], position[1], position[2])
    );
    
    if (canvasPosition) {
        labelDiv.style.left = canvasPosition.x + 'px';
        labelDiv.style.top = canvasPosition.y + 'px';
        labelDiv.style.display = 'block';
    } else {
        labelDiv.style.display = 'none';
    }
}

4. 标记点与标牌的统一管理

实现标记点和标牌的批量移除功能,包括 Cesium 实体的删除、自定义 DOM 标牌的清理,以及 FreeXEarth 要素的移除(兼容原有框架逻辑)。

javascript 复制代码
// 移除所有标记和标牌
function removeMark() {
    // 移除所有Cesium实体
    entities.forEach(function(entity) {
        viewer.entities.remove(entity);
    });
    entities = [];
    
    // 移除所有自定义标牌DOM
    var labels = document.querySelectorAll('.custom-label');
    labels.forEach(function(label) {
        if (label.parentNode) {
            label.parentNode.removeChild(label);
        }
    });
    
    // 移除FreeXEarth的feature(兼容原有框架)
    FreeEarth.removeAllFeature();
    arrow.map(item => {
        item.removeFromMap();
    });
}

5. 样式优化与交互增强

通过 CSS 过渡效果、字体样式优化,提升标牌的视觉体验和交互流畅度。

javascript 复制代码
// 添加CSS样式
function addCustomStyles() {
    var style = document.createElement('style');
    style.textContent = `
        .custom-label {
            transition: all 0.3s ease;
            font-family: 'Microsoft YaHei', Arial, sans-serif;
        }
        
        .label-content {
            line-height: 1.4;
        }
        
        .label-title {
            font-weight: bold;
            color: #00a0e9;
            margin-bottom: 4px;
            border-bottom: 1px solid #00a0e9;
            padding-bottom: 2px;
        }
        
        .label-info {
            font-size: 11px;
            opacity: 0.9;
        }
        
        .custom-label:hover {
            transform: translate(-50%, -100%) scale(1.05);
            z-index: 1000;
        }
    `;
    document.head.appendChild(style);
}

6. 初始化与测试优化

初始化样式、添加标记点,并可选择添加测试按钮,方便调试标记点的添加 / 清除功能。

javascript 复制代码
// 初始化样式
addCustomStyles();
// 批量添加标记点和标牌
addMultipleMarkers();

// 飞行到初始查看位置
FreeEarth.flyTo({
    destination: [115, 33, 4500000],
    duration: 3,
});

// 可选:添加测试按钮
function addTestButtons() {
    var buttonContainer = document.createElement('div');
    buttonContainer.style.cssText = `
        position: absolute;
        top: 10px;
        left: 10px;
        z-index: 1000;
        background: rgba(0,0,0,0.7);
        padding: 10px;
        border-radius: 4px;
    `;
    
    var addBtn = document.createElement('button');
    addBtn.textContent = '添加标记';
    addBtn.onclick = addMultipleMarkers;
    
    var removeBtn = document.createElement('button');
    removeBtn.textContent = '清除标记';
    removeBtn.onclick = removeMark;
    
    buttonContainer.appendChild(addBtn);
    buttonContainer.appendChild(removeBtn);
    viewer.container.appendChild(buttonContainer);
}
// addTestButtons(); // 取消注释启用测试按钮

三、关键技术要点总结

1. 坐标转换

Cesium 的 cartesianToCanvasCoordinates 是实现三维空间坐标与二维画布坐标绑定的核心,确保自定义 DOM 标牌能精准跟随三维标记点。

2. 相机联动

通过 viewer.scene.postRender 事件监听相机变化,实时更新标牌位置,解决地球旋转、缩放、视角切换时标牌偏移的问题。

3. 交互体验优化

  • 鼠标悬停时的样式过渡,提升视觉反馈;
  • 点击标牌飞行到标记点,结合 Cesium flyTo API 实现平滑视角切换;
  • 超出视口时隐藏标牌,避免无效渲染和视觉干扰。

4. 资源管理

通过数组存储 Cesium 实体,批量移除时避免内存泄漏;清理自定义 DOM 元素时检查父节点,确保移除成功。

四、扩展与优化方向

  1. 性能优化:当标记点数量较多(上千个)时,可通过 Cesium 的 BillboardCollection 批量渲染,替代单个 Entity 创建,提升渲染性能;
  2. 样式定制:支持根据标记点属性(如类型、状态)动态切换标牌样式和图标;
  3. 拖拽交互:结合 Cesium 的 ScreenSpaceEventHandler 实现标记点拖拽,同步更新标牌位置;
  4. 数据驱动:对接后端接口,从数据库加载标记点坐标和属性,实现动态渲染;
  5. 移动端适配:优化标牌尺寸、字体大小,适配移动端触摸交互。

五、总结

本文基于 FreeXGIS 和 Cesium 实现了三维场景中多标记点与自定义交互式标牌的开发,核心在于结合 Cesium 的空间坐标转换、相机监听能力,以及 HTML/CSS 的灵活样式定制,既保留了 FreeXGIS 框架的便捷性,又通过原生 DOM 扩展了交互体验。帮助开发者快速实现高交互性的三维标记点可视化功能。

相关推荐
漫长的~以后1 小时前
全模态交互革命:阿里Qwen3-Omni-Flash深度解析,拟人化AI的奇点已至
人工智能·交互
song5013 小时前
鸿蒙 Flutter 支付安全:TEE 可信环境下的支付校验实战
分布式·flutter·百度·重构·交互
自在极意功。13 小时前
Web开发中的分层解耦
java·microsoft·web开发·解耦
txzz888814 小时前
网络应用netstart命令
网络·windows·计算机网络·microsoft
灰灰勇闯IT1 天前
RN核心语法与组件体系:UI布局与基础交互
ui·交互·rn
song5011 天前
鸿蒙 Flutter 语音交互进阶:TTS/STT 全离线部署与多语言适配
分布式·flutter·百度·华为·重构·electron·交互
武藤一雄1 天前
[WPF] 万字拆解依赖属性与附加属性
前端·microsoft·c#·.net·wpf
灰灰勇闯IT1 天前
RN原生模块交互:打通JS与原生的桥梁
开发语言·javascript·交互
武藤一雄1 天前
C#:深入浅出委托(Delegate/Func/Action/Predicate)
开发语言·后端·microsoft·微软·c#·.net