坐标转换
坐标转换,简言之就是讲一个坐标系的坐标通过特定模型转换到另一坐标下;转换中,需要用到不同的转换模型,例如三维空间下的布尔莎七参数、莫洛金斯模型;二维空间下的平面三参、四参、多项式拟合等模型。本文中,主要使用布尔莎七参数模型进行坐标转换。
坐标转换关系
布尔莎模型(七参数模型)
详细见百科: https://baike.baidu.com/item/%E4%B8%83%E5%8F%82%E6%95%B0
程序实现
1.编写数据转换类
把各种格式表示的度分秒转换为弧度形式进行计算,最后一个方法把弧度转为度分秒连写的格式。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Text.RegularExpressions; namespace CoordTF { class DataCnversion { #region ////// 数据格式化,全转为度分秒(x.xxxx)连写格式 /// /// 大地坐标 /// 数据格式 ///public double DataFormatting(string oldCoord,string dataFormat) { double newCoord = 0.00; switch (dataFormat) { case "度°分'秒″": newCoord = DMSA_RAD(oldCoord); break; case "度:分:秒": newCoord = DMSB_RAD(oldCoord); break; case "度:分": break; case "度": newCoord = D_RAD(oldCoord); break; case "度分秒连写": newCoord = DMSLX_RAD(oldCoord); break; } return newCoord; } #endregion #region 各形式角度转弧度 double d, m, s; //度分秒 /// /// 度°分'秒"格式转弧度 /// /// 格式为XX ° XX ' XX.XX " ///public double DMSA_RAD(string dms) { double rad; var newD = dms.Substring(0, dms.IndexOf("°")); var newM = dms.Substring(dms.IndexOf("°")+1,dms.IndexOf("′")-3); var newS = dms.Substring(dms.IndexOf("′")+1,dms.Length-dms.IndexOf("′")-2); rad = (double.Parse(newD) + double.Parse(newM)/ 60 + double.Parse(newS) / 3600) * Math.PI / 180; return rad; } /// /// 度:分:秒 格式转弧度 /// /// 格式为度:分:秒 ///public double DMSB_RAD(string dms) { double rad; string[] newDms = dms.Split(':'); rad = (double.Parse(newDms[0].ToString()) + double.Parse(newDms[1].ToString()) / 60 + double.Parse(newDms[2].ToString()) / 3600) * Math.PI / 180; return rad; } /// /// 度分秒连写(x.xxxx)转弧度 /// /// 格式为x.xxxx ///public double DMSLX_RAD(string dms) { double rad; var newDms = double.Parse(dms); d = Math.Floor(newDms); //度 var x = (newDms - d) * 100; x = double.Parse(x.ToString("N8")); m = Math.Floor(x); //分 s = (x - m) * 100; m /= 60; //秒 s /= 3600; rad = (d + m + s) / 180 * (Math.PI); //弧度 return rad; } /// /// xx.xxxx° 格式转弧度 /// /// 角度,格式为XX.XXXX° ///public double D_RAD(string dms) { double rad; rad = (double.Parse(dms) * Math.PI / 180); return rad; } #endregion /// /// 弧度转角度(结果为xx.xxxxxx连写格式) /// /// 弧度值 ///public double RAD_DMS(string rad) { double dms; var newRad = double.Parse(rad); var d = newRad / Math.PI * 180; //度 var dStr = d.ToString("N8"); d = double.Parse(dStr); var x = Math.Floor(d); var m = (d - x) * 60; //分 var mStr = m.ToString("N8"); m = double.Parse(mStr); var f = Math.Floor(m); var s = (m - f) * 60; //秒 var sStr = s.ToString("N8"); s = double.Parse(sStr); dms = x + f / 100 + s / 10000; return dms; } } }
2.编写坐标转换类
包含空间与大地互转、高斯正反算、同一坐标系下换带计算、布尔莎七参数转换计算。
如果有想学习编程的初学者,可来我们的C/C++技术学习扣qun的哦:前面是58,93后面是4,83-89里面免费送整套系统的C/C++教程!
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using MathNet.Numerics.LinearAlgebra.Double; namespace CoordTF { class CoordTransform { ////// 大地坐标转空间直角 /// /// 纬度 /// 经度 /// 高度 /// 椭球长半轴 /// 扁率倒数 ///public double[] BLH_XYZ(double B, double L, double H, double a, double fl,string dataFormat) { var f = 1 / fl; var e2 = 2 * f - f * f; DataCnversion dataCnversion = new DataCnversion(); B = dataCnversion.DataFormatting(B.ToString(), dataFormat); L = dataCnversion.DataFormatting(L.ToString(), dataFormat); var W = Math.Sqrt(1 - e2 * (Math.Sin(B) * Math.Sin(B))); var N = a / W; var X = (N + H) * Math.Cos(B) * Math.Cos(L); var Y = (N + H) * Math.Cos(B) * Math.Sin(L); var Z = (N * (1 - e2) + H) * Math.Sin(B); double[] XYZ = new double[3]; XYZ[0] = X; XYZ[1] = Y; XYZ[2] = Z; return XYZ; } /// /// 空间直角转大地坐标 /// /// 空间X /// 空间Y /// 空间Z /// 椭球长半轴 /// 扁率倒数 ///public double[] XYZ_BLH(double X, double Y, double Z, double a, double fl) { var f = 1 / fl; var e2 = 2 * f - f * f; var S = Math.Sqrt(X * X + Y * Y); var L = Math.Acos(X / S); if (Y < 0) L = -L; var Rho = 206264.806247096363; var Error = 0.00001; var B1 = Math.Atan(Z / S); double B2; while (true) { var W1 = Math.Sqrt(1 - e2 * (Math.Sin(B1) * Math.Sin(B1))); var N1 = a / W1; B2 = Math.Atan((Z + N1 * e2 * Math.Sin(B1)) / S); if (Math.Abs(B2 - B1) * Rho <= Error) break; B1 = B2; } var B = B2; var W = Math.Sqrt(1 - e2 * (Math.Sin(B) * Math.Sin(B))); var N = a / W; var H = S / Math.Cos(B) - N; DataCnversion dataCnversion = new DataCnversion(); B = dataCnversion.RAD_DMS(B.ToString("N8")); L = dataCnversion.RAD_DMS(L.ToString("N8")); double[] BLH = new double[3]; BLH[0] = B; BLH[1] = L; BLH[2] = H; return BLH; } /// /// 布尔莎七参转换 /// /// 空间直角X /// 空间直角Y /// 空间直角Z /// 平移参数x /// 平移参数y /// 平移参数z /// 旋转参数x /// 旋转参数y /// 旋转参数z /// 尺度参数k ///public double[] BursaTF(double X, double Y, double Z, double dx, double dy, double dz, double rpx, double rpy, double rpz, double k) { var Rho = 206264.806247096355;//常数 double A15, A16, A17, A24, A26, A27, A34, A35, A37; A15 = Z / Rho; A16 = Y / Rho; A17 = X / 1000000; A24 = Z / Rho; A26 = X / Rho; A27 = Y/ 1000000; A34 = Y / Rho; A35 = X / Rho; A37 = Z / 1000000; var newX = X + dx - A15 * rpy + A16 * rpz + A17 * k; var newY = Y + dy + A24 * rpx - A26 * rpz + A27 * k; var newZ = Z + dz - A34 * rpx + A35 * rpy + A37 * k; double[] result = { newX, newY, newZ }; return result; } /// /// 高斯正算 /// /// 大地纬度 /// 大地经度 /// 中央子午线经度(度°分′秒″格式) /// 椭球长半径 /// 扁率倒数 /// x常参 /// y常参 ///public double[] BL_xy(double B, double L, double L0, double a, double f1, double xCon, double yCon) { DataCnversion dataCnversion = new DataCnversion(); var f = 1 / f1; var e2 = 2 * f - f * f; var e12 = e2 / (1 - e2); B = dataCnversion.DMSLX_RAD(B.ToString("N8")); L = dataCnversion.DMSLX_RAD(L.ToString("N8")); L0 = dataCnversion.DMSLX_RAD(L0.ToString("N8")); var l = L - L0; var A0 = 1 + 3 * e2 / 4 + 45 * Math.Pow(e2, 2) / 64 + 350 * Math.Pow(e2, 3) / 512 + 11025 * Math.Pow(e2, 4) / 16384; var A2 = -(3 * e2 / 4 + 60 * Math.Pow(e2, 2) / 64 + 525 * Math.Pow(e2, 3) / 512 + 17640 * Math.Pow(e2, 4) / 16384) / 2; var A4 = (15 * Math.Pow(e2, 2) / 64 + 210 * Math.Pow(e2, 3) / 512 + 8820 * Math.Pow(e2, 4) / 16384) / 4; var A6 = -(35 * Math.Pow(e2, 3) / 512 + 2520 * Math.Pow(e2, 4) / 16384) / 6; var A8 = (315 * Math.Pow(e2, 4) / 16384) / 8; var X = a * (1 - e2) * (A0 * B + A2 * Math.Sin(2 * B) + A4 * Math.Sin(4 * B) + A6 * Math.Sin(6 * B) + A8 * Math.Sin(8 * B)); var m0 = l * Math.Cos(B); var t = Math.Tan(B); var n2 = e12 * Math.Pow(Math.Cos(B), 2); var W = Math.Sqrt(1 - e2 * Math.Pow(Math.Sin(B), 2)); var N = a / W; var Nt = N * t; double x = X + (Nt * Math.Pow(m0, 2) / 2) + (Nt * (5 - Math.Pow(t, 2) + (9 * n2) + (4 * Math.Pow(n2, 2))) * Math.Pow(m0, 4) / 24) + (Nt * (61 - (58 * Math.Pow(t, 2)) + Math.Pow(t, 4) + (270 * n2) - (330 * n2 * Math.Pow(t, 2))) * Math.Pow(m0, 6) / 720); double y = (N * m0) + (N * (1 - Math.Pow(t, 2) + n2) * Math.Pow(m0, 3) / 6) + (N * (5 - (18 * Math.Pow(t, 2)) + Math.Pow(t, 4) + (14 * n2) - (58 * n2 * Math.Pow(t, 2))) * Math.Pow(m0, 5) / 120); x += xCon; y += yCon; double[] point = new double[2]; point[0] = x; point[1] = y; return point; } /// /// 高斯反算 /// /// 平面坐标x /// 平面坐标y /// 中央子午线经度 /// 椭球长半轴 /// 扁率倒数 /// x常参数 /// y常参数 ///public double[] Xy_BL(double x, double y, double L0, double a, double f1, double xCon, double yCon) { var k = 1000000; x -= xCon; if (Math.Abs(y) >= k) { var zoneNumber = Math.Floor(y / k); y -= zoneNumber; } y -= yCon; var f = 1 / f1; var e2 = 2 * f - f * f; var e12 = e2 / (1 - e2); DataCnversion dataCnversion = new DataCnversion(); L0 = dataCnversion.DMSLX_RAD(L0.ToString("N8")); var A0 = 1 + 3 * e2 / 4 + 45 * Math.Pow(e2, 2) / 64 + 350 * Math.Pow(e2, 3) / 512 + 11025 * Math.Pow(e2, 4) / 16384; var B0 = x / (a * (1 - e2) * A0); var K0 = (3 * e2 / 4 + 45 * Math.Pow(e2, 2) / 64 + 350 * Math.Pow(e2, 3) / 512 + 11025 * Math.Pow(e2, 4) / 16384) / 2; var K2 = -(63 * Math.Pow(e2, 2) / 64 + 1108 * Math.Pow(e2, 3) / 512 + 58239 * Math.Pow(e2, 4) / 16384) / 3; var K4 = (604 * Math.Pow(e2, 3) / 512 + 68484 * Math.Pow(e2, 4) / 16384) / 3; var K6 = -(26328 * Math.Pow(e2, 4) / 16384) / 3; var sB0 = Math.Pow(Math.Sin(B0), 2); var Bf = B0 + Math.Sin(2 * B0) * (K0 + sB0 * (K2 + sB0 * (K4 + K6 * sB0))); var nf2 = e12 * Math.Pow(Math.Cos(Bf), 2); var tf = Math.Tan(Bf); var tf2 = tf * tf; var tf4 = Math.Pow(tf, 4); var Wf = Math.Sqrt(1 - e2 * (Math.Pow(Math.Sin(Bf), 2))); var Nf = a / Wf; var yNf = y / Nf; var Vf2 = 1 + nf2; var B = Bf - 0.5 * Vf2 * tf * (Math.Pow(yNf, 2) - (5 + 3 * tf2 + nf2 - 9 * nf2 * tf2) * Math.Pow(yNf, 4) / 12 + (61 + 90 * tf2 + 45 * tf4) * Math.Pow(yNf, 6) / 360); var l = (yNf - (1 + 2 * tf2 + nf2) * Math.Pow(yNf, 3) / 6 + (5 + 28 * tf2 + 24 * tf4 + 6 * nf2 + 8 * nf2 * tf2) * Math.Pow(yNf, 5) / 120) / Math.Cos(Bf); var L = L0 + l; B = dataCnversion.RAD_DMS(B.ToString("N8")); L = dataCnversion.RAD_DMS(L.ToString("N8")); double[] point = new double[2]; point[0] = B; point[1] = L; return point; } /// /// 同一坐标系下坐标换带 /// /// /// /// 换带前经度 /// 换带后经度 /// /// /// /// ///public double[] CoordChangeBelt(double x, double y, double l1, double l2, double a, double f1, double xCon, double yCon) { double[] xy2BL = Xy_BL(x, y, l1, a, f1, xCon, yCon); var B = xy2BL[0]; var L = xy2BL[1]; double[] BL2xy = BL_xy(B, L, l2, a, f1, xCon, yCon); return BL2xy; } } }
到此,所有的基础转换类完成,使用时依据坐标转换关系,依次调用类中的方法即可。