瀏覽分類:

Cesium

Cesium Terrain 建置

參考文件

https://github.com/geo-data/cesium-terrain-builder

https://hub.docker.com/r/homme/cesium-terrain-builder/

https://github.com/tum-gis/cesium-terrain-builder-docker

我在 ubuntu 上面編譯好多次都失敗,最後不得不妥協使用 docker

要特別注意的是他有分新舊版本,舊版是不能 work 的,雷的很痛苦

舊版

docker pull homme/cesium-terrain-builder

新版

sudo docker pull tumgis/ctb-quantized-mesh

接著我們必須去下載 SRTM 資料集,因為全球的檔案太大我先下載台灣部分區塊測試

SRTM 90m Digital Elevation Data

看網路上這篇說明,需要把 float 轉為 int,並且把 no_data 補成 0 不然會發佈失敗,不過他這篇是 ArcGIS 做出來的,窮人只好用 gdal。

gdal_translate -of GTiff -ot Int32 -a_nodata 0 D:\srtm_61_08\srtm_61_08.tif D:\srtm_61_08\srtm_61_08_new.tif

轉換完畢後執行 docker 開始計算 terrain 這個步驟會非常久

sudo docker run -it -v /home/gg/test_terrain:/data tumgis/ctb-quantized-mesh
ctb-tile -f Mesh -C -N -o ./tw ./srtm_61_08_new.tif

最後就是新版才有的 -l 參數,他會生成一個 layer.json,一定要有這個 json 檔 Cesium 才可以讀取到 terrain 的結構

ctb-tile -f Mesh -C -N -o ./tw -l ./srtm_61_08_new.tif

接著需要寫一些 C# 的 code 並且把編譯完成的 dll 放在需要佈署的網站的 bin 目錄裡面,這個地方是最關鍵的,卡了很久,可以參考這篇說明 (如果照著他的設定 web.config 會錯誤)。

最主要的原理就是任何 web server 在接收 request 為 terrain 檔時需要在 header 加上 Content-Encoding gzip

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace AddZipHeaderModule
{
    public class ZipHeaderModule : IHttpModule
    {
        public void Dispose()
        {
            //do nothing
            //throw new NotImplementedException();
        }

        public void Init(HttpApplication context)
        {
            context.EndRequest += Context_EndRequest;
        }

        private void Context_EndRequest(object sender, EventArgs e)
        {
            var context = sender as HttpApplication;
            string fileExtension = context.Request.CurrentExecutionFilePathExtension;
            if (fileExtension.Length >= 8)
            {
                if (fileExtension.Substring(0, 8) == ".terrain")
                {
                    context.Response.AddHeader("Content-Encoding", "gzip");
                }
            }
            
        }
    }
}

搞定後要設定 web.config 如下,最後就大功告成了

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
		<validation validateIntegratedModeConfiguration="false" />
		<modules runAllManagedModulesForAllRequests="true">
			<add name="AddZipHeaderModule" type="AddZipHeaderModule.ZipHeaderModule" />
		</modules>
        <httpProtocol>
            <customHeaders>
                <remove name="Access-Control-Allwo-Origin" />
                <add name="Access-Control-Allow-Origin" value="*" />
            </customHeaders>
        </httpProtocol>
    <staticContent>
      <remove fileExtension=".czml" />
      <mimeMap fileExtension=".czml" mimeType="application/json" />
      <remove fileExtension=".glsl" />
      <mimeMap fileExtension=".glsl" mimeType="text/plain" />
      <remove fileExtension=".b3dm" />
      <mimeMap fileExtension=".b3dm" mimeType="application/octet-stream" />
      <remove fileExtension=".pnts" />
      <mimeMap fileExtension=".pnts" mimeType="application/octet-stream" />
      <remove fileExtension=".i3dm" />
      <mimeMap fileExtension=".i3dm" mimeType="application/octet-stream" />
      <remove fileExtension=".cmpt" />
      <mimeMap fileExtension=".cmpt" mimeType="application/octet-stream" />
      <remove fileExtension=".gltf" />
      <mimeMap fileExtension=".gltf" mimeType="model/gltf+json" />
      <remove fileExtension=".bgltf" />
      <mimeMap fileExtension=".bgltf" mimeType="model/gltf-binary" />
      <remove fileExtension=".glb" />
      <mimeMap fileExtension=".glb" mimeType="model/gltf-binary" />
      <remove fileExtension=".json" />
      <mimeMap fileExtension=".json" mimeType="application/json" />
      <remove fileExtension=".geojson" />
      <mimeMap fileExtension=".geojson" mimeType="application/json" />
      <remove fileExtension=".topojson" />
      <mimeMap fileExtension=".topojson" mimeType="application/json" />
      <remove fileExtension=".woff" />
      <mimeMap fileExtension=".woff" mimeType="application/font-woff" />
      <remove fileExtension=".woff2" />
      <mimeMap fileExtension=".woff2" mimeType="application/font-woff2" />
      <remove fileExtension=".kml" />
      <mimeMap fileExtension=".kml" mimeType="application/vnd.google-earth.kml+xml" />
      <remove fileExtension=".kmz" />
      <mimeMap fileExtension=".kmz" mimeType="application/vnd.google-earth.kmz" />
      <remove fileExtension=".svg" />
      <mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
      <remove fileExtension=".terrain" />
      <mimeMap fileExtension=".terrain" mimeType="application/vnd.quantized-mesh" />
      <remove fileExtension=".ktx" />
      <mimeMap fileExtension=".ktx" mimeType="image/ktx" />
      <remove fileExtension=".crn" />
      <mimeMap fileExtension=".crn" mimeType="image/crn" />
      <remove fileExtension=".webm" />
      <mimeMap fileExtension=".webm" mimeType="video/webm" />
    </staticContent>
    </system.webServer>
