摘要
在日常测绘与GIS开发中,经常需要将WGS‑84大地坐标与UTM投影坐标、自定义中央子午线的工程坐标系进行互相转换。本文介绍一款基于C#开发的WinForm工具箱,支持批量转换、椭球高改正、度分秒/十进制度灵活输入输出,并提供UTM带号自动计算与工程坐标系任意参数设置。文中深入分析了横轴墨卡托投影的数学基础、椭球高对平面坐标的归化改正方法,并给出核心代码实现与详细使用教程。
一、引言
通用横轴墨卡托投影(Universal Transverse Mercator,UTM)是应用最广泛的投影系统之一,而自定义中央子午线的工程坐标系(如"任意带"投影)则在大型工程测量中不可或缺。市面已有许多专业坐标转换工具,但对于开发者而言,一个开源、可控、可批量处理的轻量级工具更为实用。
本文基于WGS‑84椭球,实现了一个完整的Windows窗体应用程序,具备以下特性:
-
常规UTM正反算:经纬度 ⇄ UTM(自动计算带号与纬度带字母);
-
工程坐标系正反算:支持任意中央子午线、尺度因子、东/北偏移;
-
椭球高改正:将大地高纳入平面坐标的比例缩放,实现"椭球高不是摆设";
-
批量处理:多行输入输出,支持复制粘贴;
-
灵活的格式:十进制度与度分秒(ddd.mmss.ssss)自由切换。
二、功能与界面设计
程序主界面采用TabControl分为两个选项卡:
TabPage1 -- 常规UTM转换
-
转换方向选择:"经纬度 → UTM"或"UTM → 经纬度";
-
经纬度格式下拉框:度(ddd.dddddd)或度分秒(ddd.mmss.ssss);
-
左侧多行文本框输入坐标,右侧输出结果;
-
UTM输出格式:
东坐标 北坐标 带号 纬度带字母(保留4位小数); -
支持批量处理,错误行会单独提示。
TabPage2 -- 工程坐标系
-
投影参数设置:中央子午线、尺度因子(1.0或0.9996)、东偏移、北偏移、默认椭球高(通常为0);
-
转换方向:"经纬度 → 投影"或"投影 → 经纬度";
-
同样支持格式切换与批量处理;
-
椭球高参与计算:输出坐标会因高程不同而产生细微但正确的缩放。
三、数学基础
3.1 WGS-84椭球参数
3.2 横轴墨卡托投影正反算
横轴墨卡托投影公式是UTM与工程坐标系的数学核心。标准公式可由ProjNet库直接实现,但为了引入椭球高改正,我们还需要理解卯酉圈曲率半径 �N:

其中 �B 为纬度(弧度)。
3.3 椭球高改正
工程坐标系通常是投影到椭球面上的平面坐标。当测区平均高程面与椭球面不重合时,需要进行投影面变换。最简单的归化方法是比例缩放:
设椭球面上投影坐标为(E0,N0),则对应于高程 ℎh 的坐标 (E,N) 为:

其中 FE,FN 为假东、假北偏移。反算时则进行逆缩放并迭代一次(因为 N 依赖于待求纬度),精度足够。
四、核心代码解析
4.1 度分秒与十进制度的互转
度分秒格式采用 ddd.mmss.ssss,即度、分、秒之间用小数点分隔,分占两位整数,秒包括小数部分。例如 150.262400 表示 150°26′24.00″。转换函数如下:
// 度分秒 → 十进制度
private double DmsToDegrees(double dms)
{
bool negative = dms < 0;
double absVal = Math.Abs(dms);
double deg = Math.Floor(absVal);
double frac = absVal - deg;
double minutes = Math.Floor(frac * 100.0);
double seconds = (frac * 100.0 - minutes) * 100.0;
if (minutes >= 60.0) throw new FormatException("分不能≥60");
if (seconds >= 60.0) throw new FormatException("秒不能≥60");
double result = deg + minutes / 60.0 + seconds / 3600.0;
return negative ? -result : result;
}
// 十进制度 → 度分秒字符串
private string FormatCoord(double deg, ComboBox cmb)
{
if (cmb.SelectedIndex == 1) // 度分秒格式
{
bool negative = deg < 0;
double absDeg = Math.Abs(deg);
double d = Math.Floor(absDeg);
double minFloat = (absDeg - d) * 60.0;
double m = Math.Floor(minFloat);
double s = (minFloat - m) * 60.0;
s = Math.Round(s, 4, MidpointRounding.AwayFromZero);
if (s >= 60.0) { s -= 60; m++; }
if (m >= 60) { m -= 60; d++; }
return $"{(negative ? "-" : "")}{d}.{m:00}{s:00.0000}";
}
else return deg.ToString("F8");
}
4.2 UTM投影构建与转换
利用ProjNet的WKT字符串生成投影坐标系,避免手动拼接参数出错:
private ProjectedCoordinateSystem CreateUtmProjection(int zone, bool north)
{
string wkt = $@"PROJCS[""WGS 84 / UTM zone {zone}{(north ? "N" : "S")}"",
GEOGCS[""WGS 84"", DATUM[""WGS_1984"", SPHEROID[""WGS 84"", 6378137, 298.257223563]],
PRIMEM[""Greenwich"", 0], UNIT[""degree"", 0.0174532925199433]],
PROJECTION[""Transverse_Mercator""],
PARAMETER[""latitude_of_origin"", 0],
PARAMETER[""central_meridian"", {zone * 6 - 183.0}],
PARAMETER[""scale_factor"", 0.9996],
PARAMETER[""false_easting"", 500000],
PARAMETER[""false_northing"", {(north ? 0 : 10000000)}],
UNIT[""metre"", 1]]";
var cf = new CoordinateSystemFactory();
return (ProjectedCoordinateSystem)cf.CreateFromWkt(wkt);
}
// 坐标转换核心
private double[] Transform(CoordinateSystem from, CoordinateSystem to, double x, double y)
{
var trans = Ctf.CreateFromCoordinateSystems(from, to);
return trans.MathTransform.Transform(new[] { x, y });
}
4.3 工程坐标系及椭球高改正
自定义投影类似,但不使用固定带号。椭球高改正体现在正反算的缩放操作中:
// 正算:经纬度 → 投影坐标 (带椭球高改正)
double[] result = Transform(Wgs84, projCs, lon, lat);
double east0 = result[0], north0 = result[1];
double N = GetPrimeVerticalRadius(latRad);
double scale = (N + h) / N;
double east = falseE + (east0 - falseE) * scale;
double north = falseN + (north0 - falseN) * scale;
// 反算:投影坐标 → 经纬度 (迭代一次)
double[] approx = Transform(projCs, Wgs84, east, north);
double latRad = approx[1] * Math.PI / 180.0;
N = GetPrimeVerticalRadius(latRad);
double scaleInv = N / (N + h);
double east0 = falseE + (east - falseE) * scaleInv;
double north0 = falseN + (north - falseN) * scaleInv;
double[] final = Transform(projCs, Wgs84, east0, north0);
五、使用教程
5.1 环境准备
- Visual Studio 2017及以上,新建.NET Framework 4.7.2或.NET 6.0+的WinForm项目;
5.2 常规UTM转换示例

-
选择"经纬度 → UTM";
-
经纬度格式选择"度";
-
在左侧输入:
-35 150.44(南纬35°,东经150.44°); -
点击"转换",输出:
266368.9656 6123962.0530 56 H(东、北坐标保留四位小数)。
5.3 工程坐标系示例
-
在"工程坐标系"选项卡中设置:
-
中央子午线:
160.40(度分秒格式,表示160°40′) -
尺度因子:1.0
-
东偏移:500000,北偏移:0
-
默认椭球高:0
-
-
选择"经纬度 → 投影",格式"度分秒";
-
输入:
-35.000000 150.262400(150°26′24″),可选椭球高如:-35.000000 150.262400 150.5 -
输出会显示投影后的东、北坐标及椭球高;当高程不为0时,平面坐标会按比例放大(因为 N+h > N )。
5.4 批量处理
在两个输入框中可粘贴多行数据,例如:

-35 150.44
-34 151.0
266368.9656 6123962.053 56 H
程序会逐行计算,结果统一显示在右侧输出框,错误行会以[行N错误]提示。
六、总结
本文实现了一个基于WGS-84椭球、ProjNet库的横轴墨卡托投影工具箱,涵盖了从标准UTM转换到任意带工程坐标系的正反算,并创新性地引入了椭球高归化改正,使得高程数据不再流于形式。通过灵活的度分秒/度格式切换、批量输入输出、参数自由配置,大幅提升了测绘数据处理的便捷性与准确性。
完整源代码已开放,读者可根据需要进一步扩展多椭球支持、七参数转换等功能。该工具轻量、无数据库依赖,适合集成到各类地理信息生产流程中。
参考资料
-
ProjNet 官方仓库:https://github.com/NetTopologySuite/ProjNet
-
孔祥元等. 大地测量学基础M. 武汉大学出版社, 2010.
-
USGS UTM 投影文档:https://pubs.usgs.gov/pp/1395/report.pdf
