公司动态

OpenLayers WMTS加载天地图EPSG:4326瓦片与BD09坐标转换实战(07)

📅 2026/8/6 6:12:17
OpenLayers WMTS加载天地图EPSG:4326瓦片与BD09坐标转换实战(07)
天地图同时提供EPSG:4326经纬度投影和EPSG:3857Web墨卡托投影两种瓦片服务。使用EPSG:4326投影时OpenLayers需要配置自定义WMTS数据源和瓦片网格。本文基于实际项目代码详细讲解如何在Vue3 OpenLayers项目中加载天地图EPSG:4326瓦片并将百度BD09坐标正确投放到底图上。一、EPSG:4326 vs EPSG:3857如何选择1.1 两种投影的区别特性EPSG:4326EPSG:3857坐标类型经纬度度墨卡托米坐标范围[-180, -90] ~ [180, 90]约[-20037508, -20037508] ~ [20037508, 20037508]形状变形高纬度拉伸等角投影面积变形瓦片矩阵集matrixSet: cmatrixSet: wURL标识vec_c/cva_cvec_w/cia_w1.2 选择建议数据源为经纬度使用EPSG:4326坐标无需额外转换与标准Web地图对接使用EPSG:3857兼容性更好需要精确面积/距离计算使用EPSG:4326二、EPSG:4326坐标转换2.1 转换链路天地图EPSG:4326瓦片基于标准WGS84坐标系BD09坐标的转换链路非常简洁BD09百度经纬度→ EPSG:4326WGS84经纬度只需一步转换无需再转换为墨卡托坐标。2.2 转换实现importgcoordfromgcoord// BD09 → EPSG:4326WGS84经纬度constbd09ToEPSG4326(coord)gcoord.transform(coord,gcoord.BD09,gcoord.EPSG4326)// 使用示例constcenterbd09ToEPSG4326([116.404,39.915])// 结果: [116.3707xx, 39.8853xx]EPSG:4326经纬度单位度关键区别EPSG:4326模式下地图中心点直接使用经纬度坐标单位度而EPSG:3857模式下使用墨卡托坐标单位米。三、WMTS数据源封装3.1 为什么需要自定义WMTS源天地图EPSG:4326瓦片使用WMTS协议与标准XYZ瓦片不同WMTS需要指定瓦片矩阵集matrixSet需要根据投影计算分辨率数组需要配置瓦片网格TileGridOpenLayers的ol/source/WMTS需要完整的瓦片网格配置不能像XYZ那样简单使用URL模板。3.2 createWMTSSource封装import{WMTS}fromol/sourceimport{getasolProjGet}fromol/projimport{getWidth}fromol/extentimportWMTSTileGridfromol/tilegrid/WMTS/** * 创建天地图WMTS数据源 * param {Object} params - 配置参数 * param {string} params.projection - 坐标投影默认 EPSG:3857 * param {number} params.maxZoom - 最大缩放级别默认 23 * param {Array} params.origin - 坐标原点默认 [-180.0, 90.0] * param {number} params.tileSize - 瓦片大小默认 256 * returns {WMTS} WMTS数据源 */constcreateWMTSSource(params){const{projection:projectionEPSG:3857,maxZoom:maxZoom23,origin:origin[-180.0,90.0],tileSize:tileSize256,...options}params// 获取投影对象letProjectionolProjGet(projection)constprojectionExtentProjection.getExtent()// 计算投影范围宽度与瓦片大小的比值constsizegetWidth(projectionExtent)/tileSize// 生成分辨率数组和矩阵IDconstresolutions[]constmatrixIds[]for(leti1;imaxZoom;i){// 每一级分辨率 上一级分辨率 / 2resolutions.push(size/Math.pow(2,i))// 矩阵ID必须从 1 开始且为字符串格式matrixIds.push(String(i))}// 创建 WMTS 数据源returnnewWMTS({...options,projection:projection,tileGrid:newWMTSTileGrid({tileSize:tileSize,origin:origin,resolutions:resolutions,matrixIds:matrixIds,maxZoom:maxZoom})})}3.3 分辨率计算原理天地图EPSG:4326的瓦片分辨率计算基于投影范围projectionExtent [-180, -90, 180, 90] // EPSG:4326范围 size getWidth(extent) / tileSize 360 / 256 1.40625 第1级分辨率: 1.40625 / 2^1 0.703125 第2级分辨率: 1.40625 / 2^2 0.3515625 ... 第18级分辨率: 1.40625 / 2^18 ≈ 0.00000536每一级分辨率是上一级的一半缩放越高级别分辨率越精细。四、EPSG:4326 vs EPSG:3857 配置对比4.1 WMTS参数差异参数EPSG:4326EPSG:3857projectionEPSG:4326EPSG:3857matrixSetcworigin[-180.0, 90.0][0, 0]默认URL图层vec_c/cva_cvec_w/cia_w数据源类型ol/source/WMTSol/source/XYZ4.2 地图视图差异// EPSG:4326 模式中心点使用经纬度度view:newView({center:[116.404,39.915],// 直接使用经纬度zoom:10,projection:EPSG:4326})// EPSG:3857 模式中心点使用墨卡托米view:newView({center:[12958175,4862293],// 墨卡托坐标zoom:10,projection:EPSG:3857})4.3 坐标转换差异// EPSG:4326BD09 → EPSG:4326一步转换结果为经纬度constbd09To4326(coord)gcoord.transform(coord,gcoord.BD09,gcoord.EPSG4326)// EPSG:3857BD09 → EPSG:4326 → EPSG:3857两步转换结果为墨卡托constbd09To3857(coord){constwgs84gcoord.transform(coord,gcoord.BD09,gcoord.EPSG4326)returngcoord.transform(wgs84,gcoord.EPSG4326,gcoord.EPSG3857)}五、完整Vue3组件实现以下是基于项目ol-bd-tdtEPSG4326.vue的完整实现template div classwh100 refolMapRef/div /template script setup import {ref, onMounted} from vue import Map from ol/Map import View from ol/View import TileLayer from ol/layer/Tile import VectorLayer from ol/layer/Vector; import VectorSource from ol/source/Vector; import {GeoJSON} from ol/format; import gcoord from gcoord import {Circle as CircleStyle, Fill, Stroke, Style} from ol/style; import {WMTS} from ol/source; import {get as olProjGet} from ol/proj; import {getWidth} from ol/extent; import WMTSTileGrid from ol/tilegrid/WMTS; // 百度坐标(BD09) → EPSG:4326一步转换 const bd09ToEPSG4326 (coord) gcoord.transform(coord, gcoord.BD09, gcoord.EPSG4326) const bd09ToMercator (coord) bd09ToEPSG4326(coord) const olMapRef ref(null) /** * 创建天地图WMTS数据源 * 根据投影计算分辨率数组和瓦片网格 */ const createWMTSSource (params) { const { projection: projection EPSG:3857, maxZoom: maxZoom 23, origin: origin [-180.0, 90.0], tileSize: tileSize 256, ...options } params let Projection olProjGet(projection) const projectionExtent Projection.getExtent(); const size getWidth(projectionExtent) / tileSize; const resolutions []; const matrixIds []; for (let i 1; i maxZoom; i) { resolutions.push(size / Math.pow(2, i)); matrixIds.push(String(i)); } return new WMTS({ ...options, projection: projection, tileGrid: new WMTSTileGrid({ tileSize: tileSize, origin: origin, resolutions: resolutions, matrixIds: matrixIds, maxZoom: maxZoom }) }); } onMounted(() { // BD09 → EPSG:4326中心点直接使用经纬度 const center bd09ToMercator([116.404, 39.915]) const map new Map({ target: olMapRef.value, layers: [ // 矢量底图EPSG:4326 new TileLayer({ source: createWMTSSource({ url: https://t0.tianditu.gov.cn/vec_c/wmts?LAYERvectk你的密钥, tileSize: 256, crossOrigin: anonymous, maxZoom: 18, origin: [-180.0, 90.0], projection: EPSG:4326, matrixSet: c, style: default, format: image/png, VERSION: 1.0.0, REQUEST: GetTile, SERVICE: WMTS, TILEMATRIX: {z}, TILEROW: {y}, TILECOL: {x} }) }), // 矢量注记EPSG:4326 new TileLayer({ source: createWMTSSource({ url: https://t0.tianditu.gov.cn/cva_c/wmts?LAYERcvatk你的密钥, tileSize: 256, crossOrigin: anonymous, maxZoom: 18, origin: [-180.0, 90.0], projection: EPSG:4326, matrixSet: c, style: default, format: image/png, VERSION: 1.0.0, REQUEST: GetTile, SERVICE: WMTS, TILEMATRIX: {z}, TILEROW: {y}, TILECOL: {x} }) }) ], view: new View({ center: center, // 北京市中心EPSG:4326经纬度 zoom: 10, projection: EPSG:4326 }) }) let geoJsonData { type: FeatureCollection, features: [ { type: Feature, id: 1, properties: { name: , level: 10, zIndex: 200 }, geometry: { type: Point, coordinates: center } } ] } // 测试 [116.404, 39.915] 坐标是否准确 const layer new VectorLayer({ source: new VectorSource({ features: new GeoJSON({}).readFeatures(geoJsonData) }) }) layer.setStyle(new Style({ image: new CircleStyle({ radius: 5, fill: new Fill({ color: #ff0000 }), stroke: new Stroke({ color: #ff0000, width: 3 }) }), })) map.addLayer(layer) }) /script5.1 代码要点解析1. 坐标转换极简// 只需一步转换结果直接是经纬度constcenterbd09ToEPSG4326([116.404,39.915])// center ≈ [116.3707, 39.8853]单位度EPSG:4326模式下坐标就是经纬度无需再做墨卡托投影转换。2. WMTS数据源配置createWMTSSource({projection:EPSG:4326,matrixSet:c,// 关键EPSG:4326使用c矩阵集origin:[-180.0,90.0],// EPSG:4326的坐标原点maxZoom:18})3. 地图视图使用经纬度view:newView({center:center,// 直接使用EPSG:4326经纬度坐标zoom:10,projection:EPSG:4326// 关键视图投影为EPSG:4326})六、天地图EPSG:4326图层配置速查6.1 矢量地图// 底图{url:https://t0.tianditu.gov.cn/vec_c/wmts?LAYERvectk你的密钥,projection:EPSG:4326,matrixSet:c}// 注记{url:https://t0.tianditu.gov.cn/cva_c/wmts?LAYERcvatk你的密钥,projection:EPSG:4326,matrixSet:c}6.2 影像地图// 底图{url:https://t0.tianditu.gov.cn/img_c/wmts?LAYERimgtk你的密钥,projection:EPSG:4326,matrixSet:c}// 注记{url:https://t0.tianditu.gov.cn/cia_c/wmts?LAYERciatk你的密钥,projection:EPSG:4326,matrixSet:c}6.3 URL图层代码速查图层EPSG:4326EPSG:3857矢量底图vec_cvec_w矢量注记cva_ccia_w影像底图img_cimg_w影像注记cia_ccta_w七、常见问题与排查7.1 瓦片加载空白原因createWMTSSource中未正确配置EPSG:4326参数。检查项projection是否设置为EPSG:4326matrixSet是否设置为corigin是否设置为[-180.0, 90.0]URL中是否使用了_c后缀如vec_c7.2 地图显示但坐标偏移原因视图投影与瓦片投影不匹配。// 错误视图使用EPSG:3857但瓦片是EPSG:4326view:newView({projection:EPSG:3857// ❌})// 正确视图投影必须与瓦片投影一致view:newView({projection:EPSG:4326// ✓})7.3 缩放级别不正确原因maxZoom配置与实际瓦片支持级别不匹配。天地图EPSG:4326瓦片支持0-18级缩放建议设置maxZoom: 18。7.4 矩阵ID错误原因matrixIds必须从1开始且为字符串格式。// 正确从1开始的字符串数组matrixIds.push(String(i))// [1, 2, 3, ...]// 错误从0开始或使用数字类型matrixIds.push(i)// ❌八、总结8.1 核心要点步骤说明1. 坐标转换BD09 → EPSG:4326一步转换结果为经纬度2. WMTS数据源使用createWMTSSource封装配置投影和瓦片网格3. 矩阵集EPSG:4326使用matrixSet: c4. 视图投影必须设置为projection: EPSG:43265. 图层叠加底图vec_c 注记cva_c需分别加载8.2 三种底图方案对比方案坐标转换数据源类型视图投影复杂度天地图EPSG:3857BD09 → 4326 → 3857XYZEPSG:3857低天地图EPSG:4326BD09 → 4326WMTSEPSG:4326中高德瓦片BD09 → GCJ02 → 手写3857XYZEPSG:3857中8.3 坐标转换速查importgcoordfromgcoord// 百度经纬度 → 天地图EPSG:4326一步constbd09ToTdt4326(coord)gcoord.transform(coord,gcoord.BD09,gcoord.EPSG4326)// 百度经纬度 → 天地图EPSG:3857两步constbd09ToTdt3857(coord){constwgs84gcoord.transform(coord,gcoord.BD09,gcoord.EPSG4326)returngcoord.transform(wgs84,gcoord.EPSG4326,gcoord.EPSG3857)}更新日期2026年8月调试版本OpenLayers 10.9.0