轉(zhuǎn)換實(shí)戰(zhàn)(07))
天地圖同時提供EPSG:4326經(jīng)緯度投影和EPSG:3857Web墨卡托投影兩種瓦片服務(wù)。使用EPSG:4326投影時OpenLayers需要配置自定義WMTS數(shù)據(jù)源和瓦片網(wǎng)格。本文基于實(shí)際項(xiàng)目代碼詳細(xì)講解如何在Vue3 OpenLayers項(xiàng)目中加載天地圖EPSG:4326瓦片并將百度BD09坐標(biāo)正確投放到底圖上。一、EPSG:4326 vs EPSG:3857如何選擇1.1 兩種投影的區(qū)別特性EPSG:4326EPSG:3857坐標(biāo)類型經(jīng)緯度度墨卡托米坐標(biāo)范圍[-180, -90] ~ [180, 90]約[-20037508, -20037508] ~ [20037508, 20037508]形狀變形高緯度拉伸等角投影面積變形瓦片矩陣集matrixSet: cmatrixSet: wURL標(biāo)識vec_c/cva_cvec_w/cia_w1.2 選擇建議數(shù)據(jù)源為經(jīng)緯度使用EPSG:4326坐標(biāo)無需額外轉(zhuǎn)換與標(biāo)準(zhǔn)Web地圖對接使用EPSG:3857兼容性更好需要精確面積/距離計(jì)算使用EPSG:4326二、EPSG:4326坐標(biāo)轉(zhuǎn)換2.1 轉(zhuǎn)換鏈路天地圖EPSG:4326瓦片基于標(biāo)準(zhǔn)WGS84坐標(biāo)系BD09坐標(biāo)的轉(zhuǎn)換鏈路非常簡潔BD09百度經(jīng)緯度→ EPSG:4326WGS84經(jīng)緯度只需一步轉(zhuǎn)換無需再轉(zhuǎn)換為墨卡托坐標(biāo)。2.2 轉(zhuǎn)換實(shí)現(xiàn)importgcoordfromgcoord// BD09 → EPSG:4326WGS84經(jīng)緯度constbd09ToEPSG4326(coord)gcoord.transform(coord,gcoord.BD09,gcoord.EPSG4326)// 使用示例constcenterbd09ToEPSG4326([116.404,39.915])// 結(jié)果: [116.3707xx, 39.8853xx]EPSG:4326經(jīng)緯度單位度關(guān)鍵區(qū)別EPSG:4326模式下地圖中心點(diǎn)直接使用經(jīng)緯度坐標(biāo)單位度而EPSG:3857模式下使用墨卡托坐標(biāo)單位米。三、WMTS數(shù)據(jù)源封裝3.1 為什么需要自定義WMTS源天地圖EPSG:4326瓦片使用WMTS協(xié)議與標(biāo)準(zhǔn)XYZ瓦片不同WMTS需要指定瓦片矩陣集matrixSet需要根據(jù)投影計(jì)算分辨率數(shù)組需要配置瓦片網(wǎng)格TileGridOpenLayers的ol/source/WMTS需要完整的瓦片網(wǎng)格配置不能像XYZ那樣簡單使用URL模板。3.2 createWMTSSource封裝import{WMTS}fromol/sourceimport{getasolProjGet}fromol/projimport{getWidth}fromol/extentimportWMTSTileGridfromol/tilegrid/WMTS/** * 創(chuàng)建天地圖WMTS數(shù)據(jù)源 * param {Object} params - 配置參數(shù) * param {string} params.projection - 坐標(biāo)投影默認(rèn) EPSG:3857 * param {number} params.maxZoom - 最大縮放級別默認(rèn) 23 * param {Array} params.origin - 坐標(biāo)原點(diǎn)默認(rèn) [-180.0, 90.0] * param {number} params.tileSize - 瓦片大小默認(rèn) 256 * returns {WMTS} WMTS數(shù)據(jù)源 */constcreateWMTSSource(params){const{projection:projectionEPSG:3857,maxZoom:maxZoom23,origin:origin[-180.0,90.0],tileSize:tileSize256,...options}params// 獲取投影對象letProjectionolProjGet(projection)constprojectionExtentProjection.getExtent()// 計(jì)算投影范圍寬度與瓦片大小的比值constsizegetWidth(projectionExtent)/tileSize// 生成分辨率數(shù)組和矩陣IDconstresolutions[]constmatrixIds[]for(leti1;imaxZoom;i){// 每一級分辨率 上一級分辨率 / 2resolutions.push(size/Math.pow(2,i))// 矩陣ID必須從 1 開始且為字符串格式matrixIds.push(String(i))}// 創(chuàng)建 WMTS 數(shù)據(jù)源returnnewWMTS({...options,projection:projection,tileGrid:newWMTSTileGrid({tileSize:tileSize,origin:origin,resolutions:resolutions,matrixIds:matrixIds,maxZoom:maxZoom})})}3.3 分辨率計(jì)算原理天地圖EPSG:4326的瓦片分辨率計(jì)算基于投影范圍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每一級分辨率是上一級的一半縮放越高級別分辨率越精細(xì)。四、EPSG:4326 vs EPSG:3857 配置對比4.1 WMTS參數(shù)差異參數(shù)EPSG:4326EPSG:3857projectionEPSG:4326EPSG:3857matrixSetcworigin[-180.0, 90.0][0, 0]默認(rèn)URL圖層vec_c/cva_cvec_w/cia_w數(shù)據(jù)源類型ol/source/WMTSol/source/XYZ4.2 地圖視圖差異// EPSG:4326 模式中心點(diǎn)使用經(jīng)緯度度view:newView({center:[116.404,39.915],// 直接使用經(jīng)緯度zoom:10,projection:EPSG:4326})// EPSG:3857 模式中心點(diǎn)使用墨卡托米view:newView({center:[12958175,4862293],// 墨卡托坐標(biāo)zoom:10,projection:EPSG:3857})4.3 坐標(biāo)轉(zhuǎn)換差異// EPSG:4326BD09 → EPSG:4326一步轉(zhuǎn)換結(jié)果為經(jīng)緯度constbd09To4326(coord)gcoord.transform(coord,gcoord.BD09,gcoord.EPSG4326)// EPSG:3857BD09 → EPSG:4326 → EPSG:3857兩步轉(zhuǎn)換結(jié)果為墨卡托constbd09To3857(coord){constwgs84gcoord.transform(coord,gcoord.BD09,gcoord.EPSG4326)returngcoord.transform(wgs84,gcoord.EPSG4326,gcoord.EPSG3857)}五、完整Vue3組件實(shí)現(xiàn)以下是基于項(xiàng)目ol-bd-tdtEPSG4326.vue的完整實(shí)現(xiàn)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; // 百度坐標(biāo)(BD09) → EPSG:4326一步轉(zhuǎn)換 const bd09ToEPSG4326 (coord) gcoord.transform(coord, gcoord.BD09, gcoord.EPSG4326) const bd09ToMercator (coord) bd09ToEPSG4326(coord) const olMapRef ref(null) /** * 創(chuàng)建天地圖WMTS數(shù)據(jù)源 * 根據(jù)投影計(jì)算分辨率數(shù)組和瓦片網(wǎng)格 */ 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中心點(diǎn)直接使用經(jīng)緯度 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經(jīng)緯度 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] 坐標(biāo)是否準(zhǔn)確 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 代碼要點(diǎn)解析1. 坐標(biāo)轉(zhuǎn)換極簡// 只需一步轉(zhuǎn)換結(jié)果直接是經(jīng)緯度constcenterbd09ToEPSG4326([116.404,39.915])// center ≈ [116.3707, 39.8853]單位度EPSG:4326模式下坐標(biāo)就是經(jīng)緯度無需再做墨卡托投影轉(zhuǎn)換。2. WMTS數(shù)據(jù)源配置createWMTSSource({projection:EPSG:4326,matrixSet:c,// 關(guān)鍵EPSG:4326使用c矩陣集origin:[-180.0,90.0],// EPSG:4326的坐標(biāo)原點(diǎn)maxZoom:18})3. 地圖視圖使用經(jīng)緯度view:newView({center:center,// 直接使用EPSG:4326經(jīng)緯度坐標(biāo)zoom:10,projection:EPSG:4326// 關(guān)鍵視圖投影為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參數(shù)。檢查項(xiàng)projection是否設(shè)置為EPSG:4326matrixSet是否設(shè)置為corigin是否設(shè)置為[-180.0, 90.0]URL中是否使用了_c后綴如vec_c7.2 地圖顯示但坐標(biāo)偏移原因視圖投影與瓦片投影不匹配。// 錯誤視圖使用EPSG:3857但瓦片是EPSG:4326view:newView({projection:EPSG:3857// ?})// 正確視圖投影必須與瓦片投影一致view:newView({projection:EPSG:4326// ?})7.3 縮放級別不正確原因maxZoom配置與實(shí)際瓦片支持級別不匹配。天地圖EPSG:4326瓦片支持0-18級縮放建議設(shè)置maxZoom: 18。7.4 矩陣ID錯誤原因matrixIds必須從1開始且為字符串格式。// 正確從1開始的字符串?dāng)?shù)組matrixIds.push(String(i))// [1, 2, 3, ...]// 錯誤從0開始或使用數(shù)字類型matrixIds.push(i)// ?八、總結(jié)8.1 核心要點(diǎn)步驟說明1. 坐標(biāo)轉(zhuǎn)換BD09 → EPSG:4326一步轉(zhuǎn)換結(jié)果為經(jīng)緯度2. WMTS數(shù)據(jù)源使用createWMTSSource封裝配置投影和瓦片網(wǎng)格3. 矩陣集EPSG:4326使用matrixSet: c4. 視圖投影必須設(shè)置為projection: EPSG:43265. 圖層疊加底圖vec_c 注記cva_c需分別加載8.2 三種底圖方案對比方案坐標(biāo)轉(zhuǎn)換數(shù)據(jù)源類型視圖投影復(fù)雜度天地圖EPSG:3857BD09 → 4326 → 3857XYZEPSG:3857低天地圖EPSG:4326BD09 → 4326WMTSEPSG:4326中高德瓦片BD09 → GCJ02 → 手寫3857XYZEPSG:3857中8.3 坐標(biāo)轉(zhuǎn)換速查importgcoordfromgcoord// 百度經(jīng)緯度 → 天地圖EPSG:4326一步constbd09ToTdt4326(coord)gcoord.transform(coord,gcoord.BD09,gcoord.EPSG4326)// 百度經(jīng)緯度 → 天地圖EPSG:3857兩步constbd09ToTdt3857(coord){constwgs84gcoord.transform(coord,gcoord.BD09,gcoord.EPSG4326)returngcoord.transform(wgs84,gcoord.EPSG4326,gcoord.EPSG3857)}更新日期2026年8月調(diào)試版本OpenLayers 10.9.0