经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C# » 查看文章
工程坐标转换方法C#代码实现
来源:cnblogs  作者:Aidan_Lee  时间:2022/12/5 8:50:06  对本文有异议

1. 前言

前面的文章中系统的阐述了工程坐标的转换类别和转换的方法。关于转换代码实现,有很多的类库:

这里针对GPS接收的WGS84椭球的经纬度转换为地方坐标系的问题,利用C#,对工程坐标转换方法和步骤做出详细的解答。不基于任何类库和函数库,也未使用矩阵库,可以便利的将代码移植到任何语言。


2. 计算总体框架

根据上一篇文章中对七参数、四参数、高程拟合在坐标转换的作用和使用条件的阐述,我们可以将上一篇文章第7节的总结图,按照计算的流程重新绘制。
image

根据上图可知,预将WGS84椭球的GPS坐标需要经过5次转换。其中,

  1. 转换1、转换3在charlee44的博客:大地经纬度坐标与地心地固坐标的转换中详细讲解了,并且有C++代码的实现,利用C#重构即可。
  2. 转换2、转换5,以及他们的组合,在我的上一篇文章(工程)坐标转换类别和方法也详细的讲解了。

因此,根据计算原理,直接可以利用C#代码实现。


3. C#代码实现

3.1 整体类的构建

5个转换是对点的操作,不妨构建自定义点类MyPoint,在这个类中定义转换方法。在实现转换方法之前,需要定义数据属性,以承载转换参数和转换数据。代码框架如下:

  1. internal class MyPoint
  2. {
  3. // 定义椭球类型。这里仅列举了4中国内常见的椭球类型
  4. // 国际椭球可以增加自行定义
  5. public enum EllipsoidType
  6. {
  7. WGS84,
  8. CGCS2000,
  9. 西安80,
  10. 北京54
  11. }
  12. //大地坐标经度、维度、高度
  13. public double L { get; set; }
  14. public double B { get; set; }
  15. public double H { get; set; }
  16. //空间坐标系
  17. public double X { get; set; }
  18. public double Y { get; set; }
  19. public double Z { get; set; }
  20. //七参数转换后的空间坐标
  21. public double X2 { get; set; }
  22. public double Y2 { get; set; }
  23. public double Z2 { get; set; }
  24. private double a = 0, f = 0, b = 0, e = 0, e2 = 0; //椭球参数
  25. private readonly double rho = 180 / Math.PI;
  26. private readonly double d2r = Math.PI / 180;
  27. public double Xs { get; set; }
  28. public double Ys { get; set; }
  29. public double Hs { get; set; }
  30. //七参数 三个线性平移量-单位米 三个旋转平移量-十进制秒为单位(运算时注意转换为度) 比例因子-单位百万分率 (ppm)
  31. //测量队给出的七参数单位与计算的单位不同,要进行单位转化 1 秒=0.0000048481373323 弧度
  32. //尺度因子有两种单位的表示形式,一种结果约为1,如1.0000045,用k表示;
  33. //另一种就是ppm的表示形式,稍微比1大一点,如4.5,用m表示。k=m/1000000
  34. private double dx = 0, dy = 0, dz = 0, rx = 0, ry = 0, rz = 0, m = 0, k = 0;
  35. }

3.2 椭球参数赋值

常见的椭球参数值在我的文章经纬度坐标转换为工程坐标可以找到,这里选取与上述代码对应的4类椭球,并在上述MyPoint类中增加函数EllipsoidParam(EllipsoidType type)

  1. /// <summary>
  2. /// 椭球参数设置
  3. /// </summary>
  4. /// <param name="type">椭球类型</param>
  5. private void EllipsoidParam(EllipsoidType type)
  6. {
  7. // CGCS2000 椭球参数
  8. if (type == EllipsoidType.CGCS2000)
  9. {
  10. this.a = 6378137;
  11. this.f = 1 / 298.257222101;
  12. }
  13. // 西安 80
  14. else if (type == EllipsoidType.西安80)
  15. {
  16. this.a = 6378140;
  17. this.f = 1 / 298.257;
  18. }
  19. // 北京 54
  20. else if (type == EllipsoidType.北京54)
  21. {
  22. this.a = 6378245;
  23. this.f = 1 / 298.3;
  24. }
  25. // WGS-84
  26. else
  27. {
  28. this.a = 6378137;
  29. this.f = 1 / 298.257223563;
  30. }
  31. this.b = this.a * (1 - this.f);
  32. this.e = Math.Sqrt(this.a * this.a - this.b * this.b) / this.a; //第一偏心率
  33. this.e2 = Math.Sqrt(this.a * this.a - this.b * this.b) / this.b; //第二偏心率
  34. }

