javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > cesium大批量POI点位聚合渲染

详解cesium实现大批量POI点位聚合渲染优化方案

作者:不浪brown

这篇文章主要为大家介绍了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点位聚合渲染的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文