cesium实现聚合点功能,cesium沙盒也有聚合样例。聚合时的图标是可以自定义样式的,使用CustomDataSource添加所有需要聚合显示的点数据;在clustering.clusterEvent事件中更改每次缩放时的聚合点的样式,自定义聚合图标的样式可以采用canvas画图去绘制自己想要的样式,使用cluster.billboard.image接收canvas绘制的图形更改样式即可,不使用canvas也可以找一些现有图标。代码基于vue2,ClusterLayer类可以直接使用。
<template>
<div class="mycontainer">
<Map @Viewer="getViewer"></Map>
</div>
</template>
<script>
import Map from '../map.vue'
import Position from '../../../utilsCesium/position/Position'
import ClusterLayer from '../../../utilsCesium/layer/ClusterLayer'
const Cesium = require('cesium/Cesium')
export default {
components: { Map },
data () {
return {
viewer: null
}
},
mounted () {
const layer = new ClusterLayer(this.viewer)
const positions = this.generatePosition(1000)
let id = 0
positions.forEach(item => {
id = id + 1
layer._delegate.entities.add({
position: Cesium.Cartesian3.fromDegrees(item._lng, item._lat),
billboard: {
id: id,
show: true, // default
image: 'data/imageData/camera.png',
height: 32,
// 宽度(以像素为单位)
width: 32
}
})
})
this.viewer.flyTo(layer._delegate)
},
watch: {
},
methods: {
getViewer (view) {
this.viewer = view
},
generatePosition (num) {
var list = []
for (var i = 0; i < num; i++) {
var lng = 120.38105869 + Math.random() * 0.5
var lat = 31.10115627 + Math.random() * 0.5
list.push(new Position(lng, lat))
}
return list
}
}
}
聚合类ClusterLayer代码:
let that
const Cesium = require('cesium/Cesium')
const DEF_OPT = {
size: 18,
pixelRange: 40,
gradient: {
0.0001: Cesium.Color.DEEPSKYBLUE,
0.001: Cesium.Color.GREEN,
0.01: Cesium.Color.ORANGE,
0.1: Cesium.Color.RED
},
fontSize: 12,
fontColor: Cesium.Color.BLACK,
style: 'circle'
}
class ClusterLayer {
constructor (viewer) {
this._delegate = new Cesium.CustomDataSource()
this._options = {
...DEF_OPT
}
this._delegate.clustering.enabled = true
this._delegate.clustering.clusterEvent.addEventListener(
this._clusterEventHandler
)
this._delegate.clustering.pixelRange = this._options.pixelRange
that = this
this._viewer = viewer
if (this._delegate instanceof Cesium.PrimitiveCollection) {
this._viewer.scene.primitives.add(this._delegate)
} else {
this._viewer.dataSources.add(this._delegate)
}
}
set enableCluster (enableCluster) {
this._delegate.clustering.enabled = enableCluster
return this
}
/**
*
* @param color
* @param numLength
* @returns {*}
* @private
*/
_drawCircle (color, numLength) {
const size = this._options.size * (numLength + 1)
const canvas = document.createElement('canvas')
canvas.width = size
canvas.height = size
const context2D = canvas.getContext('2d')
context2D.save()
context2D.scale(size / 24, size / 24) // Added to auto-generated code to scale up to desired size.
context2D.fillStyle = color.withAlpha(0.2).toCssColorString() // Modified from auto-generated code.
context2D.beginPath()
context2D.arc(12, 12, 9, 0, 2 * Math.PI)
context2D.closePath()
context2D.fill()
context2D.beginPath()
context2D.arc(12, 12, 6, 0, 2 * Math.PI)
context2D.fillStyle = color.toCssColorString()
context2D.fill()
context2D.closePath()
context2D.restore()
return canvas.toDataURL()
}
/**
*
* @param color
* @param numLength
* @returns {*}
* @private
*/
_drawClustering (color, numLength) {
const size = this._options.size * (numLength + 1)
let startAngle = -Math.PI / 12
const angle = Math.PI / 2
const intervalAngle = Math.PI / 6
const canvas = document.createElement('canvas')
canvas.width = size
canvas.height = size
const context2D = canvas.getContext('2d')
context2D.save()
context2D.scale(size / 24, size / 24) // Added to auto-generated code to scale up to desired size.
context2D.beginPath()
context2D.arc(12, 12, 6, 0, 2 * Math.PI)
context2D.fillStyle = color.toCssColorString()
context2D.fill()
context2D.closePath()
context2D.lineWidth = 2
for (let i = 0; i < 3; i++) {
context2D.beginPath()
context2D.arc(12, 12, 8, startAngle, startAngle + angle, false)
context2D.strokeStyle = color.withAlpha(0.4).toCssColorString()
context2D.stroke()
context2D.arc(12, 12, 11, startAngle, startAngle + angle, false)
context2D.strokeStyle = color.withAlpha(0.2).toCssColorString()
context2D.stroke()
context2D.closePath()
startAngle = startAngle + angle + intervalAngle
}
context2D.restore()
return canvas.toDataURL()
}
/**
*
* @param {*} clusteredEntities
* @param {*} cluster
*/
_clusterEventHandler (clusteredEntities, cluster) {
if (!that._delegate.clustering.enabled) {
return
}
cluster.billboard.show = true
cluster.label.font = `bold ${that._options.fontSize}px sans-serif`
cluster.label.fillColor = that._options.fontColor
cluster.label.disableDepthTestDistance = Number.POSITIVE_INFINITY
if (that._delegate.entities.values.length) {
const allCount = that._delegate.entities.values.length || 0
for (const key in that._options.gradient) {
if (clusteredEntities.length >= allCount * key) {
const numLength = String(clusteredEntities.length).length
if (that._options.style === 'circle') {
cluster.billboard.image = that._drawCircle(
that._options.gradient[key],
numLength
)
} else {
cluster.billboard.image = that._drawClustering(
that._options.gradient[key],
numLength
)
}
cluster.label.show = true
if (numLength === 1) {
cluster.label.pixelOffset = new Cesium.Cartesian2(-2, 3)
} else {
cluster.label.pixelOffset = new Cesium.Cartesian2(
-5 * (numLength - 1),
5
)
}
} else if (clusteredEntities.length <= 1) {
cluster.label.show = false
}
}
}
}
clear () {
this._delegate.entities.removeAll()
return this
}
}
export default ClusterLayer