cesium 实战005 --使用primitive实现聚合效果,解决大量点页面崩溃的情况

在Cesium官方提供的沙盒实例中可以找到使用entity.add()接口添加三种数据的方法,但是在实际使用过程中由于数据量过大会出现数据加载耗时较长、页面卡顿等情况。于是使用primitive添加并利用原有的clustering 实现聚合

在这里插入图片描述

primitivecluster 源码

import KDBush from "kdbush";

/**
 * 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;
}

const labelBoundingBoxScratch = new Cesium.BoundingRectangle();

function getBoundingBox(item, coord, pixelRange, entityCluster, result) {
  if (Cesium.defined(item._labelCollection) && entityCluster._clusterLabels) {
    result = Cesium.Label.getScreenSpaceBoundingBox(item, coord, result);
  } else if (
    Cesium.defined(item._billboardCollection) &&
    entityCluster._clusterBillboards
  ) {
    result = Cesium.Billboard.getScreenSpaceBoundingBox(item, coord, result);
  } else if (
    Cesium.defined(item._pointPrimitiveCollection) &&
    entityCluster._clusterPoints
  ) {
    result = Cesium.PointPrimitive.getScreenSpaceBoundingBox(item, coord, result);
  }

  expandBoundingBox(result, pixelRange);

  if (
    entityCluster._clusterLabels &&
    !Cesium.defined(item._labelCollection) &&
    Cesium.defined(item.id) &&
    hasLabelIndex(entityCluster, item.id.id) &&
    Cesium.defined(item.id._label)
  ) {
    const labelIndex =
      entityCluster._collectionIndicesByEntity[item.id.id].labelIndex;
    const label = entityCluster._labelCollection.get(labelIndex);
    const labelBBox = Cesium.Label.getScreenSpaceBoundingBox(
      label,
      coord,
      labelBoundingBoxScratch
    );
    expandBoundingBox(labelBBox, pixelRange);
    result = Cesium.BoundingRectangle.union(result, labelBBox, result);
  }

  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)
  ) {
    const labelIndex =
      entityCluster._collectionIndicesByEntity[item.id.id].labelIndex;
    const label = entityCluster._labelCollection.get(labelIndex);
    label.clusterShow = true;
  }
}

function addCluster(position, numPoints, ids, entityCluster) {
  const cluster = {
    billboard: entityCluster._clusterBillboardCollection.add(),
    label: entityCluster._clusterLabelCollection.add(),
    point: entityCluster._clusterPointCollection.add(),
  };

  cluster.billboard.show = false;
  cluster.point.show = false;
  cluster.label.show = true;
  cluster.label.text = numPoints.toLocaleString();
  cluster.label.id = ids;
  cluster.billboard.position = cluster.label.position = cluster.point.position = position;

  entityCluster._clusterEvent.raiseEvent(ids, cluster);
}

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;
  }

  const length = collection.length;
  for (let i = 0; i < length; ++i) {
    const item = collection.get(i);
    item.clusterShow = false;

    if (
      !item.show ||
      (entityCluster._scene.mode === Cesium.SceneMode.SCENE3D &&
        !occluder.isPointVisible(item.position))
    ) {
      continue;
    }

    // const canClusterLabels =
    //   entityCluster._clusterLabels && Cesium.defined(item._labelCollection);
    // const canClusterBillboards =
    //   entityCluster._clusterBillboards && Cesium.defined(item.id._billboard);
    // const canClusterPoints =
    //   entityCluster._clusterPoints && Cesium.defined(item.id._point);
    // if (canClusterLabels && (canClusterPoints || canClusterBillboards)) {
    //   continue;
    // }

    const coord = item.computeScreenSpacePosition(scene);
    if (!Cesium.defined(coord)) {
      continue;
    }

    points.push({
      index: i,
      collection: collection,
      clustered: false,
      coord: coord,
    });
  }
}

const pointBoundinRectangleScratch = new Cesium.BoundingRectangle();
const totalBoundingRectangleScratch = new Cesium.BoundingRectangle();
const neighborBoundingRectangleScratch = new Cesium.BoundingRectangle();

function createDeclutterCallback(entityCluster) {
  return function (amount) {
    if ((Cesium.defined(amount) && amount < 0.05) || !entityCluster.enabled) {
      return;
    }

    const scene = entityCluster._scene;

    const labelCollection = entityCluster._labelCollection;
    const billboardCollection = entityCluster._billboardCollection;
    const pointCollection = entityCluster._pointCollection;

    if (
      (!Cesium.defined(labelCollection) &&
        !Cesium.defined(billboardCollection) &&
        !Cesium.defined(pointCollection)) ||
      (!entityCluster._clusterBillboards &&
        !entityCluster._clusterLabels &&
        !entityCluster._clusterPoints)
    ) {
      return;
    }

    let clusteredLabelCollection = entityCluster._clusterLabelCollection;
    let clusteredBillboardCollection =
      entityCluster._clusterBillboardCollection;
    let 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,
        }
      );
    }

    if (Cesium.defined(clusteredPointCollection)) {
      clusteredPointCollection.removeAll();
    } else {
      clusteredPointCollection = entityCluster._clusterPointCollection = new Cesium.PointPrimitiveCollection();
    }

    const pixelRange = entityCluster._pixelRange;
    const minimumClusterSize = entityCluster._minimumClusterSize;

    const clusters = entityCluster._previousClusters;
    const newClusters = [];

    const previousHeight = entityCluster._previousHeight;
    const currentHeight = scene.camera.positionCartographic.height;

    const ellipsoid = scene.mapProjection.ellipsoid;
    const cameraPosition = scene.camera.positionWC;
    const occluder = new Cesium.EllipsoidalOccluder(ellipsoid, cameraPosition);

    const points = [];
    if (entityCluster._clusterLabels) {
      getScreenSpacePositions(
        labelCollection,
        points,
        scene,
        occluder,
        entityCluster
      );
    }
    if (entityCluster._clusterBillboards) {
      getScreenSpacePositions(
        billboardCollection,
        points,
        scene,
        occluder,
        entityCluster
      );
    }
    if (entityCluster._clusterPoints) {
      getScreenSpacePositions(
        pointCollection,
        points,
        scene,
        occluder,
        entityCluster
      );
    }

    let i;
    let j;
    let length;
    let bbox;
    let neighbors;
    let neighborLength;
    let neighborIndex;
    let neighborPoint;
    let ids;
    let numPoints;

    let collection;
    let collectionIndex;

    const index = new KDBush(points, getX, getY, 64, Int32Array);
    console.log(index)
    if (currentHeight < previousHeight) {
      length = clusters.length;
      for (i = 0; i < length; ++i) {
        const cluster = clusters[i];

        if (!occluder.isPointVisible(cluster.position)) {
          continue;
        }

        const coord = Cesium.Billboard._computeScreenSpacePosition(
          Cesium.Matrix4.IDENTITY,
          cluster.position,
          Cesium.Cartesian3.ZERO,
          Cesium.Cartesian2.ZERO,
          scene
        );
        if (!Cesium.defined(coord)) {
          continue;
        }

        const factor = 1.0 - currentHeight / previousHeight;
        let width = (cluster.width = cluster.width * factor);
        let height = (cluster.height = cluster.height * factor);

        width = Math.max(width, cluster.minimumWidth);
        height = Math.max(height, cluster.minimumHeight);

        const minX = coord.x - width * 0.5;
        const minY = coord.y - height * 0.5;
        const maxX = coord.x + width;
        const 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) {
      const point = points[i];
      if (point.clustered) {
        continue;
      }

      point.clustered = true;

      collection = point.collection;
      collectionIndex = point.index;

      const item = collection.get(collectionIndex);
      bbox = getBoundingBox(
        item,
        point.coord,
        pixelRange,
        entityCluster,
        pointBoundinRectangleScratch
      );
      const totalBBox = Cesium.BoundingRectangle.clone(
        bbox,
        totalBoundingRectangleScratch
      );

      neighbors = index.range(
        bbox.x,
        bbox.y,
        bbox.x + bbox.width,
        bbox.y + bbox.height
      );
      neighborLength = neighbors.length;

      const 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) {
          const neighborItem = neighborPoint.collection.get(
            neighborPoint.index
          );
          const 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.id);
        }
      }

      if (numPoints >= minimumClusterSize) {
        const 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 (clusteredLabelCollection.length === 0) {
      clusteredLabelCollection.destroy();
      entityCluster._clusterLabelCollection = undefined;
    }

    if (clusteredBillboardCollection.length === 0) {
      clusteredBillboardCollection.destroy();
      entityCluster._clusterBillboardCollection = undefined;
    }

    if (clusteredPointCollection.length === 0) {
      clusteredPointCollection.destroy();
      entityCluster._clusterPointCollection = undefined;
    }

    entityCluster._previousClusters = newClusters;
    entityCluster._previousHeight = currentHeight;
  };
}

PrimitiveCluster.prototype._initialize = function (scene) {
  this._scene = scene;

  const cluster = createDeclutterCallback(this);
  this._cluster = cluster;
  this._removeEventListener = scene.camera.changed.addEventListener(cluster);
};

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 {Cesium.Event<PrimitiveCluster.newClusterCallback>}
   */
  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) {
    let collection = this[collectionProperty];

    if (!Cesium.defined(this._collectionIndicesByEntity)) {
      this._collectionIndicesByEntity = {};
    }

    let 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,
      });
    }

    let index;
    let entityItem;

    const unusedIndices = this[unusedIndicesProperty];
    if (unusedIndices.length > 0) {
      index = unusedIndices.pop();
      entityItem = collection.get(index);
    } else {
      entityItem = collection.add();
      index = collection.length - 1;
    }

    entityIndices[entityIndexProperty] = index;

    const that = this;
    Promise.resolve().then(function () {
      that._clusterDirty = true;
    });

    return entityItem;
  };
}

