記一次 CesiumJS 中非 4326/3857 WMTS 數據的加載


記一次 CesiumJS 中 WMTS 數據的加載


CesiumJS 能用的 WMTS 目前只支持兩種切片方案(TilingScheme):

  • 0 級瓦片有 2 個的 GeographicTilingScheme
  • 0 級瓦片只有 1 個的 WebMercatorTilingScheme

光說很抽象,上圖:

image

image

0 級瓦片有 2 個的投影,是直接以經緯度數值展平成平面,衆所周知:

\[緯度跨度:經度跨度 = 180:360 = 1:2 \]

所以 GeographicTilingScheme 的樣子就是一個一比二的 矩形,剛好就在 0 級瓦片時有兩個,後續就按常規的四叉樹切分即可。而 WebMercator 投影后的座標系 xy 值域是 \([-20037508.34,20037508.34]^2\),是一個 正方形,所以可以按單個 0 級瓦片進行四叉樹切分。

以上,是技術前提,CesiumJS 只能支持這兩種切分方案,也就是說,我國常用的其他投影方法,例如高斯投影、蘭伯特投影等是不支持的,主要是形狀不太滿足構造四叉樹瓦片。

既有 WMTS 的現狀

需求是這樣的,這一份 WMTS 的起切等級並不是 0 級,通過觀察能力文檔,我可以得出如下幾個結論:

  • 它是自定義切片方案,但滿足 GeographicTilingScheme 方案的形狀,是 1:2 的矩形
  • 它的 0 級瓦片與 EPSG:4326 的 0 級瓦片不同
  • 它的 0 級瓦片分辨率與 EPSG:4326 的 9 級瓦片相同
  • 它的 0 級瓦片共有 1024 列 × 512 行,而 EPSG:4326 是 2 列 × 1 行,是 4326 的 \(512^2\)

如果讀者足夠敏銳,可以得知這個 WMTS 的起切瓦片實際上幾乎就是 4326 的第 9 級瓦片:

<TileMatrixLimits>
    <TileMatrix>EPSG:4490:0</TileMatrix>
    <MinTileRow>190</MinTileRow>
    <MaxTileRow>192</MaxTileRow>
    <MinTileCol>835</MinTileCol>
    <MaxTileCol>838</MaxTileCol>
</TileMatrixLimits>eMatrix>

比對廣東省省界數據的 4326 座標系第 9 級瓦片陣(TileMatrix)定義:

<TileMatrixLimits>
    <TileMatrix>EPSG:4326:9</TileMatrix>
    <MinTileRow>183</MinTileRow>
    <MaxTileRow>198</MaxTileRow>
    <MinTileCol>823</MinTileCol>
    <MaxTileCol>845</MaxTileCol>
</TileMatrixLimits>

最大最小行列號略有差別,是因爲數據所跨的範圍略有不同,前者範圍較後者(廣東省省界)小。

我只能說比較慶幸,這樣的 WMTS 基本還是可以滿足加載要求的,現有 API 可以滿足加載調整。

兵來將擋水來土掩 - 問題解決

先給代碼,然後解釋:

const provider = new WebMapTileServiceImageryProvider({
  url: new Resource({
    url: 'http://127.0.0.1/server/wmts', // 簡寫 url,會意即可
    headers: {
      'tk': 'aaaaaaaaa', // 數據保密,需要傳遞 token
    }
  }),
  tileMatrixSetID: 'EPSG:4490',
  format: 'image/png',
  tileMatrixLabels: Object.keys(new Array(11).fill(0)).map(v => `EPSG:4490:${v}`),
  layer: 'demo',
  rectangle: Rectangle.fromDegrees(113.75549, 22.383494, 114.662777, 22.888641),
  style: "",
  tilingScheme: new GeographicTilingScheme({
    numberOfLevelZeroTilesX: 1024,
    numberOfLevelZeroTilesY: 512,
  })
})
viewer.imageryLayers.addImageryProvider(provider)

url 參數

WebMapTileServiceImageryProviderurl 可以是兩種類型的值:stringResource,這個 WMTS 數據需要在所有請求頭中加上訪問令牌(token),所以我選擇創建一個 Resource 對象來傳遞 token。

tileMatrixSetID 和 layer 參數

