Cesium源码解析六(3dtiles属性获取、建筑物距离计算、建筑物着色及其原理分析)

快速导航

Cesium源码解析一(搭建开发环境)
Cesium源码解析二(terrain文件的加载、解析与渲染全过程梳理)
Cesium源码解析三(metadataAvailability的含义)
Cesium源码解析四(metadata元数据拓展中行列号的分块规则解析)
Cesium源码解析五(Quantized-Mesh(.terrain)格式文件在CesiumJS和UE中加载情况的对比)
Cesium源码解析六(3dtiles属性获取、建筑物距离计算、建筑物着色及其原理分析)

目录

前言

在本文中,我们将探讨如何使用 CesiumJS 来加载和显示 3D 建筑物数据,并根据用户点击的位置进行动态着色。我们将使用 CesiumJS 的 OpenStreetMap 建筑物数据集,通过点击地图上的建筑物,根据距离计算并动态地为这些建筑物着色。这不仅增强了地图的交互性,还为用户提供了直观的地理空间数据可视化体验。我们将逐步讲解如何设置 Cesium Viewer,加载 3D 建筑物数据,处理用户点击事件,以及根据距离进行建筑物的颜色处理。希望通过本文,您能深入理解 CesiumJS 的强大功能,并能够在自己的项目中应用这些技术。

1.代码示例

js 复制代码
// 创建一个Cesium Viewer实例,指定目标DOM元素的id为 "cesiumContainer"
var viewer = new Cesium.Viewer("cesiumContainer", {
    baseLayerPicker: false,  // 禁用基础图层选择器
});

// 创建一个事件处理器,用于处理屏幕空间事件(如点击、拖动)
var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);

// 创建OpenStreetMap(OSM)建筑物的3D模型
var osmBuildings = Cesium.createOsmBuildings();

// 将OSM建筑物添加到场景的primitives集合中
viewer.scene.primitives.add(osmBuildings);

// 设置相机视图,定位到指定的经纬度(西雅图附近)和高度
viewer.scene.camera.setView({
    destination: Cesium.Cartesian3.fromDegrees(-122.3472, 47.598, 370),
    orientation: {
        heading: Cesium.Math.toRadians(10), // 方向(水平旋转角度)
        pitch: Cesium.Math.toRadians(-10)   // 倾斜(垂直旋转角度)
    }
});

// 定义一个函数,用于获取和打印特征的属性
function getFeatureProperty(feature) {
    let names = feature.getPropertyNames();  // 获取特征属性的名称数组
    for (var i = 0; i < names.length; i++) {
        var prop = feature.getProperty(names[i]);  // 获取每个属性的值
        if (prop) console.log(names[i], prop);     // 打印属性名称和值
    }
}