3.3 转换1、3(大地经纬度坐标与地心地固坐标的转换)

charlee44的博客有C++代码的实现,现在利用C#重构即可。上述MyPoint类中增加BLH2XYZ(EllipsoidType type)XYZ2BLH(EllipsoidType type)两个函数

  1. /// <summary>
  2. /// 经纬度坐标转空间直角坐标
  3. /// </summary>
  4. /// <param name="type">椭球类型</param>
  5. public void BLH2XYZ(EllipsoidType type = EllipsoidType.WGS84)
  6. {
  7. EllipsoidParam(type);
  8. double sB = Math.Sin(this.B * d2r);
  9. double cB = Math.Cos(this.B * d2r);
  10. double sL = Math.Sin(this.L * d2r);
  11. double cL = Math.Cos(this.L * d2r);
  12. double N = this.a / (Math.Sqrt(1 - this.e * this.e * sB * sB));
  13. this.X = (N + this.H) * cB * cL;
  14. this.Y = (N + this.H) * cB * sL;
  15. this.Z = (N * (1 - this.e * this.e) + this.H) * sB;
  16. this.X2 = this.X;
  17. this.Y2 = this.Y;
  18. this.Z2 = this.Z;
  19. }
  20. /// <summary>
  21. /// 空间直角坐标转经纬度坐标
  22. /// </summary>
  23. /// <param name="type">椭球类型</param>
  24. public void XYZ2BLH(EllipsoidType type)
  25. {
  26. EllipsoidParam(type);
  27. // 这里转出来的B L是弧度
  28. this.L = Math.Atan(this.Y2 / this.X2) + Math.PI;
  29. this.L = this.L * 180 / Math.PI;
  30. // B需要迭代计算
  31. double B2 = Math.Atan(Z2 / Math.Sqrt(X2 * X2 + Y2 * Y2));
  32. double B1;
  33. double N;
  34. while (true)
  35. {
  36. N = a / Math.Sqrt(1 - f * (2 - f) * Math.Sin(B2) * Math.Sin(B2));
  37. B1 = Math.Atan((Z2 + N * f * (2 - f) * Math.Sin(B2)) / Math.Sqrt(X2 * X2 + Y2 * Y2));
  38. if (Math.Abs(B1 - B2) < 1e-12)
  39. break;
  40. B2 = B1;
  41. }
  42. this.B = B2 * 180 / Math.PI;
  43. double sB = Math.Sin(this.B * d2r);
  44. double cB = Math.Cos(this.B * d2r);
  45. this.H = this.Z2 / sB - N * (1 - this.e * this.e);
  46. }

3.4 投影转换

此处仅实现了常见的高斯-克里格投影。上述MyPoint类中增加GaussProjection(EllipsoidType type, ProjectionSetting prjSetting)函数

  1. /// <summary>
  2. /// 利用高斯投影将指定椭球类型的经纬度坐标转为投影坐标
  3. /// </summary>
  4. /// <param name="type">椭球类型</param>
  5. /// <param name="prjSetting">投影设置实例</param>
  6. public void GaussProjection(EllipsoidType type, ProjectionSetting prjSetting)
  7. {
  8. this.EllipsoidParam(type);
  9. double l = (this.L - prjSetting.CenterL) / this.rho;
  10. double cB = Math.Cos(this.B * this.d2r);
  11. double sB = Math.Sin(this.B * this.d2r);
  12. double s2b = Math.Sin(this.B * this.d2r * 2);
  13. double s4b = Math.Sin(this.B * this.d2r * 4);
  14. double s6b = Math.Sin(this.B * this.d2r * 6);
  15. double s8b = Math.Sin(this.B * this.d2r * 8);
  16. double N = this.a / Math.Sqrt(1 - this.e * this.e * sB * sB); // 卯酉圈曲率半径
  17. double t = Math.Tan(this.B * this.d2r);
  18. double eta = this.e2 * cB;
  19. double m0 = this.a * (1 - this.e * this.e);
  20. double m2 = 3.0 / 2.0 * this.e * this.e * m0;
  21. double m4 = 5.0 / 4.0 * this.e * this.e * m2;
  22. double m6 = 7.0 / 6.0 * this.e * this.e * m4;
  23. double m8 = 9.0 / 8.0 * this.e * this.e * m6;
  24. double a0 = m0 + 1.0 / 2.0 * m2 + 3.0 / 8.0 * m4 + 5.0 / 16.0 * m6 + 35.0 / 128.0 * m8;
  25. double a2 = 1.0 / 2.0 * m2 + 1.0 / 2.0 * m4 + 15.0 / 32.0 * m6 + 7.0 / 16.0 * m8;
  26. double a4 = 1.0 / 8.0 * m4 + 3.0 / 16.0 * m6 + 7.0 / 32.0 * m8;
  27. double a6 = 1.0 / 32.0 * m6 + 1.0 / 16.0 * m8;
  28. double a8 = 1.0 / 128.0 * m8;
  29. // X1为自赤道量起的子午线弧长
  30. double X1 = a0 * (this.B * this.d2r) - 1.0 / 2.0 * a2 * s2b + 1.0 / 4.0 * a4 * s4b - 1.0 / 6.0 * a6 * s6b + 1.0 / 8.0 * a8 * s8b;
  31. this.Xs = X1 + N / 2 * t * cB * cB * l * l + N / 24 * t * (5 - t * t + 9 * Math.Pow(eta, 2) + 4 * Math.Pow(eta, 4)) * Math.Pow(cB, 4) * Math.Pow(l, 4)
  32. + N / 720 * t * (61 - 58 * t * t + Math.Pow(t, 4)) * Math.Pow(cB, 6) * Math.Pow(l, 6);
  33. this.Ys = N * cB * l + N / 6 * (1 - t * t + eta * eta) * Math.Pow(cB, 3) * Math.Pow(l, 3)
  34. + N / 120 * (5 - 18 * t * t + Math.Pow(t, 4) + 14 * Math.Pow(eta, 2) - 58 * eta * eta * t * t) * Math.Pow(cB, 5) * Math.Pow(l, 5);
  35. this.Hs = this.H;
  36. // 假东 假北偏移
  37. this.Xs += prjSetting.PseudoNorth;
  38. this.Ys += prjSetting.PseudoEast;
  39. }

其中,ProjectionSetting是一个投影参数设置类,独立于MyPoint,用于设定中央经线、东偏等投影参数

  1. internal class ProjectionSetting
  2. {
  3. private double _centerL;
  4. public double CenterL
  5. {
  6. get { return _centerL; }
  7. set { _centerL = value; }
  8. }
  9. private double _centerB;
  10. public double CenterB
  11. {
  12. get { return _centerB; }
  13. set { _centerB = value; }
  14. }
  15. private double _pseudoEast;
  16. public double PseudoEast
  17. {
  18. get { return _pseudoEast; }
  19. set { _pseudoEast = value; }
  20. }
  21. private double _pseudoNorth;
  22. public double PseudoNorth
  23. {
  24. get { return _pseudoNorth; }
  25. set { _pseudoNorth = value; }
  26. }
  27. private double _prjScale;
  28. public double PrjScale
  29. {
  30. get { return _prjScale; }
  31. set { _prjScale = value; }
  32. }
  33. /// <summary>
  34. /// 设置全部的投影参数
  35. /// </summary>
  36. /// <param name="centerL"></param>
  37. /// <param name="centerB"></param>
  38. /// <param name="pseudoEast"></param>
  39. /// <param name="pseudoNorth"></param>
  40. /// <param name="prjScale"></param>
  41. public ProjectionSetting(double centerL, double centerB,
  42. double pseudoEast, double pseudoNorth,
  43. double prjScale)
  44. {
  45. CenterL = centerL;
  46. CenterB = centerB;
  47. PseudoEast = pseudoEast;
  48. PseudoNorth = pseudoNorth;
  49. PrjScale = prjScale;
  50. }
  51. /// <summary>
  52. /// 仅设置中央经线和东偏
  53. /// </summary>
  54. /// <param name="centerL"></param>
  55. /// <param name="pseudoEast"></param>
  56. public ProjectionSetting(double centerL, double pseudoEast)
  57. {
  58. CenterL = centerL;
  59. CenterB = 0.0;
  60. PseudoEast = pseudoEast;
  61. PseudoNorth = 0.0;
  62. PrjScale = 1.0;
  63. }
  64. /// <summary>
  65. /// 默认常用投影参数,中央经线120°,东偏500000
  66. /// </summary>
  67. public ProjectionSetting()
  68. {
  69. CenterL = 120.0;
  70. CenterB = 0.0;
  71. PseudoEast = 500000;
  72. PseudoNorth = 0.0;
  73. PrjScale = 1.0;
  74. }
  75. }

