• Cesium加载3D Tiles数据:
  • 作业

9 - 3D Tiles

  • 我们团队有时把Cesium描述成一个真实世界数据的3D游戏引擎。然而,使用真实世界的数据比使用典型的视频游戏数据资料要困难得多,因为真实数据可能是难以置信的高分辨率,并且需要精确的可视化。幸运的是,Cesium 与开源社区合作开发了3D Tilesopen in new window,这是一个开放的规范open in new window,用于传输海量的异构三维地理空间数据集。

    使用概念上类似于Cesium的terrain和imagery的流技术,3D Tiles 使得可以查看原本不能交互式查看的巨大的模型,包括建筑物数据集、CAD(或BIM)模型、点云和摄影测量模型。

    下面是一些展示不同格式的3D Tiles演示:

    在我们的应用中,我们将使用Cesium3DTileset通过展示纽约所有建筑物的全3D模型来为我们的可视化添加现实主义!这种纽约 tileset托管在Cesium Ion中,我们可以使用Cesium3DTileset.fromIonAssetId添加它:

    // Load the NYC buildings tileset
    // 旧版本
    // var city = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({ url: Cesium.IonResource.fromAssetId(3839) }));
    // 新版本
    const city = await Cesium.Cesium3DTileset.fromIonAssetId(75343, {});
    

    你可能注意到建筑物没有正确地定位在地平面上。幸运的是,它很容易修复。我们可以通过修改模型矩阵modelMatrix来调整tileset的位置。

    我们可以通过将tileset的边界球转换成地图Cartographic,然后添加期望的偏移量并重置模型矩阵,从地面找到模型modelMatrix的当前偏移量。

    // Adjust the tileset height so its not floating above terrain
    var heightOffset = -32;
    var boundingSphere = city.boundingSphere;
    var cartographic = Cesium.Cartographic.fromCartesian(boundingSphere.center);
    var surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0.0);
    var offset = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, heightOffset);
    var translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3());
    city.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
    

    现在在我们的场景中有110万个建筑模型流。

    3D Tiles 还允许我们使用3D Tiles styling语言 来调整我们的样式。3D Tiles 样式定义了用于评估颜色(RGB和透明度)的表达式,并显示了Cesium3DTileFeature特征的属性,这是tileset的一部分,例如城市中的单个建筑物。样式通常基于存储在瓦片的批处理表中的特征属性。特征属性可以是高度、名称、坐标、构造日期等任何东西,但被构建到tileset asset 中。样式是用JSON定义的,而表达式是在JavaScript的小子集中编写的,用于样式化。此外,样式语言提供了一组内置函数来支持常见的数学运算。

    一个Cesium3DTilesetStyle的例子如下:

    var defaultStyle = new Cesium.Cesium3DTileStyle({
        color : "color('white')",
        show : true
    });
    

    上述代码使我们NYC的tileset是白色并且总是可见的。为了实际设置tileset的样式,我们设置city.style:

    city.style = defaultStyle;
    

    img

    我们还可以定义许多我们喜欢的样式。比如,让建筑变透明:

    var transparentStyle = new Cesium.Cesium3DTileStyle({
        color : "color('white', 0.3)",
        show : true
    });
    city.style = transparentStyle;
    

    img

    在我们的tileset中使用相同的样式来达到每一个特征只是皮毛工作。我们还可以使用特定于每个特征的属性来确定造型。下面是一个基于建筑物高度的建筑物颜色的例子:

    var heightStyle = new Cesium.Cesium3DTileStyle({
        color : {
            conditions : [
                ["${Height} >= 300", "rgba(45, 0, 75, 0.5)"],
                ["${Height} >= 200", "rgb(102, 71, 151)"],
                ["${Height} >= 100", "rgb(170, 162, 204)"],
                ["${Height} >= 50", "rgb(224, 226, 238)"],
                ["${Height} >= 25", "rgb(252, 230, 200)"],
                ["${Height} >= 10", "rgb(248, 176, 87)"],
                ["${Height} >= 5", "rgb(198, 106, 11)"],
                ["true", "rgb(127, 59, 8)"]
            ]
        }
    });
    

    img

    为了在样式间交换,我们可以添加更多的代码来侦听HTML输入:

    const viewer = new Cesium.Viewer("cesiumContainer", {
            terrain: Cesium.Terrain.fromWorldTerrain(),
            // infoBox: false,
          });
    
          try {
            const city = await Cesium.Cesium3DTileset.fromIonAssetId(75343, {});
            console.log(city);
            viewer.scene.primitives.add(city);
            var heightOffset = -32;
            var boundingSphere = city.boundingSphere;
            var cartographic = Cesium.Cartographic.fromCartesian(
              boundingSphere.center
            );
            var surface = Cesium.Cartesian3.fromRadians(
              cartographic.longitude,
              cartographic.latitude,
              0.0
            );
            var offset = Cesium.Cartesian3.fromRadians(
              cartographic.longitude,
              cartographic.latitude,
              heightOffset
            );
            var translation = Cesium.Cartesian3.subtract(
              offset,
              surface,
              new Cesium.Cartesian3()
            );
            city.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
            var transparentStyle = new Cesium.Cesium3DTileStyle({
              color: "color('white', 0.3)",
              show: true,
            });
            city.style = transparentStyle;
            var heightStyle = new Cesium.Cesium3DTileStyle({
              color: {
                conditions: [
                  ["${Height} >= 300", "rgba(45, 0, 75, 0.5)"],
                  ["${Height} >= 200", "rgb(102, 71, 151)"],
                  ["${Height} >= 100", "rgb(170, 162, 204)"],
                  ["${Height} >= 50", "rgb(224, 226, 238)"],
                  ["${Height} >= 25", "rgb(252, 230, 200)"],
                  ["${Height} >= 10", "rgb(248, 176, 87)"],
                  ["${Height} >= 5", "rgb(198, 106, 11)"],
                  ["true", "rgb(127, 59, 8)"],
                ],
              },
              show: "${Height} > 0",
              meta: {
                description: '"Building id ${id} has height ${Height}."',
              },
            });
            city.style = heightStyle;
    
            viewer.zoomTo(
              city,
              new Cesium.HeadingPitchRange(
                0.0,
                -0.5,
                city.boundingSphere.radius / 4.0
              )
            );
            const options = [
              {
                text: "高度",
                onselect: function () {
                  city.style = heightStyle;
                },
              },
              {
                text: "透明度",
                onselect: function () {
                  city.style = transparentStyle;
                },
              },
            ];
            // viewer.scene.imageryLayers和viewer.imageryLayers是同一对象
            Sandcastle.addToolbarMenu(options);
          } catch (error) {
            console.log(`Error loading tileset: ${error}`);
          }
    

    更多关于3D Tiles的例子、如何使用及调整样式,请查看[the 3D Tiles demos](http://111.22.69.99:58080/cesium/Apps/Sandcastle/?src=Hello%20World.html&label=3D Tiles)。

    3D Tiles例子:

Cesium加载3D Tiles数据:

(1)3D Tiles数据介绍:

带有LOD(层次细节模型)的glTF,专门为流式传输和渲染海量3D地理空间数据设计,例如倾斜摄影、3D建模、BIM/CAD、实例化要素集的点云。

3D Tiles相关类介绍:

cesium更新版本后将Cesium3DTileFeature类的getPropertyNames()换成getPropertyIds()。

(2)添加3D Tiles数据代码:

// 添加3D Tiles的数据源,需要使用异步函数加载
  async function add3DTile() {
    const tileset = await Cesium.Cesium3DTileset.fromUrl(
      "/Tileset/tileset.json"       // 瓦片索引
    )
    // 1,添加tileset
    viewer.scene.primitives.add(tileset)
    // 视图定位
    viewer.zoomTo(tileset, new Cesium.HeadingPitchRange(
      0.0, -0.5, tileset.boundingSphere.radius * 2.0
    ))
    const boundingSphere = tileset.boundingSphere
    console.log(boundingSphere.center)
    // 2,设置样式
    const properties = tileset.properties
    if (Cesium.defined(properties) && Cesium.defined(properties.Height)) {
      tileset.style = new Cesium.Cesium3DTileStyle({
        color: {
          conditions: [
            ["${Height} >= 83", "color('purple', 0.5)"],
            ["${Height} >= 80", "color('red')"],
            ["${Height} >= 70", "color('orange')"],
            ["${Height} >= 12", "color('yellow')"],
            ["${Height} >= 7", "color('lime')"],
            ["${Height} >= 1", "color('cyan')"],
            ["true", "color('blue')"]
          ]
        }
      })
    }
    // 3,添加鼠标拾取要素
    const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas)
    // 绑定鼠标左点击事件
    handler.setInputAction((e) => {
      console.log(e)
      let windowPosition = e.position   // 屏幕坐标
      // 屏幕坐标转世界坐标
      const ray = viewer.camera.getPickRay(windowPosition)
      const catesian = viewer.scene.globe.pick(ray, viewer.scene)
      // 世界坐标转经纬度坐标
      const lat = Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(catesian).latitude)
      const lng = Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(catesian).longitude)
      console.log('经度坐标是:' + lng + ';纬度坐标是:' + lat)
      // 获取要素
      const feature = viewer.scene.pick(windowPosition)
      if (Cesium.defined(feature) && feature instanceof Cesium.Cesium3DTileFeature) {
        const propertyIds = feature.getPropertyIds()
        console.log(propertyIds)
        const length = propertyIds.length
        for (let i = 0; i < length; ++i) {
          let propertyId = propertyIds[i]
          console.log(propertyId + ': ' + feature.getProperty(propertyId))
        }
      }

    }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
  }
  add3DTile()