</configuration>

後記

有種相見恨晚的感覺

http://sunhongyi.com/

Cesium 初體驗

載點

https://cesiumjs.org/downloads/

下載回來最主要會引用的是 Build/CesiumUnminified 或 Build/Cesium 這兩個資料夾內的檔案。因為我是使用 vscode 進行開發所以就不用他預設的 nodejs 啟動方式,而直接使用 live server。

值得一提的是我以為偷懶把通用電子地圖直接丟到 geoserver 的 webapps 目錄底下就可以動了,但卻出現了跨域魔咒,可見 geoserver 的跨域設定只對自己有效,對偷懶丟到 webapps 的是不起作用的,最後只好偷懶丟到 vscode 開發目錄底下讓他形成同個 domain 才順利搞定。

Access to XMLHttpRequest at ‘http://127.0.0.1:8080/TGOS/4/13/5.png’ from origin ‘http://127.0.0.1:5500’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

以下為我串接通用電子地圖的結果。

<!DOCTYPE html>
<html lang="en">

<head>
  <!-- Use correct character set. -->
  <meta charset="utf-8">
  <!-- Tell IE to use the latest, best version. -->
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <!-- Make the application on mobile take up the full browser screen and disable user scaling. -->
  <meta name="viewport"
    content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
  <title>喇低喇賽</title>

  <style>
    @import url(./CesiumUnminified/Widgets/widgets.css);

    html,
    body,
    #cesiumContainer {
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
      overflow: hidden;
    }

    /*去除 credits*/
    .cesium-widget-credits {
      display: none !important;
      visibility: hide !important;
    }


    #creditContainer {
      position: absolute;
      top: 0;
      left: 0;
      z-index: 3;
      visibility: visible;


      font-family: 微軟正黑體;
      font-size: 36pt;
      border-radius: 5px;
      padding: 3px;
      margin: 5px;
      color: white;
    }
  </style>
</head>

<body>
  <!--放logo用的-->
  <div id="creditContainer">
    喇低喇賽
  </div>

  <!--cesium本體-->
  <div id="cesiumContainer"></div>

  <script src="./CesiumUnminified/Cesium.js"></script>
  <script src="./jquery/jquery-3.3.1.js"></script>

  <script>
    //基本圖層
    imageryProviderViewModels = [];
    imageryProviderViewModels.push(new Cesium.ProviderViewModel({
      name: '開放街圖',
      iconUrl: Cesium.buildModuleUrl('Widgets/Images/ImageryProviders/openStreetMap.png'),
      tooltip: '通用電子地圖',
      creationFunction: function () {
        var custom = new Cesium.UrlTemplateImageryProvider({
          url: 'http://127.0.0.1:5500/TGOS/{z}/{x}/{y}.png'
        });

        return custom;
      }
    }));

    var viewer = new Cesium.Viewer('cesiumContainer', {

      animation: false,
      baseLayerPicker: true,
      fullscreenButton: false,
      geocoder: false,
      homeButton: true,
      infoBox: true,
      sceneModePicker: false,
      selectionIndicator: true,
      timeline: false,
      navigationHelpButton: false,
      navigationInstructionsInitiallyVisible: false,
      scene3DOnly: true,
      clockViewModel: null,

      imageryProviderViewModels: imageryProviderViewModels
      //creditContainer: 'creditContainer'
    });

    viewer.camera.setView({
      destination: Cesium.Cartesian3.fromDegrees(120.827262, 24.608264, 1550000)
    });
  </script>
</body>

</html>