function removeEntityIndicesIfUnused(entityCluster, entityId) {
  const 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 Cesium.Label}.
 * @param {Entity} entity The entity that will use the returned {@link Cesium.Label} for visualization.
 * @returns {Cesium.Label} The label that will be used to visualize an entity.
 *
 * @private
 */
PrimitiveCluster.prototype.getLabel = createGetEntity(
  "_labelCollection",
  Cesium.LabelCollection,
  "_unusedLabelIndices",
  "labelIndex"
);

/**
 * Removes the {@link Cesium.Label} associated with an entity so it can be reused by another entity.
 * @param {Entity} entity The entity that will uses the returned {@link Cesium.Label} for visualization.
 *
 * @private
 */
PrimitiveCluster.prototype.removeLabel = function (entity) {
  const entityIndices =
    this._collectionIndicesByEntity &&
    this._collectionIndicesByEntity[entity.id];
  if (
    !Cesium.defined(this._labelCollection) ||
    !Cesium.defined(entityIndices) ||
    !Cesium.defined(entityIndices.labelIndex)
  ) {
    return;
  }

  const index = entityIndices.labelIndex;
  entityIndices.labelIndex = undefined;
  removeEntityIndicesIfUnused(this, entity.id);

  const label = this._labelCollection.get(index);
  label.show = false;
  label.text = "";
  label.id = undefined;

  this._unusedLabelIndices.push(index);

  this._clusterDirty = true;
};