這裏設爲 EPSG:4490,指的是能力文檔中該圖層(layer: 'demo')下的 <TileMatrixSetLink></TileMatrixSetLink> 的瓦片陣集ID(TileMatrixSetID):

<TileMatrixSetLink>
    <TileMatrixSet>EPSG:4490</TileMatrixSet>
    <TileMatrixSetLimits><!-- ... --></TileMatrixSetLimits>
</TileMatrixSetLink>

tileMatrixLabels 參數

這個沒什麼好說的,就是每一層瓦片陣的訪問標籤,我這裏使用 JavaScript 的數組語法糖快速生成了 11 級(0到10,共11層)瓦片陣的 ID,即:

Object.keys(new Array(11).fill(0)).map(v => `EPSG:4490:${v}`)

// ["EPSG:4490:0", "EPSG:4490:1", ..., "EPSG:4490:10"]

這樣,網絡請求瓦片的鏈接:

http://127.0.0.1/server/wmts?tilematrix=EPSG%3A4490%3A1&layer=ZT%3ATDYT&style=&tilerow=382&tilecol=1671&tilematrixset=EPSG%3A4490&format=image%2Fpng&service=WMTS&version=1.0.0&request=GetTile

中的 tilematrix 參數的值:EPSG:4490:1 就是正確的了。tileMatrixLabels 數組默認是數字 0 ~ 最大等級,如果圖層在能力文檔中定義的 tilematrix 的 ID 不是 0 ~ 最大等級的數字的話,就需要這樣構造一個數組來告訴 CesiumJS,要以什麼樣的 tilematrix 發出請求。

rectangle 參數

加上這個參數,就可以最大優化 WMTS 的請求性能,如果不加這個參數限制請求範圍,就會取相機視角下的所有篩選到的瓦片,這對這個例子十分有效,主要還是要加上最後一個參數:

tilingScheme 參數

上文已經提及,這個 WMTS 的切片方案與 4326 的是幾乎一致的,只不過其 0 級相當於 4326 的 9 級。

那麼,當 CesiumJS 發出第 0 級瓦片請求時,默認的 TilingScheme 是隻有 1 行 2 列的瓦片的,但是在能力文檔中,我注意到了一個定義:

<Contents>
    <TileMatrixSet>
        <ows:Identifier>EPSG:4490</ows:Identifier>
        <ows:SupportedCRS>urn:ogc:def:crs:EPSG::4490</ows:SupportedCRS>
        <TileMatrix>
            <ows:Identifier>EPSG:4490:0</ows:Identifier>
            <ScaleDenominator>545978.7734655447</ScaleDenominator>
            <TopLeftCorner>90.0 -180.0</TopLeftCorner>
            <TileWidth>256</TileWidth>
            <TileHeight>256</TileHeight>
            <MatrixWidth>1024</MatrixWidth>
            <MatrixHeight>512</MatrixHeight>
        </TileMatrix>
        <!-- ... -->
    <TileMatrixSet>
</Contents>

這是這個 WMTS 服務下的 EPSG:4490 的瓦片陣集(TileMatrixSet)的第 0 個瓦片陣(TileMatrix)的定義,這裏定義了幾個比較重要的參數:

  • Identifier:瓦片陣的 ID
  • ScaleDenominator:比例(分母)
  • TileWidth / TileHeight:該瓦片陣的瓦片的像素寬高
  • MatrixWidth / MatrixHeight:該瓦片陣的瓦片行列數

要用於前端的就是最後一個,行列數:

new GeographicTilingScheme({
  numberOfLevelZeroTilesX: 1024,
  numberOfLevelZeroTilesY: 512,
})

這樣就能在 CesiumJS 發出第 0 級瓦片請求時,行列號能與 WMTS 的瓦片行列號對應上了,TilingScheme API 的這兩個參數就是這麼個用法,前提是切片的形狀滿足 CesiumJS 支持的這兩個:GeographicTilingSchemeWebMercatorTilingScheme

小結

通過這次實踐,我又進一步學習了老舊但是又不得不用的 WMTS 規範,以及這種非標準 4326、3857 切片數據的加載細節,那就是精確地根據能力文檔中各項參數(瓦片陣集的選擇、瓦片陣的範圍等),配合 JsAPI 控制發出準確的請求。

如果真遇上那種不滿足 CesiumJS 這倆切片方案的,估計就難搞了,水平有限。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章