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

相关推荐
duansamve7 小时前
WebGIS地图框架有哪些?
javascript·gis·openlayers·cesium·mapbox·leaflet·webgis
汪洪墩1 天前
【Mars3d】实现这个地图能靠左,不居中的样式效果
前端·javascript·vue.js·3d·webgl·cesium
vener_4 天前
Cesium用zoomTo函数实现对目标(各种实体)的视角控制
前端·javascript·cesium
智方科技8 天前
3DTiles之使用customShader调整风格
3d·智慧城市·cesium
山海鲸可视化8 天前
GIS融合之路(八)-如何用Cesium直接加载OSGB文件(不用转换成3dtiles)
3d·gis·数字孪生·cesium·倾斜摄影·osgb
智方科技8 天前
如何编译 Cesium 源码
3d·智慧城市·cesium·tilesbuilder
柳晓黑胡椒9 天前
cesiusm实现 多图例展示+点聚合(base64图标)
css3·canvas·base64·cesium·animation
智方科技小智9 天前
3DTiles之使用customShader调整风格
cesium
智方科技小智9 天前
3D Tiles 之 Geometry Error 介绍
cesium
年轻的古尔丹9 天前
【C++ 20进阶(2):属性 Attribute】
c++20·属性·新特性·c++20新特性·c++属性