3.5 转换2的实现(三参数、七参数)

上述MyPoint类中增加SevenParamTrans(Datum7Paras datum7Paras)TreeParamTrans(Datum3Paras datum3Paras)函数

  1. /// <summary>
  2. /// 利用7参数进行坐标系之间转换
  3. /// </summary>
  4. /// <param name="datum7Paras">7参数实例</param>
  5. public void SevenParamTrans(Datum7Paras datum7Paras)
  6. {
  7. this.dx = datum7Paras.Dx;
  8. this.dy = datum7Paras.Dy;
  9. this.dz = datum7Paras.Dz;
  10. this.rx = datum7Paras.Rx * 0.0000048481373323; //1 秒=0.0000048481373323 弧度
  11. this.ry = datum7Paras.Ry * 0.0000048481373323;
  12. this.rz = datum7Paras.Rz * 0.0000048481373323;
  13. this.m = datum7Paras.PPM;
  14. this.k = this.m / 1000000;
  15. this.X2 = (1 + k) * (this.X + this.rz * this.Y - this.ry * this.Z) + this.dx;
  16. this.Y2 = (1 + k) * (-this.rz * this.X + this.Y + this.rx * this.Z) + this.dy;
  17. this.Z2 = (1 + k) * (this.ry * this.X - this.rx * this.Y + this.Z) + this.dz;
  18. }
  19. /// <summary>
  20. /// 利用3参数进行坐标系之间转换
  21. /// </summary>
  22. /// <param name="datum3Paras">3参数实例</param>
  23. public void TreeParamTrans(Datum3Paras datum3Paras)
  24. {
  25. this.dx = datum3Paras.Dx;
  26. this.dy = datum3Paras.Dy;
  27. this.dz = datum3Paras.Dz;
  28. this.X2 = this.X + this.dx;
  29. this.Y2 = this.Y + this.dy;
  30. this.Z2 = this.Z + this.dz;
  31. }

Datum3ParasDatum7Paras独立于MyPoint,用于设定坐标转换参数

  1. /// <summary>
  2. /// 7参数
  3. /// </summary>
  4. internal class Datum7Paras
  5. {
  6. private double _dx;
  7. public double Dx
  8. {
  9. get { return _dx; }
  10. set { _dx = value; }
  11. }
  12. private double _dy;
  13. public double Dy
  14. {
  15. get { return _dy; }
  16. set { _dy = value; }
  17. }
  18. private double _dz;
  19. public double Dz
  20. {
  21. get { return _dz; }
  22. set { _dz = value; }
  23. }
  24. private double _rx;
  25. public double Rx
  26. {
  27. get { return _rx; }
  28. set { _rx = value; }
  29. }
  30. private double _ry;
  31. public double Ry
  32. {
  33. get { return _ry; }
  34. set { _ry = value; }
  35. }
  36. private double _rz;
  37. public double Rz
  38. {
  39. get { return _rz; }
  40. set { _rz = value; }
  41. }
  42. private double _ppm;
  43. public double PPM
  44. {
  45. get { return _ppm; }
  46. set { _ppm = value; }
  47. }
  48. public Datum7Paras(double dx, double dy, double dz,
  49. double rx, double ry, double rz,
  50. double ppm)
  51. {
  52. _dx = dx;
  53. _dy = dy;
  54. _dz = dz;
  55. _rx = rx;
  56. _ry = ry;
  57. _rz = rz;
  58. _ppm = ppm;
  59. }
  60. }
  1. internal class Datum3Paras
  2. {
  3. private double _dx;
  4. public double Dx
  5. {
  6. get { return _dx; }
  7. set { _dx = value; }
  8. }
  9. private double _dy;
  10. public double Dy
  11. {
  12. get { return _dy; }
  13. set { _dy = value; }
  14. }
  15. private double _dz;
  16. public double Dz
  17. {
  18. get { return _dz; }
  19. set { _dz = value; }
  20. }
  21. public Datum3Paras(double dx, double dy, double dz)
  22. {
  23. Dx = dx;
  24. Dy = dy;
  25. Dz = dz;
  26. }
  27. }

3.6 转换5的实现(四参数+高程拟合)

