CesiumJS 能用的 WMTS 目前只支援兩種切片方案(TilingScheme
):
- 0 級瓦片有 2 個的
GeographicTilingScheme
- 0 級瓦片只有 1 個的
WebMercatorTilingScheme
光說很抽象,上圖:
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 引數
WebMapTileServiceImageryProvider
的 url
可以是兩種型別的值:string
或 Resource
,這個 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
:瓦片陣的 IDScaleDenominator
:比例(分母)TileWidth
/TileHeight
:該瓦片陣的瓦片的畫素寬高MatrixWidth
/MatrixHeight
:該瓦片陣的瓦片行列數
要用於前端的就是最後一個,行列數:
new GeographicTilingScheme({
numberOfLevelZeroTilesX: 1024,
numberOfLevelZeroTilesY: 512,
})
這樣就能在 CesiumJS 發出第 0 級瓦片請求時,行列號能與 WMTS 的瓦片行列號對應上了,TilingScheme
API 的這兩個引數就是這麼個用法,前提是切片的形狀滿足 CesiumJS 支援的這兩個:GeographicTilingScheme
、WebMercatorTilingScheme
小結
透過這次實踐,我又進一步學習了老舊但是又不得不用的 WMTS 規範,以及這種非標準 4326、3857 切片資料的載入細節,那就是精確地根據能力文件中各項引數(瓦片陣集的選擇、瓦片陣的範圍等),配合 JsAPI 控制發出準確的請求。
如果真遇上那種不滿足 CesiumJS 這倆切片方案的,估計就難搞了,水平有限。