緣由
因為各種各種wmts地圖客戶端產品的標准的支持不一定是一致的,就像ArcGIS不同版本加載WMTS圖層的時候計算的規則就有差別(米和經緯度之間轉換系數的區別),導致會出現適應各個客戶端而出的WMTS服務,里面的ScaleDenominator值有細微的差別。
例如都是Google經緯度(天地圖經緯度直投),其切分規則是一樣的,但其DPI不一致導致計算的ScaleDenominator是不一致的。
雖然OGC標准里面對DPI大小的定義就是90.714,但是國內天地圖使用的DPI標准為96,這里也有一個計算的差值。DPI不同對應的也就是每個像素的大小不一致,也就是分辨率不一致,造成瓦片的行列號計算有差異。
QGIS是支持OGC WMTS標准的,其DPI的大小就是90.714(像素大小0.00028)。為了讓QGIS能夠支持DPI為96的WMTS服務,需要對QGIS進行一點修改。
解決過程
為了解決這個問題,看了一下QGIS的源碼,相關的定義如下:
文件qgis\src\providers\wms\qgswmscapabilities.cpp 1382行前后
m.tileWidth = e1.firstChildElement( QStringLiteral( "TileWidth" ) ).text().toInt();
m.tileHeight = e1.firstChildElement( QStringLiteral( "TileHeight" ) ).text().toInt();
m.matrixWidth = e1.firstChildElement( QStringLiteral( "MatrixWidth" ) ).text().toInt();
m.matrixHeight = e1.firstChildElement( QStringLiteral( "MatrixHeight" ) ).text().toInt();
// the magic number below is "standardized rendering pixel size" defined
// in WMTS (and WMS 1.3) standard, being 0.28 pixel
m.tres = m.scaleDenom * 0.00028 / metersPerUnit;
QgsDebugMsg( QString( " %1: scale=%2 res=%3 tile=%4x%5 matrix=%6x%7 topLeft=%8" )
.arg( m.identifier )
.arg( m.scaleDenom ).arg( m.tres )
.arg( m.tileWidth ).arg( m.tileHeight )
.arg( m.matrixWidth ).arg( m.matrixHeight )
.arg( m.topLeft.toString() )
);
s.tileMatrices.insert( m.tres, m );
這里可以看到通過ScaleDenominator計算瓦片分辨率的過程,這里使用的像素大小是0.00028。
因為像素大小0.00028的計算是0.0254/DPI(這個原理網上搜就有了),所以我們可以算出DPI為96時候的像素大小為0.000264583。
如果這里我們把0.00028修改為0.000264583再編譯就可以支持天地圖的了。這里我不打算改它,因為編譯太麻煩。
這里我們直接改QGIS的二進制程序文件好了。
使用十六進制編輯器打開C:\Program Files\QGIS 3.0\apps\qgis\plugins目錄下在wmsprovider.dll(最好先備份)。
然后搜索0.00028

整個文件就這一處使用了0.00028這個數值,這里直接將其修改為0.000264583保存即可。
再打開QGIS就可以按照96的DPI加載WMTS和WMS圖層了。
還可以保留着兩個dll文件,寫一個批處理來決定加載的時候使用哪一個來運行。
ArcGIS支持DPI 96
ARCGIS中一樣沒有找到相關的設置,猜測也是寫到代碼里面了。聽聞國內某些單位購買的ArcGIS是支持96的DPI的,但我們沒有。
ArcGIS安裝目錄下找到WMSLayer.dll和WMSService.dll的文件,還是一樣的套路,搜索0.00028修改為0.000264583即可。

