麻辣GIS微信平台

更多 GIS 干货

微信关注不错过

Cesium正式支持海底地球了,Cesium World Bathymetry 上手试用指南

最近刷到 Cesium ion 上出现了一套新的地形数据 Cesium World Bathymetry,之前使用 Cesium 做三维地球开发的时候,默认使用的地形数据是 Cesium World Terrain,那它们之间有什么不同呢?小编于是写了个 Demo,结果发现 Cesium World Bathymetry 这套数据集更偏向海底地形,比如海沟、珊瑚礁、近岸水深起伏等,今天小编就给大家做个介绍。

什么是 Cesium World Bathymetry

Cesium World Bathymetry 是一套全球覆盖的水深与地形瓦片,quantized-mesh 1.0格式,把多源水深与近岸地形集成到了一张全球底图。底库主要来自 GEBCO 2023,再叠上各地更高分辨率的水深成果,并补了内陆地形。

官方示例中的应用方向包括油气勘探、无人潜航器导航、海上风电场管理、气候相关分析等;对 GISer 来说,更常见的是海岸带三维展示、海底地貌科普,以及把自有 DEM 叠到这套全球底图上做对照。

基础数据信息:

官方给出的大致精度如下:

  1. 瑞士湖泊:约 1 m、2 m、3 m
  2. 美国东岸、美属岛屿,以及阿拉斯加、俄勒冈、加州部分区域:约 3 m、10 m
  3. 墨西哥湾北部:约 12 m
  4. 大堡礁:约 30 m
  5. 美国西海岸:约 90 m
  6. 全球其余区域:约 450 m

CesiumJS 上手示例

登录 Cesium ion,把 Cesium World Bathymetry 加到 My Assets。对应 Asset ID 是 2426648。较新的 CesiumJS 也可直接用 Cesium.Terrain.fromWorldBathymetry(),不必手写 ID。

下面这份示例中小编主要做了三处改动,一是加载 World Bathymetry,并请求顶点法线;二是关掉雾与地面大气、打开方向光,让海底起伏更清楚;三是用高程色带加等高线,并垂直夸张 3 倍,镜头飞到台湾西岸,把代码中的YOUR_ION_ACCESS_TOKEN 换成自己的 token 后运行即可,运行效果如图:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <title>Cesium World Bathymetry Demo</title>
  <script src="https://cesium.com/downloads/cesiumjs/releases/1.142/Build/Cesium/Cesium.js"></script>
  <link href="https://cesium.com/downloads/cesiumjs/releases/1.142/Build/Cesium/Widgets/widgets.css" rel="stylesheet" />
  <style>
    html, body, #cesiumContainer { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; }
  </style>
</head>
<body>
  <div id="cesiumContainer"></div>
  <script>
    Cesium.Ion.defaultAccessToken = "YOUR_ION_ACCESS_TOKEN";

    const viewer = new Cesium.Viewer("cesiumContainer", {
      terrain: Cesium.Terrain.fromWorldBathymetry({
        requestVertexNormals: true,
      }),
      timeline: false,
      animation: false,
      baseLayerPicker: false,
      geocoder: false,
    });

    const scene = viewer.scene;
    const globe = scene.globe;
    const camera = scene.camera;

    // 水深可视化时,雾与地面大气容易糊掉起伏
    scene.fog.enabled = false;
    globe.showGroundAtmosphere = false;
    globe.enableLighting = true;
    globe.maximumScreenSpaceError = 1.0;
    scene.verticalExaggeration = 3.0;

    scene.light = new Cesium.DirectionalLight({
      direction: new Cesium.Cartesian3(1, 0, 0),
    });

    const scratchNormal = new Cesium.Cartesian3();
    scene.preRender.addEventListener(function () {
      const surfaceNormal = globe.ellipsoid.geodeticSurfaceNormal(
        camera.positionWC,
        scratchNormal
      );
      Cesium.Cartesian3.negate(surfaceNormal, surfaceNormal);
      scene.light.direction = Cesium.Cartesian3.normalize(
        Cesium.Cartesian3.add(surfaceNormal, camera.rightWC, surfaceNormal),
        scene.light.direction
      );
    });

    // 高程色带 + 等高线(色阶参考 cmocean deep)
    const minHeight = -10000.0;
    const maxHeight = 2000.0;
    const contourLineSpacing = 500.0;
    const range = maxHeight - minHeight;
    const d = (height) => (height - minHeight) / range;

    function getColorRamp() {
      const ramp = document.createElement("canvas");
      ramp.width = 100;
      ramp.height = 15;
      const ctx = ramp.getContext("2d");
      const grd = ctx.createLinearGradient(0, 0, 100, 0);
      grd.addColorStop(d(maxHeight), "#B79E6C");
      grd.addColorStop(d(100.0), "#FBFFEE");
      grd.addColorStop(d(0.0), "#F9FCCA");
      grd.addColorStop(d(-500.0), "#BDE7AD");
      grd.addColorStop(d(-1000.0), "#81D2A3");
      grd.addColorStop(d(-1500.0), "#5AB7A4");
      grd.addColorStop(d(-2000.0), "#4C9AA0");
      grd.addColorStop(d(-2500.0), "#437D9A");
      grd.addColorStop(d(-4000.0), "#3E6194");
      grd.addColorStop(d(-5000.0), "#424380");
      grd.addColorStop(d(-8000.0), "#392D52");
      grd.addColorStop(d(minHeight), "#291C2F");
      ctx.fillStyle = grd;
      ctx.fillRect(0, 0, ramp.width, ramp.height);
      return ramp;
    }

    function getElevationContourMaterial() {
      return new Cesium.Material({
        fabric: {
          type: "ElevationColorContour",
          materials: {
            contourMaterial: { type: "ElevationContour" },
            elevationRampMaterial: { type: "ElevationRamp" },
          },
          components: {
            diffuse:
              "(1.0 - contourMaterial.alpha) * elevationRampMaterial.diffuse + contourMaterial.alpha * contourMaterial.diffuse",
            alpha: "max(contourMaterial.alpha, elevationRampMaterial.alpha)",
          },
        },
        translucent: false,
      });
    }

    function updateGlobeMaterial() {
      const material = getElevationContourMaterial();
      const exaggeration = scene.verticalExaggeration;
      let shadingUniforms = material.materials.elevationRampMaterial.uniforms;
      shadingUniforms.image = getColorRamp();
      shadingUniforms.minimumHeight = minHeight * exaggeration;
      shadingUniforms.maximumHeight = maxHeight * exaggeration;
      shadingUniforms = material.materials.contourMaterial.uniforms;
      shadingUniforms.width = 1.0;
      shadingUniforms.spacing = contourLineSpacing * exaggeration;
      shadingUniforms.color = Cesium.Color.BLACK.withAlpha(0.5);
      globe.material = material;
    }

    updateGlobeMaterial();

    // 飞到台湾西岸,便于观察台湾海峡水深起伏
    viewer.camera.flyTo({
      destination: Cesium.Cartesian3.fromDegrees(119.6, 24.0, 180000.0),
      orientation: {
        heading: Cesium.Math.toRadians(90.0),
        pitch: Cesium.Math.toRadians(-45.0),
        roll: 0.0,
      },
      duration: 2.5,
    });
  </script>