(3)3D Tiles制作平移动画:

// 4,3DTiles制作向上平移的动画
    const adjustTilesetHeight = (tileset, height = 10) => {
      // 计算出模型模型包围球的中心点,转弧度制
      const cartography = Cesium.Cartographic.fromCartesian(tileset.boundingSphere.center)
      // 计算与模型包围球中心点经纬度相同的地表点位
      const surface = Cesium.Cartesian3.fromRadians(
        cartography.longitude, cartography.latitude,
        0
      )
      // 计算调整高度后的模型包围球的中心点
      const offset = Cesium.Cartesian3.fromRadians(
        cartography.longitude, cartography.latitude,
        height
      )
      // 计算差向量
      const diff = Cesium.Cartesian3.subtract(
        offset, surface, new Cesium.Cartesian3()
      )
      console.log(diff);
      // 修改模型的变换矩阵
      tileset.modelMatrix = Cesium.Matrix4.fromTranslation(diff)
    }
    let h = 10
    setInterval(() => {
      adjustTilesetHeight(tileset, h++)
    }, 1000)

作业

  1. 使用下拉列表修改模型的透明度和颜色。

image-20240409162735930

  1. 3D Tiles制作平移动画。

image-20240409163108688

  1. 加载华旦的3dtiles的url(http://111.22.69.99:58080/cesium/Apps/SampleData/Cesium3DTiles/huadan/tileset.json)到地图中,并调整离地高度,使用flyTo调整相机位置。

image-20240409162226997

  1. (选做)参考天地图官方文档三维服务 - 天地图 帮助文档 (tianditu.gov.cn)open in new window,使用天地图卫星影像底图和地形图+华旦的3Dtiles模型。

image-20240409163935198

Last Updated:
Contributors: zly