4. 坐标系统与投影:地理坐标系(WGS84、GCJ02)、投影坐标系、坐标转换实战
坐标系统,说白了就是给地球上的每个点发一个「身份证号」。但地球是个不规则的椭球体,怎么发这个号,不同国家、不同场景有各自的玩法。
我刚开始做地图开发时,觉得坐标就是个经纬度嘛,有啥好学的?直到有一次,我用 WGS84 的数据直接叠到高德地图上,结果点位偏了五六百米。嗯,从那以后我再也不敢小看坐标系了。
核心认知:坐标系统不是数学问题,是政治问题 + 历史问题 + 工程问题的混合体。你不需要背公式,但必须知道「谁在用哪个坐标系」以及「怎么转」。
4.1 地理坐标系:地球的「经纬度身份证」
地理坐标系用经纬度描述位置。经度范围 -180° 到 180°,纬度范围 -90° 到 90°。听起来简单,但不同坐标系用的「地球模型」不一样。
4.1.1 WGS84 —— 全球通用标准
WGS84 全称 World Geodetic System 1984。GPS 卫星用的就是它。Google Earth、OpenStreetMap、大部分国际 GIS 数据都基于 WGS84。
我个人习惯把 WGS84 当作「原始数据格式」。所有从 GPS 设备、手机定位 API 拿到的坐标,默认都是 WGS84。
小技巧:WGS84 的经纬度通常用十进制度数表示,比如 39.9042°N, 116.4074°E。不要用度分秒格式做计算,容易出精度问题。
4.1.2 GCJ02 —— 中国特有的「加密坐标系」
GCJ02 全称是「国测局 2002 坐标系」。说白了,就是在 WGS84 基础上加了一个非线性偏移。偏移量大约在 100-700 米之间,随机且不可逆。
为什么会有这个?嗯,这是国家测绘局的要求。所有在中国境内发行的互联网地图,必须使用 GCJ02。高德、腾讯、百度(百度有自己的二次偏移,叫 BD09)都遵循这个规则。
我在项目中遇到过最坑的事:客户拿了一台进口 GPS 设备,直接往高德地图上打点。结果所有点位都偏到隔壁楼去了。后来加了坐标转换才解决。
| 坐标系 | 全称 | 使用场景 | 偏移量 |
|---|---|---|---|
| WGS84 | World Geodetic System 1984 | GPS、Google Earth、OSM、国际数据 | 无 |
| GCJ02 | 国测局 2002 | 高德、腾讯、国内互联网地图 | 100-700m 随机偏移 |
| BD09 | 百度坐标系 2009 | 百度地图 | 在 GCJ02 基础上再偏移 |
警告:千万不要把 WGS84 坐标直接扔到高德或百度地图上显示。偏移量足够让你的 POI 跑到隔壁街区。反过来,从高德拿到的坐标也不能直接用于 GPS 导航。
4.2 投影坐标系:把球面压成平面
地球是球体,但屏幕和纸张是平面。投影坐标系就是把球面上的经纬度「压」到平面上。压的过程必然有变形——面积、距离、角度、形状,至少牺牲一个。
4.2.1 Web 墨卡托投影(EPSG:3857)
Web 墨卡托是目前互联网地图的主流。Google Maps、高德、百度都用它。它的特点是:
- 角度不变(等角投影),适合导航
- 面积变形严重,越靠近两极越大
- 把地球近似为球体,计算简单
你想想看,为什么 Greenland 在地图上看起来比非洲还大?就是墨卡托投影的锅。实际面积非洲是 Greenland 的 14 倍。
4.2.2 高斯-克吕格投影(Gauss-Krüger)
高斯-克吕格是分带投影。把地球切成 60 个 6° 宽的带,每个带单独投影。中国主要用 3° 带和 6° 带。
这个投影在国内测绘、国土、规划领域用得非常多。如果你做的是政府项目,大概率会遇到 CGCS2000 坐标系 + 高斯-克吕格投影的组合。
经验之谈:做 Web 前端地图用 Web 墨卡托(EPSG:3857),做专业测绘分析用高斯-克吕格(EPSG:4490 系列)。选错了投影,面积计算能差出 30%。
4.3 坐标转换实战:代码说话
理论说完了,来点实际的。坐标转换最常用的场景就是 WGS84 ↔ GCJ02。我直接给你一个可用的 Python 实现。
4.3.1 WGS84 转 GCJ02
import math
def wgs84_to_gcj02(lng, lat):
"""
WGS84 转 GCJ02(高德/腾讯坐标系)
:param lng: 经度
:param lat: 纬度
:return: (gcj_lng, gcj_lat)
"""
a = 6378245.0 # 长半轴
ee = 0.00669342162296594323 # 偏心率平方
def _transform_lat(x, y):
ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * math.sqrt(abs(x))
ret += (20.0 * math.sin(6.0 * x * math.pi) + 20.0 * math.sin(2.0 * x * math.pi)) * 2.0 / 3.0
ret += (20.0 * math.sin(y * math.pi) + 40.0 * math.sin(y / 3.0 * math.pi)) * 2.0 / 3.0
ret += (160.0 * math.sin(y / 12.0 * math.pi) + 320.0 * math.sin(y * math.pi / 30.0)) * 2.0 / 3.0
return ret
def _transform_lng(x, y):
ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * math.sqrt(abs(x))
ret += (20.0 * math.sin(6.0 * x * math.pi) + 20.0 * math.sin(2.0 * x * math.pi)) * 2.0 / 3.0
ret += (20.0 * math.sin(x * math.pi) + 40.0 * math.sin(x / 3.0 * math.pi)) * 2.0 / 3.0
ret += (150.0 * math.sin(x / 12.0 * math.pi) + 300.0 * math.sin(x / 30.0 * math.pi)) * 2.0 / 3.0
return ret
# 判断是否在中国境内
if lng < 72.004 or lng > 137.8347 or lat < 0.8293 or lat > 55.8271:
return lng, lat # 境外不偏移
dlat = _transform_lat(lng - 105.0, lat - 35.0)
dlng = _transform_lng(lng - 105.0, lat - 35.0)
radlat = lat / 180.0 * math.pi
magic = math.sin(radlat)
magic = 1 - ee * magic * magic
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)
gcj_lat = lat + dlat
gcj_lng = lng + dlng
return gcj_lng, gcj_lat
4.3.2 GCJ02 转 WGS84
def gcj02_to_wgs84(lng, lat):
"""
GCJ02 转 WGS84(迭代法,精度约 0.5 米)
"""
wgs_lng, wgs_lat = lng, lat
for _ in range(5): # 迭代 5 次
gcj_lng, gcj_lat = wgs84_to_gcj02(wgs_lng, wgs_lat)
wgs_lng += lng - gcj_lng
wgs_lat += lat - gcj_lat
return wgs_lng, wgs_lat
注意:GCJ02 转 WGS84 没有解析解,只能用迭代逼近。上面的代码迭代 5 次,精度已经足够日常使用。如果你需要更高精度,可以迭代 10 次。
4.3.3 使用第三方库(推荐)
自己手写转换代码适合理解原理。实际项目中,我建议直接用现成的库:
- Python:
coordTransform或geopy - JavaScript:
coordtransform(npm 包) - Java:
CoordinateTransform(GitHub 开源)
我曾经在一个数据管道项目里,每天要转换 500 万条 GPS 轨迹。手写代码跑了 3 天发现精度不够,换成 coordTransform 库后,精度从 5 米提升到 0.5 米,速度还快了 30%。
4.4 常见踩坑与避坑指南
做坐标转换这么多年,我总结了几条血泪教训:
- 不要相信「一次转换永久使用」——不同数据源的坐标系标注可能不准,拿到数据先抽几个点验证
- 投影坐标系一定要带 EPSG 编号——光说「高斯-克吕格」不够,要说明是 3° 带还是 6° 带,中央经线是多少
- 百度坐标是 GCJ02 的二次加密——从百度地图拿到的坐标,要先转 GCJ02,再转 WGS84,不能直接转
- 境外坐标不需要偏移——GCJ02 只在中国境内生效,境外坐标直接透传
我曾经踩过的坑:有一次做全国 POI 数据融合,把高德数据和 GPS 数据直接合并分析。结果北京地区的 POI 密度异常高,查了两天才发现是坐标偏移导致同一个 POI 在两种坐标系下被识别成两个点。后来加了坐标统一转换,数据量直接砍掉 40%。
4.5 本章小结
坐标系统是 GIS 的基石。你不需要记住所有公式,但必须记住三件事:
- WGS84 是原始数据,GCJ02 是国内互联网地图的标准
- 投影坐标系把球面压成平面,Web 墨卡托是 Web 地图的默认选择
- 坐标转换用现成库,别自己造轮子