Cesium案例(八) Terrain

2023-04-08 06:01:14

第一步正常建viewer,需要注意的是官網例子屬性值比較老,最新版本的屬性值有所差異,全copy官網會無法執行,提示函數未定義。

第一處差異

官網:

 

1 const viewer = new Cesium.Viewer("cesiumContainer", {
2   terrain: Cesium.Terrain.fromWorldTerrain({
3     requestWaterMask: true,
4     requestVertexNormals: true,
5   }),
6 });

 

實際程式碼:

const viewer = new Cesium.Viewer("cesiumContainer", {
        terrainProvider: Cesium.createWorldTerrain({
          requestWaterMask: true,
          requestVertexNormals: true,
        }),
      });

 


 
Cesium.createWorldTerrain          //為 Cesium World Terrain 建立一個 CesiumTerrainProvider 範例

第二處差異

 
官方
{
      text: "CesiumTerrainProvider - Cesium World Terrain",
      onselect: function () {
        viewer.scene.setTerrain(
          Cesium.Terrain.fromWorldTerrain({
            requestWaterMask: true,
            requestVertexNormals: true,
          })
        );
        viewer.scene.globe.enableLighting = true;
      },

實際程式碼

{
            text: "CesiumTerrainProvider - Cesium World Terrain",
            onselect: function () {
              viewer.terrainProvider = Cesium.createWorldTerrain({
                requestWaterMask: true,
                requestVertexNormals: true,
              });
              viewer.scene.globe.enableLighting = true;
            },
          },

後續幾個都是如此,直接替換

第三處程式碼差異

官網程式碼
{
      text: "VRTheWorldTerrainProvider",
      onselect: function () {
        viewer.scene.setTerrain(
          new Cesium.Terrain(
            Cesium.VRTheWorldTerrainProvider.fromUrl(
              "http://www.vr-theworld.com/vr-theworld/tiles1.0.0/73/",
              {
                credit: "Terrain data courtesy VT MÄK",
              }
            )
          )
        );
      },

 

 實際程式碼
    {
            text: "VRTheWorldTerrainProvider",
            onselect: function () {
              viewer.terrainProvider = Cesium.createWorldTerrain(
                Cesium.VRTheWorldTerrainProvider({
                  url: "http://www.vr-theworld.com/vr-theworld/tiles1.0.0/73/",

                  credit: "Terrain data courtesy VT MÄK",
                })
              );
            },
          },

完整程式碼

     Cesium.Ion.defaultAccessToken =
  token;
      const viewer = new Cesium.Viewer("cesiumContainer", {
        terrainProvider: Cesium.createWorldTerrain({
          //為 Cesium World Terrain 建立一個 CesiumTerrainProvider 範例
          requestWaterMask: true,
          //指示使用者端是否應從伺服器請求每個瓷磚水面具(如果可用)。

          requestVertexNormals: true,
          //指示使用者端是否應從伺服器請求額外的照明資訊(如果可用)。
        }),
      });

      viewer.scene.globe.enableLighting = true;
      //啟用使用場景光源照亮地球。

      const ellipsoidProvider = new Cesium.EllipsoidTerrainProvider();
      //一個非常簡單的 TerrainProvider ,它通過對橢球面進行鑲嵌來生成幾何圖形。

      const customHeightmapWidth = 32;
      const customheightmapHeight = 32;
      const customHeightmapProvider = new Cesium.CustomHeightmapTerrainProvider(
        //從回撥函數獲取高度值的簡單 TerrainProvider 。
        //它可以用於程式生成的地形或作為一種載入自定義高度圖資料的方式,
        //而無需建立 TerrainProvider 的子類。
        //有一些限制,例如沒有水面具、沒有頂點法線和沒有可用性,
        //所以一個成熟的 TerrainProvider 子類更適合這些更復雜的用例。
        {
          width: customHeightmapWidth,
          height: customheightmapHeight,
          callback: function (x, y, level) {
            const width = customHeightmapWidth;
            const height = customheightmapHeight;
            const buffer = new Float32Array(width * height);
            for (let yy = 0; yy < height; yy++) {
              for (let xx = 0; xx < width; xx++) {
                const u = (x + xx / (width - 1)) / Math.pow(2, level);
                const v = (y + yy / (height - 1)) / Math.pow(2, level);

                const heightValue = 4000 * (Math.sin(8000 * v) * 0.5 + 0.5);
                const index = yy * width + xx;
                buffer[index] = heightValue;
              }
            }

            return buffer;
          },
        }
      );

      Sandcastle.addToolbarMenu(
        [
          {
            text: "CesiumTerrainProvider - Cesium World Terrain",
            onselect: function () {
              viewer.terrainProvider = Cesium.createWorldTerrain({
                requestWaterMask: true,
                requestVertexNormals: true,
              });
              viewer.scene.globe.enableLighting = true;
            },
          },

          {
            text: "CesiumTerrainProvider - Cesium World Terrain - no effects",
            onselect: function () {
              viewer.terrainProvider = new Cesium.createWorldTerrain();
            },
          },
          {
            text: "CesiumTerrainProvider - Cesium World Terrain w/ Lighting",
            onselect: function () {
              viewer.terrainProvider = Cesium.createWorldTerrain({
                requestVertexNormals: true,
              });
              viewer.scene.globe.enableLighting = true;
            },
          },
          {
            text: "CesiumTerrainProvider - Cesium World Terrain w/ Water",
            onselect: function () {
              viewer.terrainProvider = Cesium.createWorldTerrain({
                requestWaterMask: true,
              });
            },
          },
          {
            text: "EllipsoidTerrainProvider",
            onselect: function () {
              viewer.terrainProvider = ellipsoidProvider;
            },
          },
          {
            text: "CustomHeightmapTerrainProvider",
            onselect: function () {
              viewer.terrainProvider = customHeightmapProvider;
            },
          },
          {
            text: "VRTheWorldTerrainProvider",
            onselect: function () {
              viewer.terrainProvider = Cesium.createWorldTerrain(
                Cesium.VRTheWorldTerrainProvider({
                  url: "http://www.vr-theworld.com/vr-theworld/tiles1.0.0/73/",

                  credit: "Terrain data courtesy VT MÄK",
                  // 資料來源的功勞,顯示在畫布上。
                })
              );
            },
          },
          {
            text: "ArcGISTerrainProvider",
            onselect: function () {
              viewer.terrainProvider = Cesium.createWorldTerrain(
                Cesium.ArcGISTiledElevationTerrainProvider({
                  url: "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer",
                })
              );
            },
          },
        ],
        "terrainMenu"
      );
      Sandcastle.addDefaultToolbarMenu(
        [
          {
            text: "Mount Everest",
            onselect: function () {
              const target = new Cesium.Cartesian3(
                300770.50872389384,
                5634912.131394585,
                2978152.2865545116
              );
              const offset = new Cesium.Cartesian3(
                6344.974098678562,
                -793.3419798081741,
                2499.9508860763162
              );
              viewer.camera.lookAt(target, offset);
              viewer.camera.lookAtTransform(
                Cesium.Matrix4.IDENTITY //transform     Matrix4     定義參考系的變換矩陣。
              );
              //使用目標和變換矩陣設定相機位置和方向。
              //偏移量可以是笛卡爾座標或航向/間距/範圍。
              //如果偏移量是笛卡爾座標,則它是從由變換矩陣定義的參考系中心的偏移量。
              //如果偏移量是航向/俯仰/範圍,則航向和俯仰角在由變換矩陣定義的參考系中定義。
              //航向是從 y 軸到 x 軸增加的角度。
              //俯仰是從 xy 平面的旋轉。正俯仰角在平面下方。
              //負俯仰角在平面上方。範圍是到中心的距離。
              //在 2D 中,必須有自上而下的檢視。相機將放置在參考框架的中心上方。
              //目標上方的高度將是偏移量的大小。航向將根據偏移量確定。
              //如果無法根據偏移量確定航向,則航向將為北。
            },
          },
          {
            text: "Half Dome",
            onselect: function () {
              const target = new Cesium.Cartesian3(
                -2489625.0836225147,
                -4393941.44443024,
                3882535.9454173897
              );
              const offset = new Cesium.Cartesian3(
                -6857.40902037546,
                412.3284835694358,
                2147.5545426812023
              );
              viewer.camera.lookAt(target, offset);
              viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
            },
          },
          {
            text: "San Francisco Bay",
            onselect: function () {
              const target = new Cesium.Cartesian3(
                -2708814.85583248,
                -4254159.450845907,
                3891403.9457429945
              );
              const offset = new Cesium.Cartesian3(
                70642.66030209465,
                -31661.517948317807,
                35505.179997143336
              );
              viewer.camera.lookAt(target, offset);
              viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
            },
          },
        ],
        "zoomButtons"
      );
      let terrainSamplePositions;
      function sampleTerrainSuccess(terrainSamplePositions) {
        const ellipsoid = Cesium.Ellipsoid.WGS84;
        //初始化為 WGS84 標準的 Ellipsoid 範例。
        viewer.scene.globe.depthTestAgainstTerrain = true;
        //如果廣告牌、折線、標籤等圖元應針對地形表面進行深度測試,則為 true;
        //如果此類圖元應始終繪製在地形頂部,除非它們位於地球的另一側,則為 false。
        //針對地形進行深度測試圖元的缺點是,
        //輕微的數值噪聲或地形細節級別的切換有時會使應該在表面上的圖元在其下方消失。

        viewer.entities.suspendEvents();
        //防止引發 EntityCollection#collectionChanged 事件,
        //直到對 EntityCollection#resumeEvents 進行相應的呼叫,
        //此時將引發涵蓋所有暫停操作的單個事件。
        //這允許有效地新增和刪除許多專案。
        //只要有對 EntityCollection#resumeEvents 的相應呼叫,就可以安全地多次呼叫此函數。
        viewer.entities.removeAll();
        // 從集合中移除所有實體。
        for (let i = 0; i < terrainSamplePositions.length; ++i) {
          const position = terrainSamplePositions[i];

          viewer.entities.add({
            name: position.height.toFixed(1),
            //toFixed 固定小數位
            position: ellipsoid.cartographicToCartesian(position),
            //將提供的製圖轉換為笛卡爾表示。
            billboard: {
              //描述位於包含 Entity 位置的二維圖示。
              verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
              //原點相對於物件的垂直位置,例如 Billboard 或 Label 。例如,
              //將垂直原點設定為 TOP 或 BOTTOM 將在錨點位置上方或下方(在螢幕空間中)顯示廣告牌
              scale: 0.7,
              //     一個數位屬性,指定應用於影象大小的比例。
              image: "../../images/facility.gif",
              //一個屬性,指定用於廣告牌的影象、URI 或畫布。
            },
            label: {
              //描述位於包含 Entity 位置的二維標籤
              text: position.height.toFixed(1),
              //     指定文字的屬性。支援顯式換行符 ''。
              font: "10pt monospace",
              horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
              //原點相對於物件的水平位置,例如 Billboard 或 Label 。
              //例如,將水平原點設定為 LEFT 或 RIGHT 將在錨點位置的左側或右側(在螢幕空間中)顯示廣告牌。
              pixelOffset: new Cesium.Cartesian2(0, -14),
              //     指定畫素偏移的 Cartesian2 屬性
              fillColor: Cesium.Color.BLACK, //填充
              outlineColor: Cesium.Color.BLACK, //輪廓
              showBackground: true,
              //一個布林屬性,指定標籤背後背景的可見性。
              backgroundColor: new Cesium.Color(0.9, 0.9, 0.9, 0.7),
              backgroundPadding: new Cesium.Cartesian2(4, 3),
              //     一個 Cartesian2 屬性,以畫素為單位指定水平和垂直背景填充。
            },
          });
        }
        viewer.entities.resumeEvents();
        //新增或刪除專案時立即恢復引發 EntityCollection#collectionChanged 事件。
        //在事件掛起期間所做的任何修改都將在呼叫此函數時作為單個事件觸發。
        //此函數是參照計數的,只要有對 EntityCollection#resumeEvents 的相應呼叫,就可以安全地多次呼叫該函數。
      }

      function createGrid(rectangleHalfSize) {
        //矩形一半大小
        const gridWidth = 41;
        const gridHeight = 41;
        const everestLatitude = Cesium.Math.toRadians(27.988257);
        //將度數轉換為弧度
        const everestLongitude = Cesium.Math.toRadians(86.925145);
        //everest 最高
        const e = new Cesium.Rectangle(
          everestLongitude - rectangleHalfSize,
          everestLatitude - rectangleHalfSize,
          everestLongitude + rectangleHalfSize,
          everestLatitude + rectangleHalfSize
        );
        const terrainSamplePositions = [];
        for (let y = 0; y < gridHeight; ++y) {
          for (let x = 0; x < gridWidth; ++x) {
            const longitude = Cesium.Math.lerp(
              e.west,
              e.east,
              x / (gridWidth - 1)
            );
            //計算兩個值的線性插值。
            const latitude = Cesium.Math.lerp(
              e.south,
              e.north,
              y / (gridHeight - 1)
            );
            const position = new Cesium.Cartographic(longitude, latitude);
            //由經度、緯度和高度定義的位置。
            terrainSamplePositions.push(position);
          }
        }
        return terrainSamplePositions;
      }

      Sandcastle.addToggleButton(
        "Enable Lighting",
        viewer.scene.globe.enableLighting,
        function (checked) {
          viewer.scene.globe.enableLighting = checked;
        }
      );

      Sandcastle.addToggleButton(
        "Enable fog",
        viewer.scene.fog.enabled,
        function (checked) {
          viewer.scene.fog.enabled = checked;
        }
      );

      Sandcastle.addToolbarButton(
        "Sample Everest Terrain at Level 9",
        function () {
          const terrainSamplePositions = createGrid(0.005);
          Promise.resolve(
            Cesium.sampleTerrain(
              //通過向地形提供者請求切片、取樣和插值,啟動對 Cartographic 位置陣列的地形高度查詢。
              //插值匹配用於在指定級別渲染地形的三角形。查詢是非同步發生的,所以這個函數返回一個在查詢完成時解決的 Promise。
              //每個點的高度都在原地修改。如果由於該位置的指定級別沒有可用的地形資料而無法確定高度,或者發生其他錯誤,則將高度設定為未定義。
              //作為典型的 Cartographic 型別,提供的高度是參考橢球體上方的高度(例如 Ellipsoid.WGS84 ),而不是高於平均海平面的高度。
              //換句話說,如果在海洋中取樣,它不一定是 0.0。此函數需要地形細節級別作為輸入,
              //如果您需要儘可能精確地獲取地形高度(即最大細節級別),請使用 sampleTerrainMostDetailed 。
              viewer.terrainProvider,
              9,
              terrainSamplePositions
              //terrainProvider     TerrainProvider     從中查詢高度的地形提供者。
              //level     Number     用於查詢地形高度的地形細節層次。
              //positions     Array.< Cartographic >     使用地形高度更新的位置。
            )
          ).then(sampleTerrainSuccess);
          const target = new Cesium.Cartesian3(
            300770.50872389384,
            5634912.131394585,
            2978152.2865545116
          );
          const offset = new Cesium.Cartesian3(
            6344.974098678562,
            -793.3419798081741,
            2499.9508860763162
          );
          viewer.camera.lookAt(target, offset);
          viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
        },
        "sampleButtons"
      );

      Sandcastle.addToolbarButton(
        "Sample Most Detailed Everest Terrain",
        function () {
          if (!Cesium.defined(viewer.terrainProvider.availability)) {
            window.alert(
              "sampleTerrainMostDetailed is not supported for the selected terrain provider"
            );
            return;
          }
          const terrainSamplePositions = createGrid(0.0005);
          Promise.resolve(
            Cesium.sampleTerrainMostDetailed(
              viewer.terrainProvider,
              terrainSamplePositions
            )
            //在地形資料集的最大可用切片級別啟動 sampleTerrain() 請求。
          ).then(sampleTerrainSuccess);
          const target = new Cesium.Cartesian3(
            300770.50872389384,
            5634912.131394585,
            2978152.2865545116
          );
          const offset = new Cesium.Cartesian3(
            6344.974098678562,
            -793.3419798081741,
            2499.9508860763162
          );
          viewer.camera.lookAt(target, offset);
          viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
        },
        "sampleButtons"
      );