通用横轴墨卡托投影学习笔记

zxl19 2025-05-31

我的通用横轴墨卡托投影学习笔记。

UTM Hello World

  1. 通用横轴墨卡托(Universal Transverse Mercator,UTM)投影是一种地图投影方法,将经纬度转换为二维笛卡尔坐标表示;
  2. UTM坐标是一种水平位置表示方式,忽略了海拔高度,将地球建模为完美椭球体;
  3. UTM坐标格式:

     [经度区间][纬度区间][方格坐标]
    
    • 经度区间:从西经180°开始,每6°一个区间,自西向东按照从0到60排列;
      • 在挪威西海岸以及斯瓦尔巴群岛附近的区域有例外;
    • 纬度区间:从南纬80°开始,每8°一个区间,自南向北按照从C到X排列;
      • 覆盖南纬80°到北纬84°N之间的区域;
      • 为避免和数字1和0混淆,跳过I和O;
    • 方格坐标:使用东向坐标(easting)和北向坐标(northing)表示,原点是中央经线和赤道的交点;
      • 为避免东向坐标出现负值,将原点西移500千米,东向坐标取值范围为0-10000千米;
      • 在北半球,北向坐标取值范围为0-9300千米;
      • 在南半球,北向坐标取值范围为10000-11000千米;
  4. UTM投影在中央经线处的尺度因子为0.9996,保证每个分区内的尺度因子均在1附近,在使用时,可以认为是等尺度的,误差不超过千分之一;
  5. 经度小数点后六位精确到米,纬度受到实际纬度影响,在中国可以认为经纬度取到小数点后六位大约在米级精度;
  6. 主要城市UTM分区:

    • 50N:北京、广州;
    • 49N:深圳;

算法实现

使用hobuinc/mgrs工程中utm.h文件中定义的转换函数可以实现经纬度和UTM投影之间的相互转换:

原型声明:

long Convert_Geodetic_To_UTM(double Latitude,
                             double Longitude,
                             long* Zone,
                             char* Hemisphere,
                             double* Easting,
                             double* Northing);
long Convert_UTM_To_Geodetic(long Zone,
                             char Hemisphere,
                             double Easting,
                             double Northing,
                             double* Latitude,
                             double* Longitude);

返回值含义:

#define UTM_NO_ERROR            0x0000
#define UTM_LAT_ERROR           0x0001
#define UTM_LON_ERROR           0x0002
#define UTM_EASTING_ERROR       0x0004
#define UTM_NORTHING_ERROR      0x0008
#define UTM_ZONE_ERROR          0x0010
#define UTM_HEMISPHERE_ERROR    0x0020
#define UTM_ZONE_OVERRIDE_ERROR 0x0040
#define UTM_A_ERROR             0x0080
#define UTM_INV_F_ERROR         0x0100

收敛角

收敛角(Convergence Angle,CA)指的是UTM投影中某一点处的网格北(Grid North)与真北(True North)之间的夹角。

#include <cmath>

static constexpr double kDEG2RAD = 0.017453292519943;   // deg -> rad
static constexpr double kRAD2DEG = 57.295779513082323;  // rad -> deg

/**
 * @brief 计算给定经纬度位置处的收敛角
 * @note 中央子午线采用六度带规定
 * @param lon_deg 经度,单位为度,东经为正,西经为负
 * @param lat_deg 纬度,单位为度,北纬为正,南纬为负
 * @return double 收敛角,单位为度,网格北转向真北,逆时针为正
 */
inline double GetConvergenceAngle(double lon_deg, double lat_deg) {
  if (lon_deg < -180 || lon_deg > 180) {
    LOG(ERROR) << "Invalid longitude: " << std::to_string(lon_deg) << " deg.";
    return 0.0;
  }
  if (lat_deg < -90 || lat_deg > 90) {
    LOG(ERROR) << "Invalid latitude: " << std::to_string(lat_deg) << " deg.";
    return 0.0;
  }
  int N = std::floor(std::abs(lon_deg) / 6);
  double central_meridian = std::copysign(6 * (N + 1) - 3, lon_deg);
  // *精确公式
  double convergence_angle =
      std::atan(std::tan((lon_deg - central_meridian) * kDEG2RAD) * std::sin(lat_deg * kDEG2RAD)) * kRAD2DEG;
  // *近似公式,仅在距离中央子午线较近的时候成立
  // double convergence_angle = (std::sin(lat_deg * kDEG2RAD)) * (lon_deg - central_meridian);
  return convergence_angle;
}

参考

  1. Universal Transverse Mercator coordinate system-Wikipedia
  2. Transverse Mercator projection-Wikipedia
  3. utm投影中央经线长度比为什么是0.9996?-知乎用户的回答-知乎
  4. Calculating grid convergence (True North to Grid North)-Stack Exchange
  5. 备用-UTM投影分带查询表-大男孩99的文章-知乎
  6. 十进制经纬度坐标小数点后几位能精确到米?-RoverTang的回答-知乎
  7. hobuinc/mgrs
  8. UTM ↔ WGS84 conversion in C++
  9. UTM投影分带相关资料及计算公式-CSDN博客
  10. UTM投影与高斯克吕格投影中分带带号与中央经线经度的计算关系-CSDN博客
  11. 根据经纬度算UTM带号-博客园