Clustering,聚合功能,单从一个例子来看,似乎没有什么作用。但是在GIS项目中是应用得比较多的功能点,当一 个区域很小,但需要显示的标注又非常多时,它就派上用场了。在C/S系统中,往往使用zoom与visible绑定来解决压盖问题,但并不能很好地解决,体验也不是很好,好久没用C/S系统了,不知道有没有平台把聚合功能整合进去。Clustering完美地解决了这个问题,再也不用担心加载的点太多而看不清楚的问题了。
代码中是以加载KML数据为例的。首先是用KmlDataSource加载KML数据,代码如下:
function addDataSources () {
var options = {
camera: viewer.scene.camera,
canvas: viewer.scene.canvas,
clampToGround: false
};
var dataSourcePromise = viewer.dataSources.add(
Cesium.KmlDataSource.load(
"../Cesium1_71/Apps/SampleData/kml/facilities/facilities.kml",
options
)
);
dataSourcePromise.then(function (dataSource) {
afterAddDataSource(dataSource)
})
}
在数据加载完成后,设置聚合功能和样式,而聚合的实现,主要有三个步骤:(1)设置数据启用聚合图标功能,并设置聚合距离;(2)首先创建设置聚合图标来渲染显示加载的数据;(3)聚合是实时更新的,所以需要一个绑定事件,把渲染的设置告诉给cesium。
KmlDataSource中的clustering属性是EntityCluster类型,下面代码设置启用聚合功能
dataSource.clustering.enabled = enabled;
dataSource.clustering.pixelRange = pixelRange;
dataSource.clustering.minimumClusterSize = minimumClusterSize;
代码中pixelRange是聚合距离,也就是小于这个距离就会被聚合,以像素为单位;minimumClusterSize是每个聚合点的最小聚合个数,这个值最好是设置为2,因为两个图标也可能叠压。
聚合图标由PinBuilder创建的,PinBuilder内置了很多图标。如下图:
可以通过多种方法获取这些图标,也可以通过fromUrl()方法使用自定义图标。事例代码中使用的是fromText()方法创建。
在通过clustering属性中的clusterEvent.addEventListener方法绑定渲染回调函数,即:Cesium.EntityCluster.newClusterCallback(clusteredEntities, cluster),其中clusteredEntities参数表示的是聚合Entities数据集;而cluster是一个包含了billboard、label、和point 属性的对象。
至此,EntityCluster属性设置完成,设置部份的代码如下:
var removeListener
function afterAddDataSource (dataSource) {
var pixelRange = 59;
var minimumClusterSize = 2;
var enabled = true;
dataSource.clustering.enabled = enabled;
dataSource.clustering.pixelRange = pixelRange;
dataSource.clustering.minimumClusterSize = minimumClusterSize;
var pinBuilder = new Cesium.PinBuilder();
var pin100 = pinBuilder
.fromText("100+", Cesium.Color.RED, 48)
.toDataURL();
var pin50 = pinBuilder
.fromText("50+", Cesium.Color.RED, 48)
.toDataURL();
var pin40 = pinBuilder
.fromText("40+", Cesium.Color.ORANGE, 48)
.toDataURL();
var pin30 = pinBuilder
.fromText("30+", Cesium.Color.YELLOW, 48)
.toDataURL();
var pin20 = pinBuilder
.fromText("20+", Cesium.Color.GREEN, 48)
.toDataURL();
var pin10 = pinBuilder
.fromText("10+", Cesium.Color.BLUE, 48)
.toDataURL();
var singleDigitPins = new Array(8);
for (var i = 0; i < singleDigitPins.length; ++i) {
singleDigitPins[i] = pinBuilder
.fromText("" + (i + 2), Cesium.Color.VIOLET, 48)
.toDataURL();
}
customStyle();
function customStyle () {
if (Cesium.defined(removeListener)) {
removeListener();
removeListener = undefined;
} else {
removeListener = dataSource.clustering.clusterEvent.addEventListener(
function (clusteredEntities, cluster) {
cluster.label.show = false;
cluster.billboard.show = true;
cluster.billboard.id = cluster.label.id;
cluster.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM;
if (clusteredEntities.length >= 100) {
cluster.billboard.image = pin100;
} else if (clusteredEntities.length >= 50) {
cluster.billboard.image = pin50;
} else if (clusteredEntities.length >= 40) {
cluster.billboard.image = pin40;
} else if (clusteredEntities.length >= 30) {
cluster.billboard.image = pin30;
} else if (clusteredEntities.length >= 20) {
cluster.billboard.image = pin20;
} else if (clusteredEntities.length >= 10) {
cluster.billboard.image = pin10;
} else {
cluster.billboard.image =
singleDigitPins[clusteredEntities.length - 2];
}
}
);
}
// force a re-cluster with the new styling
var pixelRange = dataSource.clustering.pixelRange;
dataSource.clustering.pixelRange = 0;
dataSource.clustering.pixelRange = pixelRange;
}
}