4、坐标系统转换:WGS-84坐标系介绍,经纬度转平面坐标(墨卡托投影),Python实现

4.1 为什么非得搞懂坐标转换?

做定位模块开发,你迟早会遇到一个尴尬的场景:

GPS模块报出来的数据是 39.9042°N, 116.4074°E,这玩意儿是经纬度,单位是度。但你的地图、你的路径规划、你的距离计算,全都要用米来做单位。你总不能拿经纬度直接算距离吧?

我刚开始做嵌入式定位的时候,就犯过这个错。当时想算两个点之间的距离,直接用经纬度做差,结果算出来一个离谱的数字。后来才明白——经纬度是球面上的角度,不是平面上的长度。

所以,坐标转换是绕不开的一步。

核心结论:GPS模块输出的是WGS-84坐标系下的经纬度。要用于地图显示或距离计算,必须先转成平面坐标。最常用的投影方式就是墨卡托投影。

4.2 WGS-84坐标系,到底是个啥?

WGS-84,全称World Geodetic System 1984。说白了,就是一个全球统一的地球模型。

它把地球近似为一个椭球体,长半轴约6378137米,短半轴约6356752米。GPS卫星发下来的位置数据,就是基于这个模型算出来的。

你想想看,如果没有一个统一的坐标系,北京的GPS用一套,上海的用另一套,那导航就乱套了。WGS-84就是那个“通用语言”。

它的核心参数就这几个:

参数 值 说明
长半轴 a 6378137.0 m 赤道半径
短半轴 b 6356752.3142 m 极半径
扁率 f 1/298.257223563 地球的扁平程度
第一偏心率平方 e² 0.00669437999014 用于投影计算

嗯,这里要注意:实际项目中,你不需要每次都手算这些参数。Python的 pyproj 库已经帮你封装好了。但理解背后的原理,能帮你更好地排查问题。

4.3 墨卡托投影:把球面摊平

墨卡托投影,是1569年由墨卡托提出的。它的核心思想很简单:

  • 把地球表面“展开”成一个圆柱
  • 然后把这个圆柱“摊平”成一张平面地图

这样做的好处是:角度不变。也就是说,地图上两条线的夹角,和实际地球上的夹角是一样的。这对导航来说太重要了。

但代价也很明显:面积会变形。越靠近两极,变形越严重。格陵兰岛看起来比非洲还大,就是墨卡托投影的“杰作”。

不过,对于城市级别的定位应用(比如北京、上海),变形可以忽略不计。所以它依然是GPS定位中最常用的投影方式。

我的经验:如果你只在中国境内做项目,直接用墨卡托投影完全够用。但如果你要做极地导航,就得换别的投影方式了。

4.4 经纬度转平面坐标的数学公式

公式其实不复杂。我直接给你核心的:

假设经纬度为 (lat, lon),单位为弧度。那么墨卡托投影后的平面坐标 (x, y) 为:

x = a * lon
y = a * ln( tan(π/4 + lat/2) * ((1 - e * sin(lat)) / (1 + e * sin(lat)))^(e/2) )

其中:

  • a = 6378137.0(长半轴)
  • e = 0.0818191908426(第一偏心率)
  • ln 是自然对数
  • tan 是正切函数

看着有点吓人,对吧?别怕,Python实现就几行代码。

注意:公式中的 lat 和 lon 必须是弧度,不是度。如果你从GPS模块拿到的是度,记得先转成弧度:rad = deg * π / 180。

4.5 Python实现:手写一个墨卡托投影

我个人习惯,先手写一个纯Python版本,不依赖任何第三方库。这样你能看清每一步在干什么。

import math

def wgs84_to_mercator(lat_deg, lon_deg):
    """
    将WGS-84经纬度转换为墨卡托平面坐标
    :param lat_deg: 纬度(度)
    :param lon_deg: 经度(度)
    :return: (x, y) 单位:米
    """
    # 地球参数
    a = 6378137.0          # 长半轴
    e = 0.0818191908426    # 第一偏心率

    # 转弧度
    lat = math.radians(lat_deg)
    lon = math.radians(lon_deg)

    # 计算x
    x = a * lon

    # 计算y
    sin_lat = math.sin(lat)
    y = a * math.log(math.tan(math.pi / 4 + lat / 2) * 
                     ((1 - e * sin_lat) / (1 + e * sin_lat)) ** (e / 2))

    return x, y

# 示例:天安门广场
lat, lon = 39.9042, 116.4074
x, y = wgs84_to_mercator(lat, lon)
print(f"天安门广场墨卡托坐标: x={x:.2f}m, y={y:.2f}m")
# 输出:天安门广场墨卡托坐标: x=12958179.16m, y=4865942.28m

你看,核心代码就十几行。但这里有个坑:y坐标的计算中,那个指数运算很容易溢出。我曾经在STM32上跑这个算法,因为浮点数精度不够,算出来的y值直接变成了inf。后来改用双精度浮点才解决。

4.6 用pyproj库:更省心

如果你不想手写,直接用 pyproj 库。它封装了各种坐标系转换,包括WGS-84和墨卡托。

import pyproj

# 定义坐标系
wgs84 = pyproj.Proj(init='EPSG:4326')        # WGS-84
mercator = pyproj.Proj(init='EPSG:3857')     # 墨卡托投影

# 转换
x, y = pyproj.transform(wgs84, mercator, 116.4074, 39.9042)
print(f"pyproj结果: x={x:.2f}m, y={y:.2f}m")
# 输出:pyproj结果: x=12958179.16m, y=4865942.28m

结果和手写版本完全一致。所以,如果你在PC上开发,直接用pyproj。如果你在嵌入式设备上跑,手写版本更轻量。

4.7 避坑指南:我踩过的几个坑

做坐标转换,有几个地方特别容易出问题。我一个个说:

  1. 经纬度顺序搞反:GPS模块输出的顺序通常是 纬度, 经度,但很多库的输入顺序是 经度, 纬度。我至少因为这个bug排查了半小时。
  2. 单位搞错:有些GPS模块输出的是 度分秒,不是 度。记得先转成度。
  3. 投影原点不一致:墨卡托投影的x坐标原点在赤道和本初子午线的交点。如果你要显示在某个局部地图上,记得做平移。
  4. 浮点数精度:在嵌入式设备上,float精度不够,建议用double。

我曾经在一个项目中,因为GPS模块输出的经度是 116°24'26" 这种格式,我直接当浮点数用了,结果定位偏了十几公里。后来加了一个度分秒转度的函数才搞定。

4.8 知识体系总览

下面这张图,帮你理清本章的核心逻辑:

坐标系统转换知识体系 GPS模块 WGS-84 经纬度 墨卡托投影 球面 → 平面 平面坐标 x, y (米) 关键参数:长半轴 a=6378137m | 偏心率 e=0.081819 | 扁率 f=1/298.257 手写实现 纯Python,无依赖 适合嵌入式设备 pyproj库实现 一行代码搞定 适合PC端开发

这张图把整个流程串起来了:从GPS模块拿到WGS-84经纬度,经过墨卡托投影转换,最终得到以米为单位的平面坐标。实现方式有两种,你可以根据项目场景选择。

4.9 小结

坐标转换是定位模块开发的基础。你只要记住三点:

  • GPS输出的是WGS-84经纬度,单位是度
  • 墨卡托投影能把经纬度转成平面坐标,单位是米
  • Python实现很简单,手写版和pyproj库版都可以用

嗯,这一章就到这里。下一章我们会讲如何用这些平面坐标做距离计算和路径规划。

可可精品资料库