詳解cesium實現大批量POI點位聚合渲染優化方案

前言

cesium目前隻提供瞭entityCluster這個聚合類,使打點聚合更方便快捷的實現,但是一般在真正做項目的時候,大傢會經常碰到成千上萬個甚至幾十萬個點位需要聚合打點,那這時候你如果還是用entity去實現的話,怕是要被用戶按在地上瘋狂摩擦,摩擦。。。😅

思考

我們可以通過模擬entityCluster這個類的實現方式,利用源碼中的算法,改成primitive的實現方式;

開發

拉下cesium的源碼,搜EntityCluster關鍵字,我們可以找到EntityCluster.js這個文件,那麼這個代碼就是實現聚合的核心邏輯,接下來我們可以復制一份出來,將EntityCluster全部改為PrimitiveCluster,接著getScreenSpacePositions這個方法裡將entity的邏輯刪除,否則會因為item.id為entity對象為空導致報錯

function getScreenSpacePositions(
  collection,
  points,
  scene,
  occluder,
  entityCluster
) {
  if (!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 === SceneMode.SCENE3D &&
        !occluder.isPointVisible(item.position))
    ) {
      continue;
    }
    // const canClusterLabels =
    //   entityCluster._clusterLabels && defined(item._labelCollection);
    // const canClusterBillboards =
    //   entityCluster._clusterBillboards && defined(item.id._billboard);
    // const canClusterPoints =
    //   entityCluster._clusterPoints && defined(item.id._point);
    // if (canClusterLabels && (canClusterPoints || canClusterBillboards)) {
    //   continue;
    // }
    const coord = item.computeScreenSpacePosition(scene);
    if (!defined(coord)) {
      continue;
    }
    points.push({
      index: i,
      collection: collection,
      clustered: false,
      coord: coord,
    });
  }
}

好瞭,源碼大體就是改這麼多瞭,接下來就是怎麼用;

使用

import PrimitiveCluster from "@/utils/cesiumCtrl/primitiveCluster";
// 初始化標簽實例
const billboardsCollectionCombine = new Cesium.BillboardCollection();
// 初始化實體
const primitives = viewer.scene.primitives.add(
  new Cesium.PrimitiveCollection()
);
getGeojson("/json/schools.geojson").then(({ res }) => {
    // 先獲取點位數據
    console.log(res);
    const { features } = res;
    formatClusterPoint(features);
  });
// 整理聚合數據
const formatClusterPoint = (features) => {
  var scene = viewer.scene;
  var primitivecluster = new PrimitiveCluster();
  //與entitycluster相同設置其是否聚合 以及最大最小值
  primitivecluster.enabled = true;
  primitivecluster.pixelRange = 60;
  primitivecluster.minimumClusterSize = 2;
  // primitivecluster._pointCollection = pointCollection;
  // primitivecluster._labelCollection = labelCollection;
  for (let i = 0; i < features.length; i++) {
    const feature = features[i];
    const coordinates = feature.geometry.coordinates;
    const position = Cesium.Cartesian3.fromDegrees(
      coordinates[0],
      coordinates[1]
    );
    // 帶圖片的點
    billboardsCollectionCombine.add({
      image: "/images/mark-icon.png",
      width: 32,
      height: 32,
      position,
    });
  }
  // 將數據傳給primitivecluster的標簽屬性
  primitivecluster._billboardCollection = billboardsCollectionCombine;
  // 初始化
  primitivecluster._initialize(scene);
  // 將標簽數據添加到實體中
  primitives.add(primitivecluster);
  // 監聽相機縮放
  primitivecluster.clusterEvent.addEventListener(
    (clusteredEntities, cluster) => {
      // 關閉自帶的顯示聚合數量的標簽
      cluster.label.show = false;
      cluster.billboard.show = true;
      cluster.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM;
      // 根據聚合數量的多少設置不同層級的圖片以及大小
      cluster.billboard.image = combineIconAndLabel(
        "/images/school-icon.png",
        clusteredEntities.length,
        64
      );
      // cluster.billboard.image = "/images/school-icon.png";
      cluster.billboard._imageHeight = 60;
      cluster.billboard._imageWidth = 60;
      cluster.billboard._dirty = false;
      cluster.billboard.width = 40;
      cluster.billboard.height = 40;
    }
  );
  return primitivecluster;
};
/**
 * @description: 將圖片和文字合成新圖標使用(參考Cesium源碼)
 * @param {*} url:圖片地址
 * @param {*} label:文字
 * @param {*} size:畫佈大小
 * @return {*} 返回canvas
 */
function combineIconAndLabel(url, label, size) {
  // 創建畫佈對象
  let canvas = document.createElement("canvas");
  canvas.width = size;
  canvas.height = size;
  let ctx = canvas.getContext("2d");
  let promise = new Cesium.Resource.fetchImage(url).then((image) => {
    // 異常判斷
    try {
      ctx.drawImage(image, 0, 0);
    } catch (e) {
      console.log(e);
    }
    // 渲染字體
    // font屬性設置順序:font-style, font-variant, font-weight, font-size, line-height, font-family
    ctx.fillStyle = Cesium.Color.BLACK.toCssColorString();
    ctx.font = "bold 20px Microsoft YaHei";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.fillText(label, size / 2, size / 2);
    return canvas;
  });
  return promise;
}

ok,以上就是完整的使用方法,主要是如何使用,不然會造成canvas相關方面的報錯等等;

詳細源碼細節可以查看:github.com/tingyuxuan2… ,此開源項目集合瞭目前常用的一些三維動畫場景,還在不斷更新中;

以上就是詳解cesium實現大批量POI點位聚合渲染優化方案的詳細內容,更多關於cesium大批量POI點位聚合渲染的資料請關註WalkonNet其它相關文章!

推薦閱讀: