在做中石油项目的过程中,发现他们会使用一种不同于一般 GPS 经纬度坐标的坐标系统,叫大地坐标,包含横坐标、纵坐标、带号。好像有的还有 Z 坐标。这儿记录一下把包含 X 坐标、Y 坐标和带号的大地坐标转换为 BJ54、XA80 和 WGS84 的公里网坐标的代码。使用 C# 语言。
using System;
namespace CoordinateTransportation
{
static class Program
{
static void Main()
{
do
{
Console.WriteLine("Please input your Ground Coordinate:");
Console.Write("X:");
var x = Console.ReadLine().Trim();
Console.Write("Y:");
var y = Console.ReadLine().Trim();
Console.Write("Belt number:");
var beltNumber = Console.ReadLine().Trim();
CoordinateTransportation(double.Parse(x), double.Parse(y), double.Parse(beltNumber), out var gpsX, out var gpsY);
var gcj02Coordinate = gps84_To_Gcj02(gpsY, gpsX);
Console.WriteLine("The GPS Coordinate result of Ground Coordinate {0}, {1} is {2}, {3}", x, y, gcj02Coordinate[0], gcj02Coordinate[1]);
} while (true);
}
private static double a = 6378245.0;
private static double ee = 0.00669342162296594323;
/// <summary>
/// 坐标转换
/// </summary>
/// <param name="x">公里网横坐标 X</param>
/// <param name="y">公里网纵坐标 Y</param>
/// <param name="beltNumber">带号</param>
/// <param name="longitude">大地坐标经度</param>
/// <param name="latitude">大地坐标纬度</param>
private static void CoordinateTransportation(double x, double y, double beltNumber, out double longitude, out double latitude)
{
int projNo; ////带宽
double x0, y0;
double e1, e2, f, a, ee, NN, T, C, M, D, R, u, fai, iPI;
iPI = Math.PI / 180.0;
//a = 6378245.0; f = 1.0 / 298.3; //BJ54 参数
a = 6378140.0; f = 1 / 298.257; //XA80 参数
a = 6378137.0; f = 1 / 298.257223563; // WGS84 参数
projNo = (int)(x / 1000000L); //查找带号
var longitude0 = beltNumber * 3;
longitude0 = longitude0 * iPI; //中央经线
x0 = projNo * 1000000L + 500000L;
y0 = 0;
var xval = x - x0; var yval = y - y0; //带内大地坐标
e2 = 2 * f - f * f;
e1 = (1.0 - Math.Sqrt(1 - e2)) / (1.0 + Math.Sqrt(1 - e2));
ee = e2 / (1 - e2);
M = yval;
u = M / (a * (1 - e2 / 4 - 3 * e2 * e2 / 64 - 5 * e2 * e2 * e2 / 256));
fai = u + (3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * Math.Sin(2 * u) + (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * Math.Sin(
4 * u)
+ (151 * e1 * e1 * e1 / 96) * Math.Sin(6 * u) + (1097 * e1 * e1 * e1 * e1 / 512) * Math.Sin(8 * u);
C = ee * Math.Cos(fai) * Math.Cos(fai);
T = Math.Tan(fai) * Math.Tan(fai);
NN = a / Math.Sqrt(1.0 - e2 * Math.Sin(fai) * Math.Sin(fai));
R = a * (1 - e2) / Math.Sqrt((1 - e2 * Math.Sin(fai) * Math.Sin(fai)) * (1 - e2 * Math.Sin(fai) * Math.Sin(fai)) * (1 - e2 * Math.Sin
(fai) * Math.Sin(fai)));
D = xval / NN;
//计算经度(Longitude) 纬度(Latitude)
var longitude1 = longitude0 + (D - (1 + 2 * T + C) * D * D * D / 6 + (5 - 2 * C + 28 * T - 3 * C * C + 8 * ee + 24 * T * T) * D
* D * D * D * D / 120) / Math.Cos(fai);
var latitude1 = fai - (NN * Math.Tan(fai) / R) * (D * D / 2 - (5 + 3 * T + 10 * C - 4 * C * C - 9 * ee) * D * D * D * D / 24
+ (61 + 90 * T + 298 * C + 45 * T * T - 256 * ee - 3 * C * C) * D * D * D * D * D * D / 720);
//转换为度 DD
longitude = longitude1 / iPI;
latitude = latitude1 / iPI;
}
private static double[] gps84_To_Gcj02(double lat, double lon)
{
if (OutOfChina(lat, lon))
{
return new[] { lat, lon };
}
var dLat = TransformLat(lon - 105.0, lat - 35.0);
var dLon = TransformLon(lon - 105.0, lat - 35.0);
var radLat = lat / 180.0 * Math.PI;
var magic = Math.Sin(radLat);
magic = 1 - ee * magic * magic;
var sqrtMagic = Math.Sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * Math.PI);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.Cos(radLat) * Math.PI);
var mgLat = lat + dLat;
var mgLon = lon + dLon;
return new[] { mgLat, mgLon };
}
private static bool OutOfChina(double lat, double lon)
{
if (lon is < 72.004 or > 137.8347)
return true;
if (lat is < 0.8293 or > 55.8271)
return true;
return false;
}
private static double TransformLat(double x, double y)
{
var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y
+ 0.2 * Math.Sqrt(Math.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 * Math.Sin(y * Math.PI / 30.0)) * 2.0 / 3.0;
return ret;
}
private static double TransformLon(double x, double y)
{
var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1
* Math.Sqrt(Math.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;
}
}
}