SuperMap iClient for MapboxGL 加载 EPSG:4490 WMTS 服务瓦片错乱排查思路
作者Carlo目录一、问题现象二、根因分析2.1 WMTS 的标准地理切片方案2.2 MapboxGL 的 CRS 定义行为2.3 错乱的根本原因三、解决办法3.1 方案一KVP 请求格式3.2 方案二RESTful zxy 格式四、两种方案对比五、延伸如何确认你的服务也需要修正一、问题现象使用 SuperMap iClient for MapboxGLmapbox-gl-enhance加载 EPSG:4490CGCS2000坐标系的 WMTS 服务时地图显示瓦片错乱——要么完全空白要么在错误位置显示了其他区域的瓦片和实际坐标对不上。二、根因分析2.1 WMTS 的标准地理切片方案先看这个 WMTS 服务的GetCapabilities它的 TileMatrixSet 定义如下WMTS Level瓦片网格 (宽×高)分辨率 (°/px)总瓦片数02×10.703125214×20.3515625828×40.1757812532316×80.087890625128432×160.0439453125512564×320.021972656252048这是OGC 标准的地理坐标系切片方案Level 0 的覆盖范围是[-180°, 180°] × [-90°, 90°]全球Level 0 是 2 列 × 1 行每片 256×256 像素分辨率 360° ÷ (256 × 2) 0.703125°/px关键特征Level 0 就已经是 2×1 的网格了。2.2 MapboxGL 的 CRS 定义行为当通过mapboxgl.CRS或crs: EPSG:4490定义地理坐标系时mapbox-gl-enhance 内部会从 zoom 0 的 1×1 格网开始计算分辨率MapboxGL Zoom瓦片网格 (宽×高)分辨率 (°/px)01×11.4062512×10.70312524×20.351562538×40.17578125………一张表说清楚这个1 偏移MapboxGL Zoom分辨率WMTS Level分辨率瓦片网格01.40625———10.70312500.7031252×120.351562510.35156254×230.1757812520.175781258×440.08789062530.08789062516×850.043945312540.043945312532×1660.0219726562550.0219726562564×322.3 错乱的根本原因MapboxGL zoom L1 的分辨率和瓦片网格才等于 WMTS level L。当你直接写tiles: [.../{z}/{y}/{x}.png]时MapboxGL zoom 3 → 请求.../3/1/2.png期望 WMTS level 3 的瓦片但 MapboxGL zoom 3 的分辨率 WMTS level 2 的分辨率本该拿 level 2 的瓦片却拿了 level 3 的瓦片 →场景范围和瓦片内容不匹配MapboxGL zoom 3 需要 level 2 的瓦片来拼出正确的地图但你给它的是 level 3 的瓦片它们的位置和缩放比例完全对不上自然就错乱了。三、解决办法核心思路用transformRequest拦截每一个瓦片请求把 URL 中的层级号减 1。同时调整minZoom/maxZoomMapminZoom: 1→ 对应 WMTS level 0全球可见MapmaxZoom: 6→ 对应 WMTS level 5最大细节3.1 方案一KVP 请求格式WMTS 的 KVPKey-Value Pair格式 URL 长这样?serviceWMTSrequestGetTile...tilematrix{z}tilerow{y}tilecol{x}直接在tilematrix{z}的参数值上做替换!DOCTYPEhtmlhtmllangzh-CNheadmetacharsetUTF-8/metanameviewportcontentwidthdevice-width, initial-scale1.0/titleWMTS EPSG:4490 - KVP 格式/titlelinkhrefhttps://iclient.supermap.io/web/libs/mapbox-gl-js-enhance/1.12.1-11/mapbox-gl-enhance.cssrelstylesheet/scriptsrchttps://iclient.supermap.io/web/libs/mapbox-gl-js-enhance/1.12.1-11/mapbox-gl-enhance.js/scriptstylebody{margin:0;padding:0;}#map{position:absolute;top:0;bottom:0;width:100%;}/style/headbodydividmap/divscript// 1. 定义 EPSG:4490 坐标系varcrs4490newmapboxgl.CRS(EPSG:4490,GEOGCS[China Geodetic Coordinate System 2000,DATUM[China_2000,SPHEROID[CGCS2000,6378137,298.257222101]],PRIMEM[Greenwich,0],UNIT[degree,0.0174532925199433],AUTHORITY[EPSG,4490]],[-180,-90,180,90]);// 2. 创建地图varmapnewmapboxgl.Map({container:map,style:{version:8,sources:{},layers:[]},crs:crs4490,center:[104.0,36.0],zoom:3,minZoom:1,// 对应 WMTS level 0maxZoom:6,// 对应 WMTS level 5// ★ 核心transformRequest 拦截tilematrix z - 1transformRequest:function(url,resourceType){if(resourceTypeTileurl.indexOf(GetTile)0){varmatchurl.match(/tilematrix(\d)/);if(match){varnewZparseInt(match[1],10)-1;return{url:url.replace(/tilematrix\d/,tilematrixnewZ)};}}return{url:url};}});// 3. on(load) 后添加 WMTS 图层map.on(load,function(){varwmtsUrlhttp://localhost:8090/iserver/services/map-ugcv5-Country4490World/wmts100?serviceWMTSrequestGetTileversion1.0.0layerCountry4490WorldstyledefaulttilematrixSetCustom_Country4490Worldformatimage/pngtilematrix{z}tilerow{y}tilecol{x};map.addSource(wmts,{type:raster,tiles:[wmtsUrl],tileSize:256});map.addLayer({id:wmts-layer,type:raster,source:wmts});});map.addControl(newmapboxgl.NavigationControl(),bottom-right);/script/body/html核心逻辑tilematrix3→transformRequest→tilematrix2请求的就是正确的 WMTS level。3.2 方案二RESTful zxy 格式WMTS 的 RESTful zxy 格式 URL 长这样.../TileMatrixSet/{z}/{y}/{x}.png正则匹配最后的/{z}/{y}/{x}.png把 z 减 1!DOCTYPEhtmlhtmlheadmetacharsetutf-8/metanameviewportcontentinitial-scale1,maximum-scale1,user-scalableno/titleWMTS EPSG:4490 - RESTful zxy 格式/titlescriptsrc../js/include-web.js/scriptscriptincludemapbox-gl-enhancesrc../../dist/mapboxgl/include-mapboxgl.js/scriptstylebody{margin:0;padding:0;}#map{position:absolute;top:0;bottom:0;width:100%;}/style/headbodydividmap/divscriptvarmapnewmapboxgl.Map({container:map,style:{version:8,sources:{raster-tiles:{type:raster,tileSize:256,tiles:[http://localhost:8090/iserver/services/map-ugcv5-Country4490World/wmts100/Country4490World/default/Custom_Country4490World/{z}/{y}/{x}.png]}},layers:[{id:simple-tiles,type:raster,source:raster-tiles}]},crs:EPSG:4490,center:[105.87,30.16],zoom:3,minZoom:1,// 对应 WMTS level 0maxZoom:6,// 对应 WMTS level 5// ★ 核心transformRequest 拦截把 /{z}/{y}/{x}.png 中的 z 减 1transformRequest:function(url,resourceType){if(resourceTypeTileurl.indexOf(wmts100)0){return{url:url.replace(/\/(\d)\/(\d)\/(\d)\.png$/,function(match,z,y,x){return/(parseInt(z,10)-1)/y/x.png;})};}return{url:url};}});map.addControl(newmapboxgl.NavigationControl(),top-left);/script/body/html核心逻辑/3/1/2.png→transformRequest→/2/1/2.png。⚠️ 注意不要在replace回调外面直接用z、y、x变量它们的作用域只在回调函数内部。需要日志的话在回调里先赋值给外部变量。四、两种方案对比对比维度KVP 格式RESTful zxy 格式URL 模板?tilematrix{z}tilerow{y}tilecol{x}/{z}/{y}/{x}.pngtransformRequest 匹配方式正则tilematrix(\d)正则/(\d)/(\d)/(\d).png$CRS 定义new mapboxgl.CRS(...)crs: EPSG:4490字符串图层添加map.on(load)后 addSourcestyle.sources 内联定义适用场景通用 WMTS 规范参数清晰简洁适合 RESTful API两种方案本质一样——就是让 MapboxGL 实际请求的 WMTS 层级比它自己认为的低一级实现分辨率对齐。五、延伸如何确认你的服务也需要修正不是所有 WMTS 服务都需要transformRequest。判断方法查GetCapabilities中 TileMatrixSet level 0 的MatrixWidth和MatrixHeight如果 level 0 是2×1标准地理切片方案需要修正MapboxGL zoom 0 是 1×1如果 level 0 是1×1不需要修正对齐一致如果 level 0 是1×2Google 墨卡托方案用了 Web 墨卡托场景一般不需要修正一句话总结标准地理坐标系切片方案中WMTS 的 level 0 就是 2×1 网格而 MapboxGL 的地图从 zoom 0 1×1 开始。这一级偏移需要transformRequest把所有请求的层级减 1 来弥补。