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进行属性获取、建筑物距离计算、建筑物着色及其原理分析,深入的理解了其原理,回见~

相关推荐
undefined&&懒洋洋3 天前
Cesium使用flyToBoundingSphere实现倾斜相机视角观察物体
前端·javascript·cesium·webgis
GIS瞧葩菜6 天前
GeoSever发布图层(保姆姬)
wms·geoserver·cesium
新中地GIS开发老师6 天前
地理信息科学专业想搞GIS开发:学前端还是后端?
前端·javascript·arcgis·前端框架·cesium
qbbmnnnnnn13 天前
【WebGis开发 - Cesium】三维可视化项目教程---图层管理拓展图层顺序调整功能
vue.js·webgl·三维可视化·cesium·vue3.0·webgis·vuedraggable
smiler15 天前
cesium两种方式实现贴地
前端·cesium
按图索迹17 天前
100GB,台湾台东县绿岛倾斜摄影3DTiles数据来了
三维可视化·cesium·倾斜摄影·3dtiles·台湾倾斜摄影
cy_b18 天前
PHP的json浮点精度难题
php·float·原理·json_encode
htsitr24 天前
Cesium如果链接着色器的?
cesium·着色器
T0uken1 个月前
【WebGIS】Cesium:Viewer 初始化、地图加载与基础交互
gis·cesium·webgis
qbbmnnnnnn1 个月前
【WebGis开发 - Cesium】三维可视化项目教程---图层管理基础
前端·wmts·cesium·vue3.0·webgis·3dtiles·图层管理