cesium加载三维立体primitive图元并贴地展示

项目场景:

由于项目里需要加载很多三维立方体,之前用的方式是entity,但是数据量大的话会有点卡顿,于是换成primitive图元的方式加载。

简单记录一下遇到的问题。

问题描述

viewer.scene.primitives.add()添加primitive时,地球卡死了,没办法进行放大缩小拖拽等一系列地球操作。
值得注意的是:当用entity得方式是可以加载出来的,只不过会卡顿,但是也没有遇到过场景scene直接卡死的情况。也就是说可以用entity加载但是一旦换成primitive的方式scene就卡死了。

1.刚开始还以为是几何图形太复杂的问题,但是primitie比entity更接近底层,应该比entity渲染更快才对啊,但是相反entity能加载出来,primitive却卡死了,这也就排除了这换个原因。

2.还以为是电脑GPU的问题但是发现scene场景卡死的时候电脑GPU并没有占用多少,甚至为0%,这也就排除了电脑GPU这一项。


原因分析:

排查数据格式。

我们知道加载多边形primitive需要传入经纬度数组

const geometry = new Cesium.PolygonGeometry({
     polygonHierarchy: new Cesium.PolygonHierarchy(
          new Cesium.Cartesian3.fromDegreesArray(geoms) // geoms为经纬度数组
     ),
     extrudedHeight: entityHeight,
   });

于是就对数据进行一条一条加载。直到遇到scene卡死的那条数据。
知道找到了场景卡死的那条数据:
[107.24044625699061, 31.291311316851022, 107.24047198601231, 31.291311468987722, 107.24047250052004, 31.291311493744068, 107.2404730097246, 31.29131156168811, 107.24047350872203, 31.291311672165513,NaN,NaN,107.24047399270674,31.291311824112327, 107.24047445701771, 31.291312016065213]。
注意里面竟然有两条NaN,这里就排查到了是这条数据的问题,导致加载暂停,场景卡死的。所以我查看了原始的WKT格式的数据。
大概长这样:

"MULTIPOLYGON(((107.25941467940206 31.291917660957814,107.25941751810778 31.291915276197532,107.2594241475583 31.29190534342199,107.25941467940206 31.291917660957814)),((107.25942693970325 31.291901710998584,107.2594272814655 31.291901545782753,107.25942773447713 31.291871994888496,107.25949975585885 31.291838338096827,107.25947575902191 31.291838199756548,107.25942693970325 31.291901710998584)))"

注意:这是一个多重多边形,这条数据里包含了两个polygon,于其他数据不一样,其他数据一个multipolygon只包含了一个polygon数据。
由于是需要我这边手动将wkt转为经纬度数组的。所以看了一下写的方法,代码如下:

 const geoms = item
            .replace('MULTIPOLYGON(((', '')
            .replace(')))', '')
            .replace(/\s/g, ',')
            .split(',')
            .map(item => Number(item));

问题显而易见了,上面这种方法只处理了开头和结尾处的字段,并没有对中间的括号")),((" 进行处理。所以再数组中才会出现NaN这种情况。
从wkt数据里面提取经纬度方法就改为如下:

 const geoms = item
            .replace('MULTIPOLYGON', '')
            .replace(/[()]/g, '')
            .replace(/\s/g, ',')
            .split(',')
            .map(item => Number(item));

这样就能正常渲染加载primitive了

可以看到中间有条线,将两个polygon连为一体了。
我这里没有做处理,这种情况在项目里并不常见。
如果想要处理的话,可以跟后端商量一下谁来调整。

三维primitive贴地展示:

这个根据自己的接口数据类型来调整。由于我这里数据量较大,所以先定位到具体的bbox再进行渲染数据,这里是每100条进行一次渲染。所以采用:
1.先处理所有的wkt数据转为一个二维经纬度数组。每一条数据为一个生成primitive所需的经纬度数据。
2.遍历生成的二维经纬度数组,每100条加载一次数据。
3.由于需要贴地展示,而如果用GroundPrimitive的话,是可以进行贴地的,但是就没有了拉伸高度,所以还需要用Primitie来加载。 如果需要拉伸高度的话需要根据经纬度点查询位置点的地形高度,然后加上你需要拉伸的立方体高度。

// 采用1先漫游到bbox,2再渲染primitive
  async loadGeoJSON(data) {
    const viewer = window.viewer;
    const chunkSize = 100; // 每100条数据加载一次
    console.time();
    //1
    const coords = []; // 存储所有的坐标点,用于生成primitive和计算bbox
    for (let i = 0; i < data.length; i++) {
      const geoms = data[i]
        .replace('MULTIPOLYGON', '')
        .replace(/[()]/g, '')
        .replace(/\s/g, ',')
        .split(',')
        .map(item => Number(item));
      coords.push(geoms);
    }
    viewer.camera.flyTo({
      destination: new Cesium.Rectangle.fromDegrees(...getBBoxByCoords(coords)),
    });
    //2
    for (let index = 0; index < coords.length; index += chunkSize) {
      const chunk = coords.slice(index, index + chunkSize);
      const instances = await Promise.all(
        chunk.map(async item => {
          // 异步根据经纬度获取地形高度
          const height = await getHeigthByLonLat(item[0], item[1]);
          const entityHeight = height + 50;
          const geometry = new Cesium.PolygonGeometry({
            polygonHierarchy: new Cesium.PolygonHierarchy(
              new Cesium.Cartesian3.fromDegreesArray(item)
            ),
            extrudedHeight: entityHeight,
          });
          return new Cesium.GeometryInstance({
            geometry,
          });
        })
      );
      const primitive = new Cesium.Primitive({
        geometryInstances: instances,
        appearance: new Cesium.MaterialAppearance({
          material: new Cesium.Material({
            fabric: {
              type: 'Color',
              uniforms: {
                color: new Cesium.Color(1.0, 1.0, 0.0, 1.0),
              },
            },
          }),
        }),
      });
      viewer.scene.primitives.add(primitive);
    }
    console.timeEnd();
  },
};

根据经纬度查询地形高度:

export const getHeigthByLonLat = async (lon, lat) => {
  var positions = Cesium.Cartographic.fromDegrees(lon, lat);
  const updatedPositions = await Cesium.sampleTerrain(window.viewer.terrainProvider, 13, [
    positions,
  ]);
  if (updatedPositions && updatedPositions.length) {
    return updatedPositions[0].height;
  } else {
    return 0;
  }
};

总结:如果加载primitive,scene卡死,大概率就是数据的问题,只需要进行排查即可。

  • 17
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值