公司动态

OpenLayers 加载高德瓦片地图与百度坐标转换实战(05)

📅 2026/8/15 12:38:01
OpenLayers 加载高德瓦片地图与百度坐标转换实战(05)
OpenLayers加载高德瓦片百度坐标我花了两天才搞明白的坐标转换坑公司项目里存的都是百度坐标BD09但领导偏偏要用高德瓦片做底图。我一开始以为直接gcoord.transform转一下就行结果地图上一放坐标偏了500多米。这篇文章把我在实际项目中踩的坑和最终的解决方案都记录下来希望后来人少走弯路。先说结论百度坐标BD09要在高德瓦片上正确显示必须分两步转换BD09 → GCJ02 → EPSG:3857而且第二步不能用gcoord.transform必须手写墨卡托投影公式否则会偏。一、为什么直接转不行1.1 我踩的第一个坑一开始我这样写// 我以为这样就行...constcentergcoord.transform([116.404,39.915],gcoord.BD09,gcoord.EPSG3857)结果放上去红点离目标位置偏了好几百米。1.2 查了三天才发现的原因翻遍了gcoord的源码发现// gcoord源码里 WebMercator EPSG3857e.WebMercatorEPSG3857gcoord.WebMercator和gcoord.EPSG3857是同一个值所以不管你写哪个效果一样。真正的问题是gcoord的BD09 → EPSG:3857直接转换跳过了GCJ02中间态导致坐标偏移。1.3 正确的转换链路BD09百度经纬度 → GCJ02高德经纬度 ← gcoord可以做 → EPSG:3857墨卡托 ← 必须手写二、手写墨卡托投影公式第二步转换我用了手写公式这是EPSG:3857的标准定义/** * GCJ02经纬度 → WebMercator(EPSG:3857) * 公式x R * λ, y R * ln(tan(π/4 φ/2)) */constgcj02ToMercator(coord){constR6378137// 地球半径米constlngcoord[0]*Math.PI/180constlatcoord[1]*Math.PI/180constxR*lngconstyR*Math.log(Math.tan(Math.PI/4lat/2))return[x,y]}然后组合成完整函数// BD09 → GCJ02constbd09ToGcj02(coord)gcoord.transform(coord,gcoord.BD09,gcoord.GCJ02)// 完整链路BD09 → GCJ02 → EPSG:3857constbd09ToMercator(coord)gcj02ToMercator(bd09ToGcj02(coord))和gcoord对比的结果转换方式坐标结果与真实位置偏差gcoord.BD09 → gcoord.EPSG3857偏约500米gcoord.BD09 → gcoord.GCJ02 手写公式准 1米三、高德瓦片加载高德瓦片用标准XYZ格式直接用ol/source/XYZ就行比百度瓦片简单得多importXYZfromol/source/XYZnewTileLayer({source:newXYZ({url:http://webrd0{1-4}.is.autonavi.com/appmaptile?langzh_cnsize1scale1style8x{x}y{y}z{z},tileSize:256,crossOrigin:anonymous})})和百度瓦片的区别高德瓦片百度瓦片数据源ol/source/XYZ直接用需要封装createBDXYZSource瓦片编号标准XYZY轴反转负数用M前缀配置复杂度低高四、完整Vue3组件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 XYZ from ol/source/XYZ; // 坐标转换 const bd09ToGcj02 (coord) gcoord.transform(coord, gcoord.BD09, gcoord.GCJ02) const gcj02ToMercator (coord) { const R 6378137 const lng coord[0] * Math.PI / 180 const lat coord[1] * Math.PI / 180 const x R * lng const y R * Math.log(Math.tan(Math.PI / 4 lat / 2)) return [x, y] } const bd09ToMercator (coord) gcj02ToMercator(bd09ToGcj02(coord)) const olMapRef ref(null) onMounted(() { // BD09坐标 → 墨卡托 const center bd09ToMercator([116.404, 39.915]) const map new Map({ target: olMapRef.value, layers: [ new TileLayer({ source: new XYZ({ url: http://webrd0{1-4}.is.autonavi.com/appmaptile?langzh_cnsize1scale1style8x{x}y{y}z{z}, tileSize: 256, crossOrigin: anonymous }) }) ], view: new View({ center: center, zoom: 10, projection: EPSG:3857 }) }) // 添加点验证坐标是否准确 let geoJsonData { type: FeatureCollection, features: [{ type: Feature, id: 1, properties: { name: , level: 10, zIndex: 200 }, geometry: { type: Point, coordinates: center } }] } 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) }) /script五、踩坑记录坑1坐标偏移500米原因gcoord的BD09 → EPSG:3857跳过了GCJ02中间态。解决显式分两步第二步手写公式。坑2gcoord.WebMercator和EPSG3857一样改了gcoord.EPSG3857为gcoord.WebMercator发现没任何变化因为它们值相同。坑3手写公式精度问题手写公式用的是球体近似和OpenLayers内置的fromLonLat有几米偏差。在瓦片地图场景下完全可以接受如果需要更高精度可以用import{fromLonLat}fromol/projconstcenterfromLonLat([116.404,39.915])// 更精确六、总结场景转换方式BD09 高德瓦片BD09 → GCJ02手写GCJ02 → EPSG:3857BD09 百度瓦片BD09 → BD09MCgcoord直接转BD09 天地图BD09 → EPSG:4326 → EPSG:3857百度原始坐标位置openlayers高德瓦片包更新日期2026年8月调试版本OpenLayers 10.9.0