// 定义一个函数,用于根据距离着色
function showByDistance() {
    colorByDistanceToCoordinate(47.62051, -122.34931);  // 默认根据指定经纬度着色

    // 内部函数处理点击事件
    function move(movement) {
        viewer.selectEntiy = undefined;
        var pickBuilding = viewer.scene.pick(movement.position);  // 获取点击位置的建筑物
        if (pickBuilding) {
            getFeatureProperty(pickBuilding);  // 获取点击建筑物的属性
            var latitude = pickBuilding.getProperty("cesium#latitude");
            var longitude = pickBuilding.getProperty("cesium#longitude");
            colorByDistanceToCoordinate(latitude, longitude);  // 根据点击的建筑物位置进行着色
        }
    }

    // 设置鼠标左键点击事件的处理函数
    handler.setInputAction(move, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}

// 定义一个函数,根据指定经纬度着色建筑物
function colorByDistanceToCoordinate(pickLatitude, pickLongitude) {
    osmBuildings.style = new Cesium.Cesium3DTileStyle({
        defines: {
            // 定义着色器中的distance变量,计算每个建筑物与指定坐标的距离
            distance: "distance(vec2(${feature['cesium#longitude']},${feature['cesium#latitude']})," + `vec2(${pickLongitude},${pickLatitude}))`
        },
        color: {
            conditions: [
                ["${distance} > 0.014", "color('blue')"],  // 大于0.014的距离,颜色为蓝色
                ["${distance} > 0.010", "color('green')"], // 大于0.010的距离,颜色为绿色
                ["${distance} > 0.006", "color('yellow')"],// 大于0.006的距离,颜色为黄色
                ["${distance} > 0.0001", "color('red')"],  // 大于0.0001的距离,颜色为红色
                ["true", "color('white')"],                // 其他距离,颜色为白色
            ]
        }
    });
}
// 定义一个函数,根据距离渲染建筑物
function showByDistance() {
    // 首先调用 colorByDistanceToCoordinate,以特定坐标(47.62051, -122.34931)为参考点进行初始着色
    colorByDistanceToCoordinate(47.62051, -122.34931);

    // 定义一个内部函数 move 用于处理点击事件
    function move(movement) {
        viewer.selectEntiy = undefined;  // 清除当前选择的实体
        
        // 获取点击位置的建筑物特征
        var feature = viewer.scene.pick(movement.position);

        if (feature) {  // 如果点击位置有建筑物特征
            getFeatureProperty(feature);  // 获取并打印该建筑物的属性
            
            // 获取建筑物的经度和纬度
            var latitude = feature.getProperty("cesium#latitude");
            var longitude = feature.getProperty("cesium#longitude");

            // 调用 colorByDistanceToCoordinate 以点击位置的建筑物为参考点重新着色
            colorByDistanceToCoordinate(latitude, longitude);
        }
    }

    // 设置鼠标左键点击事件的处理函数,将其与 move 函数绑定
    handler.setInputAction(move, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}

// 调用 showByDistance 函数以启动初始着色和点击事件处理
showByDistance();

        

2.属性获取原理分析

当我们点击一个建筑物时,Cesium会自动展示这个建筑物的信息。我们也在控制台输出一下自己获取到的属性信息,看看能不能对应的上。

可以看到完全对应的上。而当我们看下源码就会发现属性获取的调用过程如下:

sequenceDiagram

participant User as 用户

participant Scene as Cesium.Scene

participant Handler as ScreenSpaceEventHandler

participant Feature as Cesium3DTileFeature

participant BatchTable as Cesium3DTileBatchTable

复制代码
User->>Handler: 点击事件
Handler->>Scene: pick(movement.position)
Scene-->>Handler: 返回Cesium3DTileFeature
Handler->>Feature: 获取属性名
Feature->>BatchTable: getPropertyNames()
BatchTable-->>Feature: 返回属性名数组
Feature-->>Handler: 返回属性名数组
Handler->>Feature: 获取属性值(name)
Feature->>BatchTable: getProperty(batchId, name)
BatchTable-->>Feature: 返回属性值
Feature-->>Handler: 返回属性值
Handler-->>User: 打印属性名和值

3.建筑物距离计算原理分析

CesiumJS 中,建筑物之间的距离计算是通过 GLSL 着色器来实现的,计算每个建筑物与一个参考点之间的欧几里得距离。以下是距离计算的步骤:

  1. 定义距离计算表达式

    javascript 复制代码
    defines: {
        distance: "distance(vec2(${feature['cesium#longitude']}, ${feature['cesium#latitude']}), vec2(" + `${pickLongitude}, ${pickLatitude}))`
    }
    • defines 部分定义了 GLSL 表达式,用于计算每个建筑物与参考点的距离。
    • distance 是 GLSL(OpenGL Shading Language)中的函数,用于计算两个二维向量(坐标)之间的欧几里得距离。
    • vec2 是 GLSL 中表示二维向量的构造函数。
    • ${feature['cesium#longitude']}${feature['cesium#latitude']} 分别表示建筑物的经度和纬度。
    • ${pickLongitude}${pickLatitude} 表示参考点(如用户点击位置)的经度和纬度。
  2. 计算距离

    GLSL 表达式 distance 计算每个建筑物的位置与参考点之间的距离。这个距离值将用于后续的颜色条件判断。

    glsl 复制代码
    float distance = distance(vec2(buildingLongitude, buildingLatitude), vec2(referenceLongitude, referenceLatitude));
  3. 应用距离值

    这个计算的距离值会被应用到每个建筑物,用于在着色器中进行条件判断,决定建筑物的颜色。

    javascript 复制代码
    osmBuildings.style = new Cesium.Cesium3DTileStyle({
        defines: {
            distance: "distance(vec2(${feature['cesium#longitude']}, ${feature['cesium#latitude']}), vec2(" + `${pickLongitude}, ${pickLatitude}))`
        }
    });

4.建筑物着色原理分析

CesiumJS 中,建筑物的着色是基于计算出的距离,并通过条件语句来设置颜色的。在 Cesium3DTileStyle中定义颜色条件,根据计算出的距离为建筑物设置颜色。

javascript 复制代码
osmBuildings.style = new Cesium.Cesium3DTileStyle({
  defines:{
       distance:"distance(vec2(${feature['cesium#longitude']},${feature['cesium#latitude']}),"+`vec2(${pickLongitude},${pickLatidude}))`
   },
   color:{
       conditions:[
           ["${distance} > 0.014","color('blue')"],
           ["${distance} > 0.010","color('green')"],
           ["${distance} > 0.006","color('yellow')"],
           ["${distance} > 0.0001","color('red')"],
           ["true","color('white')"],
       ]
   }
})
  1. 定义部分

    • defines 部分定义了 GLSL 表达式,用于计算每个建筑物与参考点的距离。
    • distance 函数使用建筑物的经纬度和参考点的经纬度,计算欧几里得距离。
  2. 颜色条件部分

    • color 部分定义了颜色条件,根据 distance 值设置不同的颜色。
    • 使用条件数组 conditions,依次匹配 distance 的值,并根据匹配的条件设置颜色。

这两个原理共同作用,为用户提供了一个动态且交互的建筑物着色效果,根据点击的位置和距离进行直观的展示。

5.总结

本文中我们通过代码示例展示了如何在Cesiumjs中对3dtiles进行属性获取、建筑物距离计算、建筑物着色及其原理分析,深入的理解了其原理,回见~

相关推荐
1H1R1M17 小时前
同步绘制视锥几何体和实际相机视锥体
前端·javascript·cesium
青山Coding21 小时前
Cesium应用(一):解决 Cesium 海量船舶轨迹点渲染难题:轨迹数据池方案实践
gis·cesium
锦岁1 天前
cesium-1.92源码编译
cesium
allenjiao2 天前
Cesium粒子系统模拟风场动态效果
javascript·arcgis·gis·webgl·cesium·三维·风场
GISBox4 天前
GISBox中OSGB数据转3DTiles格式指南
3dtiles·gisbox·免费工具·odgb·切片转化
GIS瞧葩菜6 天前
Cesium 中拾取 3DTiles 交点坐标
前端·javascript·cesium
刘小筛6 天前
Cesium视锥和航向角,终于被我玩明白了。纯干货,全程无废话。
cesium
不浪brown7 天前
丝滑!Cesium中实现机械模型动作仿真全流程
cesium
duansamve10 天前
Cesium性能优化
cesium
一梦、んんん11 天前
cesium FBO(一)渲染到纹理(RTT)
cesium