cesium点聚合点击扩散点位

Cesium 实现点聚合点击扩散效果

在 Cesium 三维地图应用中,当需要展示大量点位数据时,点聚合(Point Clustering)是一种常用的可视化优化技术。然而,聚合后的点位如何高效地展示和交互,成为了一个关键问题。本文将介绍一种简洁而实用的解决方案:点击聚合点后,相机自动飞行并放大到合适的高度,实现聚合点的”扩散”效果,让用户可以清晰查看聚合点包含的所有单独点位。

问题背景

在处理大规模点位数据时,点聚合技术可以有效减少渲染压力,提升应用性能。但这带来了新的交互挑战:

  • 用户如何查看被聚合的详细点位?
  • 如何设计直观的交互方式展开聚合点?
  • 扩散后,如何让用户清晰地了解空间位置关系?

传统的解决方案往往是点击后弹出列表,或者简单地取消聚合显示所有点位,这些方法要么缺乏空间关联性,要么容易产生视觉混乱。

解决方案

我们设计了一种基于相机飞行的扩散效果:当用户点击聚合点时,应用会计算聚合点内所有点位的中心位置,然后控制相机飞行到该位置上方的适当高度,使得聚合点自然”扩散”为单独的点位。

这种方案的优势在于:

  1. 保持了空间上下文,用户不会迷失方向
  2. 交互方式直观,符合用户心智模型
  3. 视觉效果流畅,提供良好的用户体验

实现代码

1. 点击事件处理

首先,我们需要处理地图点击事件,判断用户是否点击了聚合点:

1
2
3
4
5
6
7
8
9
10
11
// 点击聚合点扩散功能
earthCtrl.pick((data, pickObject, movement) => {
if (!pickObject) {
return;
}
if (Array.isArray(pickObject.id) && pickObject.id.length > 1) {
const clusteredEntities = pickObject.id; // 聚合点的所有实体
const rate = 4; // 缩放倍数
clusterLayer.expandCluster(clusteredEntities, rate);
}
});

这段代码的关键点:

  • 通过pick事件捕获用户点击
  • 判断点击对象的 id 是否为数组且长度大于 1,这是 Cesium 中聚合点的典型特征
  • 获取聚合点包含的所有实体,传递给扩散函数
  • 设置缩放倍数,决定相机放大的程度

2. 扩散功能实现

接下来,实现扩散函数,计算聚合点中心并控制相机飞行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
* 点击扩散聚合点
* @param {*} clusteredEntities
* @param {*} rate
*/
ClusterLayer.prototype.expandCluster = function (clusteredEntities, rate = 4) {
let totalLon = 0;
let totalLat = 0;
clusteredEntities.forEach(entity => {
const position = entity.position.getValue(Cesium.JulianDate.now());
const cartographic = Cesium.Cartographic.fromCartesian(position);
totalLon += Cesium.Math.toDegrees(cartographic.longitude);
totalLat += Cesium.Math.toDegrees(cartographic.latitude);
});
const centerLon = totalLon / clusteredEntities.length;
const centerLat = totalLat / clusteredEntities.length;
const currentHeight = this._viewer.camera.positionCartographic.height;
const targetHeight = currentHeight / rate;
this._viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(centerLon, centerLat, targetHeight),
duration: 1.5,
});
};

这段代码的实现细节:

  1. 遍历所有聚合实体,获取每个点的位置
  2. 计算所有点的经纬度平均值,作为聚合点的中心
  3. 获取当前相机高度,并根据缩放倍数计算目标高度
  4. 使用flyTo方法控制相机飞行到新位置,实现平滑的过渡效果

代码解析

中心点计算逻辑

聚合点中心的计算使用了简单的平均值:

1
2
3
4
5
6
7
8
clusteredEntities.forEach(entity => {
const position = entity.position.getValue(Cesium.JulianDate.now());
const cartographic = Cesium.Cartographic.fromCartesian(position);
totalLon += Cesium.Math.toDegrees(cartographic.longitude);
totalLat += Cesium.Math.toDegrees(cartographic.latitude);
});
const centerLon = totalLon / clusteredEntities.length;
const centerLat = totalLat / clusteredEntities.length;

这里的关键操作:

  • 使用position.getValue()获取当前时间点的位置值
  • 将 Cartesian3 坐标转换为经纬度表示
  • 累加所有点的经纬度值,最后求平均值

相机高度计算

相机高度的计算基于当前视图高度和缩放比例:

1
2
const currentHeight = this._viewer.camera.positionCartographic.height;
const targetHeight = currentHeight / rate;

这种方式确保了缩放效果与当前视图状态相适应:

  • 当用户在高空视图点击聚合点时,扩散后会显示中等范围
  • 当用户在低空视图点击聚合点时,扩散后会显示更详细的局部区域

相机飞行控制

最后,使用 Cesium 的飞行功能实现平滑过渡:

1
2
3
4
this._viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(centerLon, centerLat, targetHeight),
duration: 1.5,
});
  • destination参数指定目标位置,使用之前计算的中心点和高度
  • duration参数控制飞行动画的持续时间,1.5 秒提供了良好的平衡,既不会太慢导致用户等待,也不会太快导致视觉不连贯

总结

点聚合点击扩散功能是一种简单而强大的交互设计,它既保留了点聚合带来的性能优势,又解决了聚合点信息查看的交互问题。通过控制相机的智能飞行,我们实现了自然流畅的扩散效果,让用户可以轻松地在不同层级之间进行探索。

这种实现方式代码量少,易于集成,同时可以根据实际需求进行各种优化和扩展。在处理大规模点位数据的 Cesium 应用中,这将成为一个非常实用的交互功能。

作者

wangyawei

发布于

2025-03-21

更新于

2025-03-21

许可协议