/**
 * Returns a new {@link Cesium.Billboard}.
 * @param {Entity} entity The entity that will use the returned {@link Cesium.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 Cesium.Billboard} associated with an entity so it can be reused by another entity.
 * @param {Entity} entity The entity that will uses the returned {@link Cesium.Billboard} for visualization.
 *
 * @private
 */
PrimitiveCluster.prototype.removeBillboard = function (entity) {
  const entityIndices =
    this._collectionIndicesByEntity &&
    this._collectionIndicesByEntity[entity.id];
  if (
    !Cesium.defined(this._billboardCollection) ||
    !Cesium.defined(entityIndices) ||
    !Cesium.defined(entityIndices.billboardIndex)
  ) {
    return;
  }

  const index = entityIndices.billboardIndex;
  entityIndices.billboardIndex = undefined;
  removeEntityIndicesIfUnused(this, entity.id);

  const 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) {
  const entityIndices =
    this._collectionIndicesByEntity &&
    this._collectionIndicesByEntity[entity.id];
  if (
    !Cesium.defined(this._pointCollection) ||
    !Cesium.defined(entityIndices) ||
    !Cesium.defined(entityIndices.pointIndex)
  ) {
    return;
  }

  const index = entityIndices.pointIndex;
  entityIndices.pointIndex = undefined;
  removeEntityIndicesIfUnused(this, entity.id);

  const 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;
  }

  const length = collection.length;
  for (let 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 clustering is enabled before the label collection is updated,
  // the glyphs haven't been created so the screen space bounding boxes
  // are incorrect.
  let commandList;
  if (
    Cesium.defined(this._labelCollection) &&
    this._labelCollection.length > 0 &&
    this._labelCollection.get(0)._glyphs.length === 0
  ) {
    commandList = frameState.commandList;
    frameState.commandList = [];
    this._labelCollection.update(frameState);
    frameState.commandList = commandList;
  }

  // If clustering is enabled before the billboard collection is updated,
  // the images haven't been added to the image atlas so the screen space bounding boxes
  // are incorrect.
  if (
    Cesium.defined(this._billboardCollection) &&
    this._billboardCollection.length > 0 &&
    !Cesium.defined(this._billboardCollection.get(0).width)
  ) {
    commandList = frameState.commandList;
    frameState.commandList = [];
    this._billboardCollection.update(frameState);
    frameState.commandList = commandList;
  }

  if (this._enabledDirty) {
    this._enabledDirty = false;
    updateEnable(this);
    this._clusterDirty = true;
  }

  if (this._clusterDirty) {
    this._clusterDirty = false;
    this._cluster();
  }

  if (Cesium.defined(this._clusterLabelCollection)) {
    this._clusterLabelCollection.update(frameState);
  }
  if (Cesium.defined(this._clusterBillboardCollection)) {
    this._clusterBillboardCollection.update(frameState);
  }
  if (Cesium.defined(this._clusterPointCollection)) {
    this._clusterPointCollection.update(frameState);
  }

  if (Cesium.defined(this._labelCollection)) {
    this._labelCollection.update(frameState);
  }
  if (Cesium.defined(this._billboardCollection)) {
    this._billboardCollection.update(frameState);
  }
  if (Cesium.defined(this._pointCollection)) {
    this._pointCollection.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 the Billboard, Label, and Point
 * primitives that represent this cluster of entities.
 * @param {Cesium.Billboard} cluster.billboard
 * @param {Cesium.Label} cluster.label
 * @param {Cesium.PointPrimitive} cluster.point
 *
 * @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 "@/utils/cesiumCtrl/primitiveCluster"

使用

    // primitive 实现点位聚合(有图标,没有名称)
    addCluster(data) {
      let that = this
      // 使用primitives 添加点
      var labels = new Cesium.LabelCollection()
      var billboards = new Cesium.BillboardCollection()
      var collection = new Cesium.PrimitiveCollection()

      data.forEach(ele => {
        let center = {
          lng: ele.longitude,
          lat: ele.latitude,
        }
        let title = {
          id:  ele.id,
          position: Cesium.Cartesian3.fromDegrees(Number(center.lng), Number(center.lat), 0),
          text: ele.name,
          font: "30px Source Han Sans CN", //字体样式
          fillColor: new Cesium.Color.fromCssColorString("#ffffff"), //字体颜色
          showBackground: true, //是否显示背景颜色
          backgroundColor: new Cesium.Color.fromCssColorString("#000000"), //背景颜色
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM, //垂直位置
          horizontalOrigin: Cesium.HorizontalOrigin.CENTER, //水平位置
        }
        let img = {
          id: ele.id,
          position: Cesium.Cartesian3.fromDegrees(Number(center.lng), Number(center.lat), 80),
          image: ele.url.slice(ele.url.indexOf("/xgcsdn")),
          width: ele.poiWid,
          height: ele.poiHig,
          scale: 1,
          verticalOrigin: Cesium.VerticalOrigin.TOP, //垂直位置
        }
        labels.add(title)
        billboards.add(img)
      })
      let primitivecluster = null
      primitivecluster = new PrimitiveCluster()
      primitivecluster.enabled = true
      primitivecluster.pixelRange = 1
      primitivecluster.minimumClusterSize = 10
      primitivecluster._billboardCollection = billboards
      // 同时在赋值时调用_initialize方法
      primitivecluster._initialize(that.webGlobe.scene)
      collection.add(primitivecluster)
      this.primitivesCluster = that.webGlobe.scene.primitives.add(collection)
      const pinBuilder = new Cesium.PinBuilder()
      primitivecluster.clusterEvent.addEventListener((clusteredEntities, cluster) => {
        // 关闭自带的显示聚合数量的标签
        cluster.label.show = false
        cluster.billboard.show = true
        cluster.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM
        let pinImg = pinBuilder.fromText(cluster.label.text, Cesium.Color.RED, 60).toDataURL()
        // 根据聚合数量的多少设置不同层级的图片以及大小
        cluster.billboard.image = pinImg
      })
      return primitivecluster
    },

目前有点小问题的是,聚合的数字有时算的不准确,需要小伙伴帮忙指正一下

  • 12
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值