快速导航
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
着色器来实现的,计算每个建筑物与一个参考点之间的欧几里得距离。以下是距离计算的步骤:
-
定义距离计算表达式:
javascriptdefines: { 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}
表示参考点(如用户点击位置)的经度和纬度。
-
计算距离:
GLSL 表达式
distance
计算每个建筑物的位置与参考点之间的距离。这个距离值将用于后续的颜色条件判断。glslfloat distance = distance(vec2(buildingLongitude, buildingLatitude), vec2(referenceLongitude, referenceLatitude));
-
应用距离值:
这个计算的距离值会被应用到每个建筑物,用于在着色器中进行条件判断,决定建筑物的颜色。
javascriptosmBuildings.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')"],
]
}
})
-
定义部分:
defines
部分定义了 GLSL 表达式,用于计算每个建筑物与参考点的距离。distance
函数使用建筑物的经纬度和参考点的经纬度,计算欧几里得距离。
-
颜色条件部分:
color
部分定义了颜色条件,根据distance
值设置不同的颜色。- 使用条件数组
conditions
,依次匹配distance
的值,并根据匹配的条件设置颜色。
这两个原理共同作用,为用户提供了一个动态且交互的建筑物着色效果,根据点击的位置和距离进行直观的展示。
5.总结
本文中我们通过代码示例展示了如何在Cesiumjs
中对3dtiles
进行属性获取、建筑物距离计算、建筑物着色及其原理分析,深入的理解了其原理,回见~