地图导入基础:坐标系与底图加载

大家好,我是老张。做轨迹回放这些年,我踩过最多的坑就是坐标系问题。你想想看,明明GPS采集的坐标是对的,放到地图上却偏了几百米——这种事儿我遇到过不下十次。今天咱们就把这个基础打牢。

一、三大坐标系,到底有什么区别?

先说结论:WGS84是原始坐标,GCJ02是国标加密,BD09是百度再加密。说白了,就是一层套一层的加密关系。

核心记忆点:

  • WGS84 → 加一次密 → GCJ02
  • GCJ02 → 再加一次密 → BD09
  • BD09 → 不能直接转回WGS84,必须先转GCJ02

1. WGS84(世界大地测量系统)

这是GPS卫星直接输出的坐标。我刚开始做轨迹采集时,用的就是这种。它是个全球通用的坐标系,精度也最高。但问题来了——国内地图不允许直接使用WGS84。为什么?因为国家测绘局要求所有公开地图必须经过加密偏移。

2. GCJ02(国测局坐标)

这是国内最常用的坐标系。高德地图、腾讯地图、Google中国版用的都是它。我记得有一次项目,客户给的轨迹数据是WGS84的,我直接往高德地图上一扔,结果轨迹全飘到河对岸去了。嗯,这就是没做坐标系转换的后果。

3. BD09(百度坐标)

百度地图自己搞的一套,在GCJ02基础上又加了一层偏移。我个人习惯是:能用GCJ02就别用BD09,因为BD09的转换算法不公开,只能靠百度提供的API来转,自由度很低。

二、坐标系转换实战

转换其实不复杂,但要注意精度。我曾经在项目里直接用网上找的转换公式,结果偏差了十几米。后来才发现,不同版本的转换算法精度差异很大。下面是我验证过的代码:

// WGS84 转 GCJ02
function wgs84ToGcj02(lat, lng) {
    const a = 6378245.0;
    const ee = 0.00669342162296594323;
    
    let dLat = transformLat(lng - 105.0, lat - 35.0);
    let dLng = transformLng(lng - 105.0, lat - 35.0);
    let radLat = lat / 180.0 * Math.PI;
    let magic = Math.sin(radLat);
    magic = 1 - ee * magic * magic;
    let sqrtMagic = Math.sqrt(magic);
    dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * Math.PI);
    dLng = (dLng * 180.0) / (a / sqrtMagic * Math.cos(radLat) * Math.PI);
    
    return {
        lat: lat + dLat,
        lng: lng + dLng
    };
}

// GCJ02 转 BD09
function gcj02ToBd09(lat, lng) {
    const x = lng;
    const y = lat;
    const z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * Math.PI);
    const theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * Math.PI);
    
    return {
        lat: z * Math.sin(theta) + 0.006,
        lng: z * Math.cos(theta) + 0.0065
    };
}

我的经验:转换时尽量用高德或百度官方提供的SDK。如果必须自己写,一定要用经过大量测试的算法库。我曾经因为转换精度问题,导致轨迹回放时车辆位置和实际道路差了20米,排查了整整两天。

三、常用地图底图加载方式

底图加载,说白了就是告诉地图SDK:你要用哪家的地图数据。不同地图的加载方式略有不同,但核心思路是一样的——初始化地图实例,设置坐标系,然后加载瓦片。

1. OpenStreetMap(OSM)

OSM是开源地图,数据由全球志愿者贡献。我特别喜欢它的免费且无使用限制。但要注意,OSM在国内的详细程度不如高德和百度。加载方式很简单:

// 使用 Leaflet 加载 OSM
var map = L.map('map').setView([39.9042, 116.4074], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: '© OpenStreetMap contributors'
}).addTo(map);

2. Google Maps

Google Maps的全球数据最全,但在国内访问不稳定。我建议只在海外项目中使用。加载时需要API Key:

// 加载 Google Maps
function initMap() {
    var map = new google.maps.Map(document.getElementById('map'), {
        center: {lat: 39.9042, lng: 116.4074},
        zoom: 13
    });
}

注意:Google Maps在国内部分地区可能无法正常加载瓦片。如果你做的是国内项目,优先考虑高德或百度。

3. 高德地图

高德是国内最常用的地图之一,对GCJ02坐标系支持最好。我个人习惯用高德做轨迹回放,因为它的路网数据更新快,而且API文档写得很清楚。

// 加载高德地图
var map = new AMap.Map('map', {
    zoom: 13,
    center: [116.397428, 39.90923],
    viewMode: '2D'
});

四、坐标系与底图的匹配关系

这里有个容易踩的坑:底图用什么坐标系,你的轨迹数据就必须用什么坐标系。比如高德地图用的是GCJ02,你传WGS84的坐标进去,轨迹就会偏移。我整理了一个对照表:

地图底图 默认坐标系 推荐数据坐标系
OpenStreetMap WGS84 WGS84
Google Maps WGS84(海外)/ GCJ02(国内) WGS84 或 GCJ02
高德地图 GCJ02 GCJ02
百度地图 BD09 BD09

五、知识体系总览

下面这张图是我自己画的,把本章的核心逻辑串起来了。你一看就明白:

地图导入核心知识体系 坐标系 WGS84(GPS原始) GCJ02(国标加密) BD09(百度再加密) 坐标转换 WGS84 → GCJ02 GCJ02 → BD09 BD09 → GCJ02 → WGS84 地图底图 OpenStreetMap Google Maps 高德地图 核心原则:底图坐标系必须与数据坐标系一致,否则轨迹偏移

我的建议:如果你刚开始做轨迹回放,先选一个地图(比如高德),把它的坐标系搞清楚。不要同时用好几个地图,容易搞混。等一个地图玩熟了,再扩展到其他。

好了,这一章的内容就这些。坐标系和底图加载是轨迹回放的地基,地基打不牢,后面全是坑。下一章咱们会讲轨迹数据的采集与清洗,到时候你会看到坐标系转换在实际数据中是怎么用的。

可可精品资料库