
Leaflet加载WMTS服务踩坑全记录从‘不李姐’到成功叠加天地图与CGCS2000图层第一次尝试在Leaflet中加载WMTS服务时那种明明照着文档做却死活不显示的挫败感至今记忆犹新。特别是当需要处理非标准坐标系如CGCS2000时官方文档的示例代码仿佛成了摆设各种未提及的细节问题接踵而至。本文将还原一个真实项目中的完整踩坑历程重点不是给你一段可以无脑复制的代码而是带你理解那些官方文档没讲清楚的关键点。1. 坐标系一切问题的起点Leaflet默认只支持EPSG:3857Web墨卡托和EPSG:4326WGS84经纬度两种坐标系这在实际项目中远远不够。当我们需要使用CGCS2000坐标系EPSG:4490时必须自定义CRSCoordinate Reference System。1.1 自定义CRS的陷阱官方推荐使用proj4leaflet插件来定义自定义坐标系但实际操作中你会发现// 理论上应该可行的proj4leaflet方案但实际可能不工作 L.Proj.CRS(EPSG:4490, { origin: [-180, 90], resolutions: [ // 分辨率数组 ], bounds: L.bounds([-180, -90], [180, 90]) });而实践中下面这种看似野路子的方案反而更可靠// 实际可用的自定义CRS方案 L.CRS.CustomEPSG4490 L.extend({}, L.CRS.Earth, { code: EPSG:4490, projection: L.Projection.LonLat, transformation: new L.Transformation(1 / 180, 1, -1 / 180, 0.5), scale: function (zoom) { return 256 * Math.pow(2, zoom - 1); } });关键差异在于transformation参数决定了坐标如何映射到屏幕像素scale函数控制不同缩放级别下的瓦片显示比例projection指定使用经纬度投影而非墨卡托2. 天地图服务的玄机天地图WMTS服务有两个容易混淆的版本参数地理坐标系版本投影坐标系版本服务地址img_cimg_w矩阵集cw适用CRSEPSG:4490EPSG:3857一个典型的坑是当你使用自定义的EPSG:4490 CRS时却错误地请求了img_w服务结果必然是空白一片。正确的图层初始化应该是const img_c L.tileLayer( http://t0.tianditu.gov.cn/img_c/wmts?SERVICEWMTSREQUESTGetTileVERSION1.0.0LAYERimgSTYLEdefaultTILEMATRIXSETcFORMATtilesTILEMATRIX{z}TILEROW{y}TILECOL{x}tk${tdtKey} );3. WMTS插件的水有多深加载自定义WMTS服务时leaflet.wmts插件是必备工具但这里藏着几个大坑3.1 插件版本差异官网下载版可能需要手动修改源码才能支持非标准CRSNPM安装版通常已经包含必要的补丁# 推荐安装方式 npm install leaflet.wmts3.2 矩阵集配置CGCS2000坐标系需要明确定义矩阵IDlet matrixIds []; for (let i 0; i 22; i) { matrixIds[i] { identifier: i, topLeftCorner: new L.LatLng(90, -180) }; }3.3 图层参数解析WMTS图层的每个参数都至关重要let wmtsLayer new L.TileLayer.WMTS(url, { layer: 图层名称, style: , tilematrixSet: CGCS2000, format: image/png, crs: L.CRS.CustomEPSG4490, matrixIds: matrixIds });4. 完整实现方案将所有碎片整合起来一个完整的Vue组件实现如下template div idmap/div /template script setup import { onMounted } from vue; import L from leaflet; import leaflet/dist/leaflet.css; import leaflet.wmts; const tdtKey 你的天地图密钥; // 自定义CRS L.CRS.CustomEPSG4490 L.extend({}, L.CRS.Earth, { code: EPSG:4490, projection: L.Projection.LonLat, transformation: new L.Transformation(1 / 180, 1, -1 / 180, 0.5), scale: function (zoom) { return 256 * Math.pow(2, zoom - 1); } }); // 准备WMTS矩阵集 let matrixIds []; for (let i 0; i 22; i) { matrixIds[i] { identifier: i, topLeftCorner: new L.LatLng(90, -180) }; } const initMap () { // 天地图底图 const img_c L.tileLayer( http://t0.tianditu.gov.cn/img_c/wmts?SERVICEWMTSREQUESTGetTileVERSION1.0.0LAYERimgSTYLEdefaultTILEMATRIXSETcFORMATtilesTILEMATRIX{z}TILEROW{y}TILECOL{x}tk${tdtKey} ); const cia_c L.tileLayer( http://t0.tianditu.gov.cn/cia_c/wmts?SERVICEWMTSREQUESTGetTileVERSION1.0.0LAYERciaSTYLEdefaultTILEMATRIXSETcFORMATtilesTILEMATRIX{z}TILEROW{y}TILECOL{x}tk${tdtKey} ); const layers L.layerGroup([img_c, cia_c]); // 初始化地图 let map L.map(map, { center: [24.889157, 102.839443], zoom: 6, maxZoom: 18, minZoom: 0, layers: [layers], crs: L.CRS.CustomEPSG4490 }); // 添加WMTS图层 let url http://your-service/wmts; let wmtsLayer new L.TileLayer.WMTS(url, { layer: your-layer, style: , tilematrixSet: CGCS2000, format: image/png, crs: L.CRS.CustomEPSG4490, matrixIds: matrixIds }); map.addLayer(wmtsLayer); }; onMounted(() { initMap(); }); /script style scoped #map { height: 100vh; width: 100%; } /style5. 调试技巧与常见问题当图层无法显示时按以下步骤排查检查控制台错误查看是否有CORS或404错误验证WMTS请求URL直接在浏览器地址栏测试坐标系一致性检查确保CRS与WMTS服务匹配确认tilematrixSet参数正确缩放级别问题检查minZoom/maxZoom设置确认矩阵ID与缩放级别对应特别提醒天地图服务需要有效的密钥且免费版有访问频率限制经过这些折腾最大的体会是Leaflet的灵活性是把双刃剑。它给了你足够的自由去实现各种需求但也意味着你需要自己处理很多底层细节。当标准方案不奏效时社区的各种非官方解决方案往往能救命这就是开源生态的魅力所在。