上述MyPoint类中增加Transform4Para(Trans4Paras transPara)函数。此处,高程拟合仅实现了已知一个测点的固定改正差

  1. /// <summary>
  2. /// 投影坐标获取后,进一步利用4参数转换坐标
  3. /// </summary>
  4. /// <param name="transPara"></param>
  5. public void Transform4Para(Trans4Paras transPara)
  6. {
  7. var X1 = transPara.Dx;
  8. var Y1 = transPara.Dy;
  9. var cosAngle = Math.Cos(transPara.A);
  10. var sinAngle = Math.Sin(transPara.A);
  11. X1 += transPara.K * (cosAngle * this.Xs - sinAngle * this.Ys);
  12. Y1 += transPara.K * (sinAngle * this.Xs + cosAngle * this.Ys);
  13. this.Xs = X1;
  14. this.Ys = Y1;
  15. // 固定改正差
  16. this.Hs += transPara.Dh;
  17. }

Trans4Paras独立于MyPoint,用于设定坐标转换参数

  1. internal class Trans4Paras
  2. {
  3. private double _dx;
  4. public double Dx
  5. {
  6. get { return _dx; }
  7. set { _dx = value; }
  8. }
  9. private double _dy;
  10. public double Dy
  11. {
  12. get { return _dy; }
  13. set { _dy = value; }
  14. }
  15. private double _a;
  16. public double A
  17. {
  18. get { return _a; }
  19. set { _a = value; }
  20. }
  21. private double _k;
  22. public double K
  23. {
  24. get { return _k; }
  25. set { _k = value; }
  26. }
  27. private double _dh;
  28. public double Dh
  29. {
  30. get { return _dh; }
  31. set { _dh = value; }
  32. }
  33. public Trans4Paras(double dx, double dy, double a, double k, double dh)
  34. {
  35. Dx = dx;
  36. Dy = dy;
  37. A = a;
  38. K = k;
  39. Dh = dh;
  40. }
  41. public Trans4Paras()
  42. {
  43. }
  44. }

3.7 调用过程

里面的参数,因为保密原因,做出了随机更改,实际使用时可根据自己情况赋值。

3.7.1 一步法

  1. // 实例化计算参数
  2. MyPoint p = new MyPoint();.
  3. p.L=113.256;
  4. p.B=31.565;
  5. p.H=5.216;
  6. // 经纬度转空间坐标
  7. p.BLH2XYZ();
  8. // 实例化七参数
  9. Datum7Paras datum7Paras = new Datum7Paras(
  10. 489.2994563566, 141.1525159753, 15.74421120568,
  11. -0.164423, 4.141573, -4.808299,
  12. -6.56482989958);
  13. p.SevenParamTrans(datum7Paras);
  14. // 空间坐标转回经纬度
  15. p.XYZ2BLH(EllipsoidType.WGS84);
  16. // 高斯投影 经纬度转平面坐标
  17. // 实例化投影参数类
  18. ProjectionSetting projectionSetting = new ProjectionSetting(120,500000);
  19. p.GaussProjection(EllipsoidType.WGS84, projectionSetting);

3.7.2 两步法

  1. // 实例化计算参数
  2. MyPoint p = new MyPoint();.
  3. p.SetLBH(113.256,31.565,5.216);
  4. // 经纬度转空间坐标
  5. p.BLH2XYZ();
  6. // 实例化七参数
  7. Datum7Paras datum7Paras = new Datum7Paras(
  8. 489.2994563566, 141.1525159753, 15.74421120568,
  9. -0.164423, 4.141573, -4.808299,
  10. -6.56482989958);
  11. p.SevenParamTrans(datum7Paras);
  12. // 空间坐标转回经纬度
  13. p.XYZ2BLH(EllipsoidType.WGS84);
  14. // 高斯投影 经纬度转平面坐标
  15. // 实例化投影参数类
  16. ProjectionSetting projectionSetting = new ProjectionSetting(120,500000);
  17. p.GaussProjection(EllipsoidType.WGS84, projectionSetting);
  18. Trans4Paras transformPara = new(6456.15957352521, -134618.390707439, 0.011104964500129, 1.00002537583871, 5.788);
  19. p.Transform4Para(transformPara);

4. 总结

至此,关于工程坐标系转化,即GPS接收的WGS84椭球的经纬度转换为地方坐标系的问题,基本全部实现。代码正确性和准确性的验证是与 南方GPS工具箱做对比。例如,采用上述的一步法,在设定好坐标、7参数、投影参数后,计算发现,与南方GPS工具箱在y方向偏差1mm。结果如下图:
image

原文链接:https://www.cnblogs.com/AidanLee/p/16950730.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号