#原因
目前涉及在三维上叠加大量点位数据,考虑到使用场景不一样,同时结合以前Arcgis与Mapboxgl使用习惯顺便写了两个类
#思考
一个GEOJSON为一个图层(GrahicLayer),同时改造Cesium的聚合类(PrimitiveCluster,仅对点作了处理),实现放大小缩小时自动聚合显示提高流畅度,因为本人目前使用的每个图层点位不超过10万,因此没有对数据进行处理切割处理。
/**
* Defines how screen space objects (billboards, points, labels) are clustered.
*
* @param {Object} [options] An object with the following properties:
* @param {Boolean} [options.enabled=false] Whether or not to enable clustering.
* @param {Number} [options.pixelRange=80] The pixel range to extend the screen space bounding box.
* @param {Number} [options.minimumClusterSize=2] The minimum number of screen space objects that can be clustered.
* @param {Boolean} [options.clusterBillboards=true] Whether or not to cluster the billboards of an entity.
* @param {Boolean} [options.clusterLabels=true] Whether or not to cluster the labels of an entity.
* @param {Boolean} [options.clusterPoints=true] Whether or not to cluster the points of an entity.
* @param {Boolean} [options.show=true] Determines if the entities in the cluster will be shown.
*
* @alias PrimitiveCluster
* @constructor
*
* @demo {@link https://sandcastle.cesium.com/index.html?src=Clustering.html|Cesium Sandcastle Clustering Demo}
*/
function PrimitiveCluster(options) {
options = Cesium.defaultValue(options, Cesium.defaultValue.EMPTY_OBJECT);
this._enabled = Cesium.defaultValue(options.enabled, false);
this._pixelRange = Cesium.defaultValue(options.pixelRange, 80);
this._minimumClusterSize = Cesium.defaultValue(options.minimumClusterSize, 2);
this._clusterBillboards = Cesium.defaultValue(options.clusterBillboards, true);
this._clusterLabels = Cesium.defaultValue(options.clusterLabels, true);
this._clusterPoints = Cesium.defaultValue(options.clusterPoints, true);
this._labelCollection = undefined;
this._billboardCollection = undefined;
this._pointCollection = undefined;
this._clusterBillboardCollection = undefined;
this._clusterLabelCollection = undefined;
this._clusterPointCollection = undefined;
this._collectionIndicesByEntity = {};
this._unusedLabelIndices = [];
this._unusedBillboardIndices = [];
this._unusedPointIndices = [];
this._previousClusters = [];
this._previousHeight = undefined;
this._enabledDirty = false;
this._clusterDirty = false;
this._cluster = undefined;
this._removeEventListener = undefined;
this._clusterEvent = new Cesium.Event();
/**
* Determines if entities in this collection will be shown.
*
* @type {Boolean}
* @default true
*/
this.show = Cesium.defaultValue(options.show, true);
}
function getX(point) {
return point.coord.x;
}
function getY(point) {
return point.coord.y;
}
function expandBoundingBox(bbox, pixelRange) {
bbox.x -= pixelRange;
bbox.y -= pixelRange;
bbox.width += pixelRange * 2.0;
bbox.height += pixelRange * 2.0;
}
var labelBoundingBoxScratch = new Cesium.BoundingRectangle();
function getBoundingBox(item, coord, pixelRange, entityCluster, result) {
if (item &&
Cesium.defined(item._billboardCollection) &&
entityCluster._clusterBillboards
) {
result = Cesium.Billboard.getScreenSpaceBoundingBox(item, coord, result);
}
expandBoundingBox(result, pixelRange);
return result;
}
function addNonClusteredItem(item, entityCluster) {
item.clusterShow = true;
if (!Cesium.defined(item._labelCollection) &&
Cesium.defined(item.id) &&
hasLabelIndex(entityCluster, item.id.id) &&
Cesium.defined(item.id._label)
) {
var labelIndex =
entityCluster._collectionIndicesByEntity[item.id.id].labelIndex;
var label = entityCluster._labelCollection.get(labelIndex);
label.clusterShow = true;
}
}
function addCluster(position, numPoints, ids, entityCluster) {
var cluster = {
billboard: entityCluster._clusterBillboardCollection.add(),
label: entityCluster._clusterLabelCollection.add(),
// point: entityCluster._clusterPointCollection.add(),
};
cluster.billboard.show = true;
cluster.billboard.count = numPoints
//cluster.billboard.position = position
cluster.billboard.disableDepthTestDistance = Number.POSITIVE_INFINITY;
cluster.billboard.pixelOffset = new Cesium.Cartesian2(0.0, -20.0)
cluster.billboard.image = entityCluster.bpts.get(0).image
cluster.billboard.height = entityCluster.bpts.get(0).height
cluster.billboard.width = entityCluster.bpts.get(0).width
ids[0].data.ids = ids;
cluster.billboard.id = ids[0];
// cluster.point.show = false;
cluster.label.show = true;
cluster.label.showBackground = true
cluster.label.backgroundColor = new Cesium.Color(1, 1, 1, 0.8)
cluster.label.fillColor = Cesium.Color.GREEN;
cluster.label.font = "12px monospace"
cluster.label.horizontalOrigin = Cesium.HorizontalOrigin.CENTER
cluster.label.verticalOrigin = Cesium.VerticalOrigin.BOTTOM
cluster.label.pixelOffset = new Cesium.Cartesian2(0.0, -40.0)
cluster.label.text = ids[0].data.name;
cluster.label.id = ids[0];
cluster.billboard.position = cluster.label.position = position //= cluster.point.position = position;
}
function hasLabelIndex(entityCluster, entityId) {
return (
Cesium.defined(entityCluster) &&
Cesium.defined(entityCluster._collectionIndicesByEntity[entityId]) &&
Cesium.defined(entityCluster._collectionIndicesByEntity[entityId].labelIndex)
);
}
function getScreenSpacePositions(
collection,
points,
scene,
occluder,
entityCluster
) {
if (!Cesium.defined(collection)) {
return;
}
var length = collection.length;
for (var i = 0; i < length; ++i) {
var item = collection.get(i);
item.clusterShow = false;
if (!item.show ||
(entityCluster._scene.mode === Cesium.SceneMode.SCENE3D &&
!occluder.isPointVisible(item.position))
) {
continue;
}
var canClusterLabels =
entityCluster._clusterLabels && Cesium.defined(item._labelCollection);
var canClusterBillboards =
entityCluster._clusterBillboards && Cesium.defined(item);
var canClusterPoints =
entityCluster._clusterPoints && Cesium.defined(item._point);
if (canClusterLabels && (canClusterPoints || canClusterBillboards)) {
continue;
}
var coord = item.computeScreenSpacePosition(scene);
if (!Cesium.defined(coord)) {
continue;
}
points.push({
index: i,
collection: collection,
clustered: false,
coord: coord,
});
}
}
var pointBoundinRectangleScratch = new Cesium.BoundingRectangle();
var totalBoundingRectangleScratch = new Cesium.BoundingRectangle();
var neighborBoundingRectangleScratch = new Cesium.BoundingRectangle();
function createDeclutterCallback(entityCluster) {
return async function(amount) {
entityCluster._clusterEvent.raiseEvent({});
if (!entityCluster.show) return;
var scene = entityCluster._scene;
var labelCollection = entityCluster._labelCollection;
var billboardCollection = entityCluster._billboardCollection;
var pointCollection = entityCluster._pointCollection;
if (
(!Cesium.defined(labelCollection) &&
!Cesium.defined(billboardCollection) &&
!Cesium.defined(pointCollection)) ||
(!entityCluster._clusterBillboards &&
!entityCluster._clusterLabels &&
!entityCluster._clusterPoints)
) {
return;
}
var clusteredLabelCollection = entityCluster._clusterLabelCollection;
var clusteredBillboardCollection =
entityCluster._clusterBillboardCollection;
// var clusteredPointCollection = entityCluster._clusterPointCollection;
if (Cesium.defined(clusteredLabelCollection)) {
clusteredLabelCollection.removeAll();
} else {
clusteredLabelCollection = entityCluster._clusterLabelCollection = new Cesium.LabelCollection({
scene: scene,
});
}
if (Cesium.defined(clusteredBillboardCollection)) {
clusteredBillboardCollection.removeAll();
} else {
clusteredBillboardCollection = entityCluster._clusterBillboardCollection = new Cesium.BillboardCollection({
scene: scene,
});
}
var pixelRange = entityCluster._pixelRange;
var minimumClusterSize = entityCluster._minimumClusterSize;
var clusters = entityCluster._previousClusters;
var newClusters = [];
var previousHeight = entityCluster._previousHeight;
var currentHeight = scene.camera.positionCartographic.height;
var ellipsoid = scene.mapProjection.ellipsoid;
var cameraPosition = scene.camera.positionWC;
var occluder = new Cesium.EllipsoidalOccluder(ellipsoid, cameraPosition);
var points = [];
if (entityCluster._clusterBillboards) {
getScreenSpacePositions(
billboardCollection,
points,
scene,
occluder,
entityCluster
);
}
var i;
var j;
var length;
var bbox;
var neighbors;
var neighborLength;
var neighborIndex;
var neighborPoint;
var ids;
var numPoints;
var collection;
var collectionIndex;
var index = new Cesium.kdbush(points, getX, getY, 64, Int32Array);
if (currentHeight < previousHeight) {
length = clusters.length;
for (i = 0; i < length; ++i) {
var cluster = clusters[i];
if (!occluder.isPointVisible(cluster.position)) {
continue;
}
var coord = Cesium.Billboard._computeScreenSpacePosition(
Cesium.Matrix4.IDENTITY,
cluster.position,
Cesium.Cartesian3.ZERO,
Cesium.Cartesian2.ZERO,
scene
);
if (!Cesium.defined(coord)) {
continue;
}
var factor = 1.0 - currentHeight / previousHeight;
var width = (cluster.width = cluster.width * factor);
var height = (cluster.height = cluster.height * factor);
width = Math.max(width, cluster.minimumWidth);
height = Math.max(height, cluster.minimumHeight);
var minX = coord.x - width * 0.5;
var minY = coord.y - height * 0.5;
var maxX = coord.x + width;
var maxY = coord.y + height;
neighbors = index.range(minX, minY, maxX, maxY);
neighborLength = neighbors.length;
numPoints = 0;
ids = [];
for (j = 0; j < neighborLength; ++j) {
neighborIndex = neighbors[j];
neighborPoint = points[neighborIndex];
if (!neighborPoint.clustered) {
++numPoints;
collection = neighborPoint.collection;
collectionIndex = neighborPoint.index;
ids.push(collection.get(collectionIndex).id);
}
}
if (numPoints >= minimumClusterSize) {
addCluster(cluster.position, numPoints, ids, entityCluster);
newClusters.push(cluster);
for (j = 0; j < neighborLength; ++j) {
points[neighbors[j]].clustered = true;
}
}
}
}
length = points.length;
for (i = 0; i < length; ++i) {
var point = points[i];
if (point.clustered) {
continue;
}
point.clustered = true;
collection = point.collection;
collectionIndex = point.index;
var item = entityCluster.bpts.get(collectionIndex);
// let _item = entityCluster.bpts.get(collectionIndex);
bbox = getBoundingBox(
item,
point.coord,
pixelRange,
entityCluster,
pointBoundinRectangleScratch
);
var totalBBox = Cesium.BoundingRectangle.clone(
bbox,
totalBoundingRectangleScratch
);
neighbors = index.range(
bbox.x,
bbox.y,
bbox.x + bbox.width,
bbox.y + bbox.height
);
neighborLength = neighbors.length;
var clusterPosition = Cesium.Cartesian3.clone(item.position);
numPoints = 1;
ids = [item.id];
for (j = 0; j < neighborLength; ++j) {
neighborIndex = neighbors[j];
neighborPoint = points[neighborIndex];
if (!neighborPoint.clustered) {
var neighborItem = neighborPoint.collection.get(neighborPoint.index);
// let _neighborItem = entityCluster.bpts.get(neighborPoint.index);
var neighborBBox = getBoundingBox(
neighborItem,
neighborPoint.coord,
pixelRange,
entityCluster,
neighborBoundingRectangleScratch
);
Cesium.Cartesian3.add(
neighborItem.position,
clusterPosition,
clusterPosition
);
Cesium.BoundingRectangle.union(totalBBox, neighborBBox, totalBBox);
++numPoints;
ids.push(neighborItem);
}
}
if (numPoints >= minimumClusterSize) {
var position = Cesium.Cartesian3.multiplyByScalar(
clusterPosition,
1.0 / numPoints,
clusterPosition
);
addCluster(position, numPoints, ids, entityCluster);
newClusters.push({
position: position,
width: totalBBox.width,
height: totalBBox.height,
minimumWidth: bbox.width,
minimumHeight: bbox.height,
});
for (j = 0; j < neighborLength; ++j) {
points[neighbors[j]].clustered = true;
}
} else {
addNonClusteredItem(item, entityCluster);
}
}
if (clusteredBillboardCollection.length === 0) {
clusteredBillboardCollection.destroy();
entityCluster._clusterBillboardCollection = undefined;
}
entityCluster._previousClusters = newClusters;
entityCluster._previousHeight = currentHeight;
entityCluster._clusterEvent.raiseEvent(entityCluster);
};
}
PrimitiveCluster.prototype._initialize = function(scene, minzoom, pts) {
this._scene = scene;
this.minzoom = minzoom
this.bpts = pts
var cluster = createDeclutterCallback(this);
this._cluster = cluster;
this.handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
this.handler.setInputAction((e) => {
cluster();
}, Cesium.ScreenSpaceEventType.WHEEL);
//this._removeEventListener = scene.camera.changed.addEventListener(cluster);
};
PrimitiveCluster.prototype.clear = function() {
if (this._billboardCollection)
this._billboardCollection.removeAll();
this._collectionIndicesByEntity = {}
}
Object.defineProperties(PrimitiveCluster.prototype, {
/**
* Gets or sets whether clustering is enabled.
* @memberof PrimitiveCluster.prototype
* @type {Boolean}
*/
enabled: {
get: function() {
return this._enabled;
},
set: function(value) {
this._enabledDirty = value !== this._enabled;
this._enabled = value;
},
},
/**
* Gets or sets the pixel range to extend the screen space bounding box.
* @memberof PrimitiveCluster.prototype
* @type {Number}
*/
pixelRange: {
get: function() {
return this._pixelRange;
},
set: function(value) {
this._clusterDirty = this._clusterDirty || value !== this._pixelRange;
this._pixelRange = value;
},
},
/**
* Gets or sets the minimum number of screen space objects that can be clustered.
* @memberof PrimitiveCluster.prototype
* @type {Number}
*/
minimumClusterSize: {
get: function() {
return this._minimumClusterSize;
},
set: function(value) {
this._clusterDirty =
this._clusterDirty || value !== this._minimumClusterSize;
this._minimumClusterSize = value;
},
},
/**
* Gets the event that will be raised when a new cluster will be displayed. The signature of the event listener is {@link PrimitiveCluster.newClusterCallback}.
* @memberof PrimitiveCluster.prototype
* @type {Event}
*/
clusterEvent: {
get: function() {
return this._clusterEvent;
},
},
/**
* Gets or sets whether clustering billboard entities is enabled.
* @memberof PrimitiveCluster.prototype
* @type {Boolean}
*/
clusterBillboards: {
get: function() {
return this._clusterBillboards;
},
set: function(value) {
this._clusterDirty =
this._clusterDirty || value !== this._clusterBillboards;
this._clusterBillboards = value;
},
},
/**
* Gets or sets whether clustering labels entities is enabled.
* @memberof PrimitiveCluster.prototype
* @type {Boolean}
*/
clusterLabels: {
get: function() {
return this._clusterLabels;
},
set: function(value) {
this._clusterDirty = this._clusterDirty || value !== this._clusterLabels;
this._clusterLabels = value;
},
},
/**
* Gets or sets whether clustering point entities is enabled.
* @memberof PrimitiveCluster.prototype
* @type {Boolean}
*/
clusterPoints: {
get: function() {
return this._clusterPoints;
},
set: function(value) {
this._clusterDirty = this._clusterDirty || value !== this._clusterPoints;
this._clusterPoints = value;
},
},
});
function createGetEntity(
collectionProperty,
CollectionConstructor,
unusedIndicesProperty,
entityIndexProperty
) {
return function(entity) {
var collection = this[collectionProperty];
if (!Cesium.defined(this._collectionIndicesByEntity)) {
this._collectionIndicesByEntity = {};
}
var entityIndices = this._collectionIndicesByEntity[entity.id];
if (!Cesium.defined(entityIndices)) {
entityIndices = this._collectionIndicesByEntity[entity.id] = {
billboardIndex: undefined,
labelIndex: undefined,
pointIndex: undefined,
};
}
if (Cesium.defined(collection) && Cesium.defined(entityIndices[entityIndexProperty])) {
return collection.get(entityIndices[entityIndexProperty]);
}
if (!Cesium.defined(collection)) {
collection = this[collectionProperty] = new CollectionConstructor({
scene: this._scene,
});
}
var index;
var entityItem;
var unusedIndices = this[unusedIndicesProperty];
if (unusedIndices.length > 0) {
index = unusedIndices.pop();
entityItem = collection.get(index);
} else {
entityItem = collection.add();
// entityItem.point = entity.point;
// entityItem.id = entity.id;
// entityItem.image=entity.image
// entityItem.data = entity.data;
index = collection.length - 1;
}
entityIndices[entityIndexProperty] = index;
this._clusterDirty = true;
return entityItem;
};
}
function removeEntityIndicesIfUnused(entityCluster, entityId) {
var indices = entityCluster._collectionIndicesByEntity[entityId];
if (!Cesium.defined(indices.billboardIndex) &&
!Cesium.defined(indices.labelIndex) &&
!Cesium.defined(indices.pointIndex)
) {
delete entityCluster._collectionIndicesByEntity[entityId];
}
}
/**
* Returns a new {@link Label}.
* @param {Entity} entity The entity that will use the returned {@link Label} for visualization.
* @returns {Label} The label that will be used to visualize an entity.
*
* @private
*/
PrimitiveCluster.prototype.getLabel = createGetEntity(
"_labelCollection",
Cesium.LabelCollection,
"_unusedLabelIndices",
"labelIndex"
);
/**
* Removes the {@link Label} associated with an entity so it can be reused by another entity.
* @param {Entity} entity The entity that will uses the returned {@link Label} for visualization.
*
* @private
*/
PrimitiveCluster.prototype.removeLabel = function(entity) {
var entityIndices =
this._collectionIndicesByEntity &&
this._collectionIndicesByEntity[entity.id];
if (!Cesium.defined(this._labelCollection) ||
!Cesium.defined(entityIndices) ||
!Cesium.defined(entityIndices.labelIndex)
) {
return;
}
var index = entityIndices.labelIndex;
entityIndices.labelIndex = undefined;
removeEntityIndicesIfUnused(this, entity.id);
var label = this._labelCollection.get(index);
label.show = false;
label.text = "";
label.id = undefined;
this._unusedLabelIndices.push(index);
this._clusterDirty = true;
};
/**
* Returns a new {@link Billboard}.
* @param {Entity} entity The entity that will use the returned {@link Billboard} for visualization.
* @returns {Billboard} The label that will be used to visualize an entity.
*
* @private
*/
PrimitiveCluster.prototype.getBillboard = createGetEntity(
"_billboardCollection",
Cesium.BillboardCollection,
"_unusedBillboardIndices",
"billboardIndex"
);
/**
* Removes the {@link Billboard} associated with an entity so it can be reused by another entity.
* @param {Entity} entity The entity that will uses the returned {@link Billboard} for visualization.
*
* @private
*/
PrimitiveCluster.prototype.removeBillboard = function(entity) {
var entityIndices =
this._collectionIndicesByEntity &&
this._collectionIndicesByEntity[entity.id];
if (!Cesium.defined(this._billboardCollection) ||
!Cesium.defined(entityIndices) ||
!Cesium.defined(entityIndices.billboardIndex)
) {
return;
}
var index = entityIndices.billboardIndex;
entityIndices.billboardIndex = undefined;
removeEntityIndicesIfUnused(this, entity.id);
var billboard = this._billboardCollection.get(index);
billboard.id = undefined;
billboard.show = false;
billboard.image = undefined;
this._unusedBillboardIndices.push(index);
this._clusterDirty = true;
};
/**
* Returns a new {@link Point}.
* @param {Entity} entity The entity that will use the returned {@link Point} for visualization.
* @returns {Point} The label that will be used to visualize an entity.
*
* @private
*/
PrimitiveCluster.prototype.getPoint = createGetEntity(
"_pointCollection",
Cesium.PointPrimitiveCollection,
"_unusedPointIndices",
"pointIndex"
);
/**
* Removes the {@link Point} associated with an entity so it can be reused by another entity.
* @param {Entity} entity The entity that will uses the returned {@link Point} for visualization.
*
* @private
*/
PrimitiveCluster.prototype.removePoint = function(entity) {
var entityIndices =
this._collectionIndicesByEntity &&
this._collectionIndicesByEntity[entity.id];
if (!Cesium.defined(this._pointCollection) ||
!Cesium.defined(entityIndices) ||
!Cesium.defined(entityIndices.pointIndex)
) {
return;
}
var index = entityIndices.pointIndex;
entityIndices.pointIndex = undefined;
removeEntityIndicesIfUnused(this, entity.id);
var point = this._pointCollection.get(index);
point.show = false;
point.id = undefined;
this._unusedPointIndices.push(index);
this._clusterDirty = true;
};
function disableCollectionClustering(collection) {
if (!Cesium.defined(collection)) {
return;
}
var length = collection.length;
for (var i = 0; i < length; ++i) {
collection.get(i).clusterShow = true;
}
}
function updateEnable(entityCluster) {
if (entityCluster.enabled) {
return;
}
if (Cesium.defined(entityCluster._clusterLabelCollection)) {
entityCluster._clusterLabelCollection.destroy();
}
if (Cesium.defined(entityCluster._clusterBillboardCollection)) {
entityCluster._clusterBillboardCollection.destroy();
}
if (Cesium.defined(entityCluster._clusterPointCollection)) {
entityCluster._clusterPointCollection.destroy();
}
entityCluster._clusterLabelCollection = undefined;
entityCluster._clusterBillboardCollection = undefined;
entityCluster._clusterPointCollection = undefined;
disableCollectionClustering(entityCluster._labelCollection);
disableCollectionClustering(entityCluster._billboardCollection);
disableCollectionClustering(entityCluster._pointCollection);
}
/**
* Gets the draw commands for the clustered billboards/points/labels if enabled, otherwise,
* queues the draw commands for billboards/points/labels created for entities.
* @private
*/
PrimitiveCluster.prototype.update = function(frameState) {
if (!this.show) {
return;
}
if (this._enabledDirty) {
this._enabledDirty = false;
updateEnable(this);
this._clusterDirty = true;
}
if (this._clusterDirty && this._cluster) {
this._clusterDirty = false;
this._cluster();
}
if (Cesium.defined(this._clusterLabelCollection)) {
this._clusterLabelCollection.update(frameState);
}
if (Cesium.defined(this._clusterBillboardCollection)) {
this._clusterBillboardCollection.update(frameState);
}
};
/**
* Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
* release of WebGL resources, instead of relying on the garbage collector to destroy this object.
* <p>
* Unlike other objects that use WebGL resources, this object can be reused. For example, if a data source is removed
* from a data source collection and added to another.
* </p>
*/
PrimitiveCluster.prototype.destroy = function() {
this._labelCollection =
this._labelCollection && this._labelCollection.destroy();
this._billboardCollection =
this._billboardCollection && this._billboardCollection.destroy();
this._pointCollection =
this._pointCollection && this._pointCollection.destroy();
this._clusterLabelCollection =
this._clusterLabelCollection && this._clusterLabelCollection.destroy();
this._clusterBillboardCollection =
this._clusterBillboardCollection &&
this._clusterBillboardCollection.destroy();
this._clusterPointCollection =
this._clusterPointCollection && this._clusterPointCollection.destroy();
if (Cesium.defined(this._removeEventListener)) {
this._removeEventListener();
this._removeEventListener = undefined;
}
this._labelCollection = undefined;
this._billboardCollection = undefined;
this._pointCollection = undefined;
this._clusterBillboardCollection = undefined;
this._clusterLabelCollection = undefined;
this._clusterPointCollection = undefined;
this._collectionIndicesByEntity = undefined;
this._unusedLabelIndices = [];
this._unusedBillboardIndices = [];
this._unusedPointIndices = [];
this._previousClusters = [];
this._previousHeight = undefined;
this._enabledDirty = false;
this._pixelRangeDirty = false;
this._minimumClusterSizeDirty = false;
return undefined;
};
/**
* A event listener function used to style clusters.
* @callback PrimitiveCluster.newClusterCallback
*
* @param {Entity[]} clusteredEntities An array of the entities contained in the cluster.
* @param {Object} cluster An object containing billboard, label, and point properties. The values are the same as
* billboard, label and point entities, but must be the values of the ConstantProperty.
*
* @example
* // The default cluster values.
* dataSource.clustering.clusterEvent.addEventListener(function(entities, cluster) {
* cluster.label.show = true;
* cluster.label.text = entities.length.toLocaleString();
* });
*/
export default PrimitiveCluster;
import PrimitiveCluster from './PrimitiveCluster.js'
import Util from "./Util.js";
function GrahicLayer() {
this.id = ''
this.name = ''
this.labels = new Cesium.LabelCollection();
this.points = new Cesium.BillboardCollection();
this.clustering = []
this.cluster = false;
this.clusterColor = '#' + ('00000' + (Math.random() * 0x1000000 << 0).toString(16)).substr(-6)
this.polylines = [];
this.polygons = [];
this.map = null
this._show = true
this.showDistance = Number.MAX_VALUE;
this.minzoom = 0;
this.textField = "name"
this.idField = "id"
this.imageField = ""
this.imageHeight = -1;
this.imageWidth = -1
this.autoHeight = true;
this.imageUrl = "images/collect/blue_marker.png"
this.graphics = [];
this.filter = []
this.type = '' //"fill", "line", "symbol", "circle", "heatmap", "fill-extrusion", "raster", "hillshade", "background"
}
GrahicLayer.prototype = {
loadImg: function(url) {
if (this.image) return this.image
return new Promise(async(resolve, reject) => {
var myImage = new Image();
myImage.src = url; //背景图片
myImage.onload = () => {
this.image = myImage
resolve(this.image)
}
});
},
drawImgText: async function(url, number) {
const canvas = document.createElement('canvas') // 不停切换canvas对象
var context = canvas.getContext('2d');
// canvas.width = document.body.clientWidth;//canvas的宽度
canvas.width = 100; //图片的宽度
canvas.height = canvas.width
var swidth = canvas.width
var sheight = canvas.height
context.rect(0, 0, swidth, sheight);
context.fill();
var myImage = await this.loadImg(url)
context.drawImage(myImage, 0, 0, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height);
// 添加文字
context.font = 'bolder 36px Microsoft YaHei';
context.textAlign = 'right';
context.textBaseline = 'bottom';
var left = canvas.width * 0.78;
var top = canvas.height * 0.066;
context.fillStyle = '#fff';
context.fillText(number, left, top);
let result = canvas.toDataURL("image/png");
return result
},
get show() {
return this._show;
},
set show(val) {
this._show = val;
if (!val) {
this.clear()
} else {
this.update()
}
if (this.cluster) {
this.clustering._cluster();
}
},
drawLayer: function(fea) {
let pro = fea.properties;
let geo = fea.geometry
if (!this._show) return;
// if (fea.show) return;
// fea.show = true;
switch (geo.type.toUpperCase()) {
case 'POINT':
{
let pt = Cesium.Cartesian3.fromDegrees(geo.coordinates[0], geo.coordinates[1], geo.coordinates[2])
let img = pro[this.imageField] || this.imageUrl;
let id = pro[this.idField] || Util.getId()
if (this.cluster) {
let _pt = this.clustering.getBillboard({ id, position: pt, image: img, data: pro });
_pt.position = pt
}
let __pt = {
id: { id: id, data: JSON.parse(JSON.stringify(pro)) },
text: pro[this.textField],
position: pt,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
// distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0.0, this.showDistance),
// heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
image: img,
pixelOffset: new Cesium.Cartesian2(0.0, -20.0)
}
if (this.imageWidth != -1) {
__pt.width = this.imageWidth;
}
if (this.imageHeight != -1) {
__pt.height = this.imageHeight
}
this.points.add(__pt)
if (this.textField) {
this.labels.add({
id: { id: id, data: JSON.parse(JSON.stringify(pro)) },
position: pt,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
// distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0.0, this.showDistance),
// heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
showBackground: true,
backgroundColor: new Cesium.Color(1, 1, 1, 0.8),
fillColor: Cesium.Color.GREEN,
font: "12px monospace",
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
text: pro[this.textField],
pixelOffset: new Cesium.Cartesian2(0.0, -40.0)
});
}
}
break;
case 'POLYGON':
{
// const polygonArr = features[i].geometry.coordinates[j].toString().split(',');
for (let i = 0; i < geo.coordinates.length; i++) {
const coor = geo.coordinates[i];
let pts = []
for (let j = 0; j < coor.length; j++) {
const xy = coor[j];
pts.push(...xy)
}
var polygon = Cesium.PolygonGeometry.fromPositions({
positions: Cesium.Cartesian3.fromDegreesArray(pts)
});
const geometry = Cesium.PolygonGeometry.createGeometry(polygon);
this.polygons.push(new Cesium.GeometryInstance({
geometry: geometry,
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({ alpha: 0.7 })),
},
}));
}
}
break;
case 'LINESTRING':
{
const coor = geo.coordinates;
let pts = []
for (let j = 0; j < coor.length; j++) {
const xy = coor[j];
pts.push(...xy)
}
let cpts = Cesium.Cartesian3.fromDegreesArray(pts);
const poline = new Cesium.PolylineGeometry({
positions: cpts,
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
});
const geometry = Cesium.PolylineGeometry.createGeometry(poline);
// geometry.rectangle=Cesium.PolygonGeometry.computeRectangle({polygonHierarchy:new Cesium.PolygonHierarchy(cpts)});
this.polylines.push(new Cesium.GeometryInstance({
geometry: geometry,
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({ alpha: 0.7 })),
},
}));
}
break;
}
},
addGraphic: function(fea) {
let pro = fea.properties;
let _fea = this.graphics.find((f) => f.id == fea.id)
if (!_fea);
this.graphics.push(fea)
this.drawLayer(fea)
},
loadIconDom: async function(url) {
let iconDom = await Cesium.Resource.fetchImage(url)
return iconDom
},
combineNewIcon: function(number, size = 60) {
if (!this.offset) this.offset = 0
const canvas = document.createElement('canvas') // 不停切换canvas对象
canvas.width = size
canvas.height = size
const center_x = canvas.width / 2
const center_y = canvas.height / 2
const step = Math.PI * 0.006
var context = canvas.getContext('2d')
context.clearRect(0, 0, canvas.width, canvas.height)
context.beginPath()
// 填充背景色
context.arc(center_x, center_y, size / 2, 0, 2 * Math.PI)
context.fillStyle = this.clusterColor;
context.fill()
context.strokeStyle = '#1E90FF'
// 画点
context.lineWidth = 2
for (let curr = 0 + this.offset; curr < 2 * Math.PI; curr += Math.PI * 0.06) {
context.beginPath()
context.arc(center_x, center_y, size / 2 - 2, curr, curr + 2 * Math.PI * 0.01)
context.stroke()
context.closePath()
}
context.strokeStyle = '#ffffFF'
for (let curr = 2 * Math.PI - this.offset; curr > 0; curr -= Math.PI * 0.06) {
context.beginPath()
context.arc(center_x, center_y, size / 2 - 4, curr, curr - 2 * Math.PI * 0.01, true)
context.stroke()
context.closePath()
}
// 切换标识,进而切换canvas对象
this.curCanvas = this.curCanvas === 'a' ? 'b' : 'a'
this.offset = this.offset > 0.1 * Math.PI ? 0 : this.offset + step
context.font = "12px Verdana";
// 创建渐变
var gradient = context.createLinearGradient(0, 0, canvas.width, 0);
gradient.addColorStop("0", "magenta");
gradient.addColorStop("0.5", "blue");
gradient.addColorStop("1.0", "red");
// 用渐变填色
context.fillStyle = gradient;
context.textAlign = 'center'
context.textBaseline = "middle";
context.fillText(number, center_x, center_y);
let result = canvas.toDataURL("image/png");
return result
},
setSource: function(features) {
this.clear();
this.graphics.length = 0;
let positions = [];
for (let i = 0; i < features.length; i++) {
let fea = features[i];
let geo = fea.geometry
if (geo.type.toUpperCase() !== 'POINT' || !this.autoHeight)
this.addGraphic(fea)
else {
let p = Cesium.Cartographic.fromDegrees(geo.coordinates[0], geo.coordinates[1])
positions.push(p);
}
}
if (this.type !== 'symbol' || !this.autoHeight)
return;
var promise = Cesium.sampleTerrainMostDetailed(jagis.viewMap.terrainProvider, positions);
Cesium.when(promise, (upts) => {
this.autoHeight = false;
this.points.show = false;
this.labels.show = false;
for (var i = 0; i < upts.length; i++) {
var y = Cesium.Math.toDegrees(upts[i].latitude);
var x = Cesium.Math.toDegrees(upts[i].longitude);
let fea = features[i];
fea.geometry.coordinates.push(upts[i].height)
if (!fea.id) fea.id = i;
if (!fea.properties.name) fea.properties.name = this.id + "_" + i
this.addGraphic(fea)
}
this.points.show = this.show;
this.labels.show = this.show;
if (this.cluster) {
this.clustering._cluster();
}
});
},
update: function() {
if (!this.map) return;
for (let i = 0; i < this.graphics.length; i++) {
const fea = this.graphics[i];
this.drawLayer(fea)
}
const polygon = new Cesium.Primitive({
geometryInstances: this.polygons,
classificationType: Cesium.ClassificationType.TERRAIN
});
this.polygonP = this.map.scene.primitives.add(polygon);
const line = new Cesium.Primitive({
geometryInstances: this.polylines,
appearance: new Cesium.PerInstanceColorAppearance({ // 为每个instance着色
translucent: true,
closed: false
}),
});
this.lineP = this.map.scene.primitives.add(line);
},
clearLineAndPolygon: function() {
if (this.polygonP) {
this.map.scene.primitives.remove(this.polygonP)
this.polygonP.destroy();
this.polygonP = null;
}
if (this.lineP) {
this.map.scene.primitives.remove(this.lineP)
this.lineP.destroy();
this.lineP = null;
}
},
clear: function() {
this.clearLineAndPolygon();
this.polylines.length = 0
this.polygons.length = 0
this.points.removeAll();
this.labels.removeAll();
if (this.cluster) {
this.clustering.clear();
}
// this.update()
},
getFeatureById(id) {
return this.graphics.find(item => item.id === id)
},
removeByid(id) {
//this.graphics.filfil(item => item.id !== id)
var index = this.graphics.findIndex(item => item.id === id)
if (index > -1) {
this.graphics.splice(index, 1)
}
this.update()
},
//视角高度 cesiun知识点
get cameraHeight() {
if (this.map) {
return Math.ceil(this.map.camera.positionCartographic.height)
}
},
addToMap: function(map, scale) {
this.scale = scale
this.map = map
let _level = this.map.scene.globe._surface._debug.maxDepthVisited + 1;
let show = true;
if (this.minzoom > 0 && this.minzoom < _level)
show = false;
this.labels = new Cesium.LabelCollection({ scene: this.map.scene, show: show });
this.points = new Cesium.BillboardCollection({ scene: this.map.scene, show: show });
this.pointsP = this.map.scene.primitives.add(this.points)
// this.pointsP.show = false;
this.labelsP = this.map.scene.primitives.add(this.labels)
if (this.cluster) {
this.clustering = new PrimitiveCluster({ enabled: true })
this.clustering._initialize(this.map.scene, this.minzoom, this.points, this.labels);
this.clusterP = this.map.scene.primitives.add(this.clustering)
this.clustering.clusterEvent.addEventListener((cluster) => {
let _level = this.map.scene.globe._surface._debug.maxDepthVisited + 1;
if (_level >= this.minzoom) {
this.points.show = true;
this.labels.show = true;
this.clusterP.show = false;
} else {
this.points.show = false;
this.labels.show = false;
this.clusterP.show = true;
}
});
}
},
}
export default GrahicLayer
本文探讨了在三维场景中叠加大量点位数据的需求,介绍了如何利用Cesium创建GEOJSON图层,并改造Cesium的PrimitiveCluster类以实现动态聚合效果,提升视图流畅性。尽管每个图层点位数量不超过10万,但未进行数据切割处理。
327

被折叠的 条评论
为什么被折叠?