</body>
</html>

如果只想最小接入,也可以写成官方文档里的两行。

viewer.scene.setTerrain(
  new Cesium.Terrain(Cesium.CesiumTerrainProvider.fromIonAssetId(2426648))
);

如果需要更详细的说明,可参考官方博客,比如影像底图选择、垂直夸张、等高线密度等都可以进行调整,另外目前的数据中大堡礁、马里亚纳海沟、菲律宾近海等分辨率相对高一点,可以用来做 Demo 演示。

对比 World Terrain

通过上述 Demo 可以看出,Cesium World Terrain 和 World Bathymetry 侧重点不一,一个关注陆地,一个关注海洋。如果目前的应用只聚焦在陆地,建议使用 Cesium World Terrain;如果偏向海洋,建议使用 World Bathymetry。当然也可以根据用户视角位置自动切换。需要注意的是,默认卫星影像在开阔洋面往往偏暗,水深起伏不好辨认,可参考小编的方式用色带把高程差表现出来,或者使用更亮的影像底图。

总结

之前使用 Cesium 做三维地球,海底区域经常是一块“平面蓝”,如果需要展示海底地形就需要自行切片水深 DEM 数据,数据处理和托管都需要一定成本。现在 Cesium 把 World Bathymetry 放进 ion 全球内容库之后,水下地形也能和陆地三维共用同一套接入方式,海岸带展示、海上风电示意、海底地貌这类需求会方便很多,对搞海洋GIS的小伙伴来说是一项不错的数据集。

目前仅有少数区域有高精度数据,后续官方也会更新,有兴趣的可以关注一下。有没有小伙伴做过类似场景?欢迎评论区留言分享。

参考

  1. Cesium World Bathymetry 产品页:https://cesium.com/platform/cesium-ion/content/cesium-world-bathymetry/
  2. Introducing Cesium World Bathymetry:https://cesium.com/blog/2024/01/23/introducing-cesium-world-bathymetry/
  3. Working with Cesium World Bathymetry in CesiumJS:https://cesium.com/blog/2024/01/29/cesium-world-bathymetry-in-cesiumjs/

相关阅读

麻辣GIS-Sailor

作者:

GIS爱好者,学GIS,更爱玩GIS。

声明

1.本文所分享的所有需要用户下载使用的内容(包括但不限于软件、数据、图片)来自于网络或者麻辣GIS粉丝自行分享,版权归该下载资源的合法拥有者所有,如有侵权请第一时间联系本站删除。

2.下载内容仅限个人学习使用,请切勿用作商用等其他用途,否则后果自负。

手机阅读
公众号关注
知识星球
手机阅读
麻辣GIS微信公众号关注
最新GIS干货
关注麻辣GIS知识星球
私享圈子
没有下文

留言板(小编看到第一时间回复)