pile_nav_new/lib/models/pilePoint/coord_trans.dart
2024-08-26 18:25:21 +08:00

684 lines
19 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ignore_for_file: non_constant_identifier_names
import 'dart:math';
class CoordGPS {
double LAT; //纬度
double LNG; //经度
double? ALT; //高度
CoordGPS({this.ALT, required this.LAT, required this.LNG});
}
class CoordXYH {
double X;
double Y;
double H;
CoordXYH({required this.X, required this.Y, required this.H});
}
class CoordBLH {
double B;
double L;
double H;
CoordBLH({required this.B, required this.H, required this.L});
}
class CoordXYZ {
double X;
double Y;
double Z;
CoordXYZ({required this.X, required this.Y, required this.Z});
}
class PointXY {
double X;
double Y;
PointXY({required this.X, required this.Y});
}
class Region {
double X;
double Y;
double? width;
double? heigth;
double? D;
Region({this.D, required this.X, required this.Y, this.heigth, this.width});
}
class Ellipsoid {
late double A;
late double F;
late double B;
late double E12;
late double E22;
Ellipsoid(String name) {
const Map<String, Map<String, double>> datums = {
'BJ54': {'a': 6378245, 'f': 1 / 298.3},
'XA80': {'a': 6378140, 'f': 1 / 298.257},
'WGS84': {'a': 6378137, 'f': 1 / 298.257223563},
'WGS2000': {'a': 6378137, 'f': 1 / 298.257222101}
};
var Datum = datums[name];
if (Datum != null) {
double a = Datum['a']!;
double f = Datum['f']!;
double b = a - f * a;
double e12 = (a * a - b * b) / (a * a); // 第一偏心率平方
double e22 = (a * a - b * b) / (b * b); // 第二偏心率平方
A = a;
F = f;
B = b;
E12 = e12;
E22 = e22;
}
}
}
class TransParm {
late double dx;
late double dy;
late double dz;
late double rx;
late double ry;
late double rz;
late double rota;
late double k;
TransParm(TransOptions options) {
dx = options.dx ?? 0;
dy = options.dy ?? 0;
dz = options.dz ?? 0;
rx = options.rx ?? 0;
ry = options.ry ?? 0;
rz = options.rz ?? 0;
k = options.k ?? 0;
rota = options.rota ?? 0;
}
}
class TransOptions {
double? dx; //X平移(M)
double? dy; //Y平移(M)
double? dz; //z平移(M)
double? rx; //X旋转(°)
double? ry; //Y旋转(°)
double? rz; //Z旋转(°)
double? rota; //平面旋转用于4参数
double? k; //尺度(ppm)
double? L0; //中央子午线(经度)
String? srcEllipsoid; //源椭球名称
String? dstEllipsoid; //目标椭球名称
double? zoneWidth; //带宽度,用于未指定中央子午线时自动计算
double? elevation; //投影高程
TransOptions({
this.L0,
this.dstEllipsoid,
this.dx,
this.dy,
this.dz,
this.elevation,
this.k,
this.rota,
this.rx,
this.ry,
this.rz,
this.srcEllipsoid,
});
}
//主天线
class CoordTrans {
late double L0; //中央子午线
late double zoneWidth; //带宽度
late double elevation; //投影高程
late double rota; //平面旋转用于4参数
late Ellipsoid srcEllipsoid; //源椭球
late Ellipsoid dstEllipsoid; //目标椭球
late TransParm transParm; //7参数
CoordTrans(TransOptions options) {
transParm = TransParm(options);
L0 = options.L0 ?? 0;
elevation = options.elevation ?? 0;
srcEllipsoid = Ellipsoid(options.srcEllipsoid ?? 'WGS84');
dstEllipsoid = Ellipsoid(options.dstEllipsoid ?? 'WGS2000');
zoneWidth = options.zoneWidth ?? 3;
}
CoordXYH GPS2XY(CoordGPS gps) {
return d2p(CoordBLH(H: gps.ALT ?? elevation, B: gps.LAT, L: gps.LNG));
}
//经纬度转平面坐标
CoordXYH d2p(CoordBLH blh) {
CoordXYZ zj = BLH2XYZ(blh);
CoordXYZ xzj = XYZ2XYZ(zj);
CoordBLH xdd = XYZ2BLH(xzj);
CoordXYH xpm = BL2XY(xdd);
return xpm;
}
//平面坐标转大地坐标
CoordBLH p2d(CoordXYH xyh) {
CoordBLH bl = XY2BL(xyh, true);
CoordXYZ zj = BLH2XYZ(bl, true);
CoordXYZ xzj = XYZ2XYZ(zj, true);
CoordBLH xdd = XYZ2BLH(xzj, true);
return xdd;
}
setZoneWidth(width) {
if (width != 6 && width != 3) {
// print('带宽应当为6或者为3');
} else {
zoneWidth = width;
}
}
setL0(l0) {
L0 = l0;
}
int getZoneNo(double lng) {
// 6度带
// 这里应为向下取整
if (zoneWidth == 6) {
return ((lng + 6) / 6).floor();
}
// 3度带
return ((lng + 1.5) / 3).floor();
}
PointXY xy2xyLocal(PointXY source) {
double x = source.X;
double y = source.Y;
double destx = x * transParm.k * cos(rota) -
y * transParm.k * sin(rota) +
transParm.dx;
double desty = x * transParm.k * sin(rota) +
y * transParm.k * cos(rota) +
transParm.dy;
return PointXY(X: destx, Y: desty);
}
CoordXYZ BLH2XYZ(CoordBLH pointBLH, [srcDst = false]) {
Ellipsoid ellipsoid = srcDst ? srcEllipsoid : dstEllipsoid;
double a = ellipsoid.A;
double e12 = ellipsoid.E12;
double radB = pointBLH.B / 180 * pi;
double radL = pointBLH.L / 180 * pi;
double H = pointBLH.H;
double N = a / sqrt(1 - e12 * sin(radB) * sin(radB)); // 卯酉圈半径
double X = (N + H) * cos(radB) * cos(radL);
double Y = (N + H) * cos(radB) * sin(radL);
double Z = (N * (1 - e12) + H) * sin(radB);
return CoordXYZ(X: X, Y: Y, Z: Z);
}
/*
七参数转换
不同椭球参数下, 地心直角坐标系之间转换
dX, dY, dZ: 三个坐标方向的平移参数
wX, wY, wZ: 三个方向的旋转角参数(单位为弧度)
Kppm: 尺度参数, 单位是ppm如果是以米为单位 需要在传参前 除以1000000
*/
CoordXYZ XYZ2XYZ(CoordXYZ xyz, [srcDst = false]) {
double X = xyz.X;
double Y = xyz.Y;
double Z = xyz.Z;
double dX = transParm.dx;
double dY = transParm.dy;
double dZ = transParm.dz;
double rX = transParm.rx / 3600 / 180 * pi;
double rY = transParm.ry / 3600 / 180 * pi;
double rZ = transParm.rz / 3600 / 180 * pi;
double Kppm = transParm.k / 1000000;
CoordXYZ result = CoordXYZ(X: 0, Y: 0, Z: 0);
if (srcDst) {
result.X = X - dX - Kppm * X + rY * Z - rZ * Y;
result.Y = Y - dY - Kppm * Y - rX * Z + rZ * X;
result.Z = Z - dZ - Kppm * Z + rX * Y - rY * X;
} else {
result.X = X + dX + Kppm * X - rY * Z + rZ * Y;
result.Y = Y + dY + Kppm * Y + rX * Z - rZ * X;
result.Z = Z + dZ + Kppm * Z - rX * Y + rY * X;
}
return result;
}
/*
地心直角坐标系 转换到 地心大地坐标系
用直接法2
https://wenku.baidu.com/view/30a08f9ddd88d0d233d46a50.html
*/
CoordBLH XYZ2BLH(CoordXYZ XYZ, [srcDst = false]) {
double X = XYZ.X;
double Y = XYZ.Y;
double Z = XYZ.Z;
Ellipsoid ellipsoid = srcDst ? srcEllipsoid : dstEllipsoid;
double a = ellipsoid.A;
double b = ellipsoid.B;
double e12 = ellipsoid.E12;
double e22 = ellipsoid.E22;
double L = atan(Y / X);
// 弧度转角度
double degL = L * 180 / pi;
// Y值为正 东半球, 否则西半球
if (Y > 0) {
while (degL < 0) {
degL += 180;
}
while (degL > 180) {
degL -= 180;
}
} else {
while (degL > 0) {
degL -= 180;
}
while (degL < -180) {
degL += 180;
}
}
double tgU = Z / (sqrt(X * X + Y * Y) * sqrt(1 - e12));
double U = atan(tgU);
double tgB = (Z + b * e22 * pow(sin(U), 3)) /
(sqrt(X * X + Y * Y) - a * e12 * pow(cos(U), 3));
double B = atan(tgB);
double degB = B * 180 / pi; // 弧度转角度
if (Z > 0) {
// Z值为正 北半球, 否则南半球
while (degB < 0) {
degB += 90;
}
while (degB > 90) {
degB -= 90;
}
} else {
while (degB > 0) {
degB -= 90;
}
while (degB < -90) {
degB += 90;
}
}
while (degB < 0) {
degB += 360;
}
while (degB > 360) {
degB -= 360;
}
double N = a / sqrt(1 - e12 * sin(B) * sin(B)); // 卯酉圈半径
double H = 0;
// B接近极区 在±90°附近
if ((degB).abs() > 80) {
H = Z / sin(B) - N * (1 - e12);
} else {
H = sqrt(X * X + Y * Y) / cos(B) - N;
}
return CoordBLH(B: degB, L: degL, H: H);
}
/*
地心大地坐标系 转换到 大地平面坐标系
prjHeight: 投影面高程
http://www.cnblogs.com/imeiba/p/5696967.html
*/
CoordXYH BL2XY(CoordBLH BLH,
[srcDst = false, offsetY = 500000, offsetX = 0]) {
Ellipsoid ellipsoid = srcDst ? srcEllipsoid : dstEllipsoid;
double a = ellipsoid.A;
double b = ellipsoid.B;
double e12 = ellipsoid.E12;
double e22 = ellipsoid.E22;
if (L0 == 0) {
int zoneNo = getZoneNo(BLH.L);
L0 = (zoneNo - 0.5) * zoneWidth;
}
double radL0 = L0 / 180 * pi;
double radB = BLH.B / 180 * pi;
double radL = BLH.L / 180 * pi;
double N = a / sqrt(1 - e12 * sin(radB) * sin(radB)); // 卯酉圈半径
double T = tan(radB) * tan(radB);
double C = e22 * cos(radB) * cos(radB);
double A = (radL - radL0) * cos(radB);
double M = a *
((1 - e12 / 4 - 3 * e12 * e12 / 64 - 5 * e12 * e12 * e12 / 256) * radB -
(3 * e12 / 8 + 3 * e12 * e12 / 32 + 45 * e12 * e12 * e12 / 1024) *
sin(2 * radB) +
(15 * e12 * e12 / 256 + 45 * e12 * e12 * e12 / 1024) *
sin(4 * radB) -
(35 * e12 * e12 * e12 / 3072) * sin(6 * radB));
//x,y的计算公式见孔祥元等主编武汉大学出版社2002年出版的《控制测量学》的第72页
//书的的括号有问题,( 和 [ 应该交换
double x = M +
N *
tan(radB) *
(A * A / 2 +
(5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24 +
(61 - 58 * T + T * T + 600 * C - 330 * e22) *
A *
A *
A *
A *
A *
A /
720);
double y = N *
(A +
(1 - T + C) * A * A * A / 6 +
(5 - 18 * T * T * T + 72 * C - 58 * e22) * A * A * A * A * A / 120);
x = offsetX + x * (b + elevation) / b;
y = offsetY + y * (b + elevation) / b;
return CoordXYH(
X: (x * 1000).round() / 1000, Y: (y * 1000).round() / 1000, H: BLH.H);
}
/*
大地平面坐标系 转换到 地心大地坐标系
prjHeight: 投影面高程
http://www.cnblogs.com/imeiba/p/5696967.html
*/
CoordBLH XY2BL(CoordXYH xyh,
[srcDst = false, offsetY = 500000, offsetX = 0]) {
Ellipsoid ellipsoid = srcDst ? srcEllipsoid : dstEllipsoid;
double a = ellipsoid.A;
double b = ellipsoid.B;
double e12 = ellipsoid.E12;
double e22 = ellipsoid.E22;
double e1 = (1 - sqrt(1 - e12)) / (1 + sqrt(1 - e12));
double radL0 = L0 / 180 * pi;
// 带内大地坐标
double Y = xyh.Y % 1000000;
double x = (xyh.X - offsetX) * b / (b + elevation);
double y = (Y - offsetY) * b / (b + elevation);
double u = x /
(a * (1 - e12 / 4 - 3 * e12 * e12 / 64 - 5 * e12 * e12 * e12 / 256));
double fai = u +
(3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * sin(2 * u) +
(21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * sin(4 * u) +
(151 * e1 * e1 * e1 / 96) * sin(6 * u) +
(1097 * e1 * e1 * e1 * e1 / 512) * sin(8 * u);
double C = e22 * cos(fai) * cos(fai);
double T = tan(fai) * tan(fai);
double N = a / sqrt(1 - e12 * sin(fai) * sin(fai));
double R = a *
(1 - e12) /
sqrt((1 - e12 * sin(fai) * sin(fai)) *
(1 - e12 * sin(fai) * sin(fai)) *
(1 - e12 * sin(fai) * sin(fai)));
double D = y / N;
double L = radL0 +
(D -
(1 + 2 * T + C) * D * D * D / 6 +
(5 - 2 * C + 28 * T - 3 * C * C + 8 * e22 + 24 * T * T) *
D *
D *
D *
D *
D /
120) /
cos(fai);
double B = fai -
(N * tan(fai) / R) *
(D * D / 2 -
(5 + 3 * T + 10 * C - 4 * C * C - 9 * e22) *
D *
D *
D *
D /
24 +
(61 + 90 * T + 298 * C + 45 * T * T - 256 * e22 - 3 * C * C) *
D *
D *
D *
D *
D *
D /
720);
B = B * 180 / pi;
L = L * 180 / pi;
return CoordBLH(B: B, L: L, H: xyh.H);
}
//两点之间的距离公式
dist(PointXY p1, PointXY p2) {
double d = sqrt(pow((p2.X - p1.X), 2) + pow((p2.Y - p1.Y), 2));
return d;
}
//求坐标方位角
double fwj(PointXY p1, PointXY p2) {
double dx = p2.X - p1.X;
double dy = p2.Y - p1.Y;
return pi - sign(dy) - atan(dx / dy);
}
// 计算三参数
TransOptions calcThree(CoordBLH p1, CoordXYH p2) {
CoordXYZ source = BLH2XYZ(p1);
CoordBLH bl = XY2BL(p2, true);
CoordXYZ dest = BLH2XYZ(bl, true);
double dX = dest.X - source.X;
double dY = dest.Y - source.Y;
double dZ = dest.Z - source.Z;
return TransOptions(dx: dX, dy: dY, dz: dZ);
}
// TransOptions calcFour(List p1, List p2, int PointCount) {
// double rota = 0;
// double scale = 0;
// double dx = 0;
// double dy = 0;
// if (PointCount == 2) {
// rota = fwj(p2[0] as PointXY, p2[1]) - fwj(p1[0], p1[1]);
// scale = dist(p2[0], p2[1]) / dist(p1[0], p1[1]);
// dx = p2[0].X - scale * cos(rota) * p1[0].X + scale * sin(rota) * p1[0].Y;
// dy = p2[0].Y - scale * sin(rota) * p1[0].X - scale * cos(rota) * p1[0].Y;
// } else if (PointCount > 2) {
// double u = 1.0, v = 0, Dx = 0.0, Dy = 0.0;
// int intCount = PointCount;
// Matrix4 dx1 = Matrix.zeros(4, 1);
// // Matrix4 B1 = Matrix.zeros(2 * intCount, 4);
// Matrix4 B1 = Matrix4.zero();
// for (int i = 0; i < 2 * intCount; i++) {
// for (int j = 0; j < 4; j++) {
// B1.setEntry(i, j, 0);
// }
// }
// Matrix4 W1 = Matrix.zeros(2 * intCount, 1);
// // Matrix4 BT = Matrix.zeros(4, 2 * intCount);
// Matrix4 BT = Matrix4.zero();
// for (int i = 0; i < 4; i++) {
// for (int j = 0; j < 2 * intCount; j++) {
// BT.setEntry(i, j, 0);
// }
// }
// Matrix4 N = Matrix4(
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// );
// Matrix4 InvN = Matrix4(
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// 0,
// );
// Matrix4 BTW = Matrix.zeros(4, 1);
// for (int i = 0; i < intCount; i++) {
// //计算误差方程系数
// B1.setEntry(2 * i, 0, 1);
// B1.setEntry(2 * i, 1, 0);
// B1.setEntry(2 * i, 2, p1[i].X);
// B1.setEntry(2 * i, 3, -p1[i].Y);
// B1.setEntry(2 * i + 1, 0, 0);
// B1.setEntry(2 * i + 1, 1, 1);
// B1.setEntry(2 * i + 1, 2, p1[i].Y);
// B1.setEntry(2 * i + 1, 3, p1[i].X);
// // B1[2 * i][0] = 1;
// // B1[2 * i][1] = 0;
// // B1[2 * i][2] = p1[i].X;
// // B1[2 * i][3] = -p1[i].Y;
// // B1[2 * i + 1][0] = 0;
// // B1[2 * i + 1][1] = 1;
// // B1[2 * i + 1][2] = p1[i].Y;
// // B1[2 * i + 1][3] = p1[i].X;
// }
// for (int i = 0; i < intCount; i++) {
// //计算误差方程系常数
// // W1[2 * i][0] = p2[i].X - u * p1[i].X + v * p1[i].Y - Dx;
// // W1[2 * i + 1][0] = p2[i].Y - u * p1[i].Y - v * p1[i].X - Dy;
// W1.setEntry(2 * i, 0, p2[i].X - u * p1[i].X + v * p1[i].Y - Dx);
// W1.setEntry(2 * i + 1, 0, p2[i].Y - u * p1[i].Y - v * p1[i].X - Dy);
// }
// // 最小二乘求解
// Matrix4 b1 = Matrix4.identity();
// // 矩阵 B1 的转置
// BT = Matrix4.copy(b1);
// BT.transpose();
// // 矩阵乘法
// N = BT * B1;
// // 逆矩阵
// InvN = N.clone();
// InvN.invert();
// BTW = BT * (W1);
// dx1 = InvN * (BTW);
// Dx = Dx + dx1[0][0];
// Dy = Dy + dx1[1][0];
// u = u + dx1[2][0];
// v = v + dx1[3][0];
// dx = Dx;
// dy = Dy;
// rota = atan(v / u);
// scale = u / cos(rota);
// }
// return TransOptions(rota: rota, k: scale, dx: dx, dy: dy);
// }
// // 计算七参数
// TransOptions calcBuras(List p1, List p2, pointCount) {
// let B1 = Matrix.zeros(pointCount * 3, 7);
// let dx1 = Matrix.zeros(7, 1);
// let L = Matrix.zeros(pointCount * 3, 1);
// let BT = Matrix.zeros(7, pointCount * 3);
// let N = Matrix.zeros(7, 7);
// let InvN = Matrix.zeros(7, 7);
// let BTL = Matrix.zeros(7, 1);
// for (int i = 0; i < pointCount * 3; i++) {
// if (i % 3 == 0) {
// L[i][0] = p2[(i / 3).floor()].X;
// } else if (i % 3 == 1) {
// L[i][0] = p2[(i / 3).floor()].Y;
// } else if (i % 3 == 2) {
// L[i][0] = p2[(i / 3).floor()].Z;
// }
// }
// for (int i = 0; i < pointCount * 3; i++) {
// if (i % 3 == 0) {
// int index = (i / 3) as int;
// B1[i][0] = 1;
// B1[i][1] = 0;
// B1[i][2] = 0;
// B1[i][3] = p1[index].X;
// B1[i][4] = 0;
// B1[i][5] = -p1[index].Z;
// B1[i][6] = p1[index].Y;
// } else if (i % 3 == 1) {
// int index = ((i - 1) / 3) as int;
// B1[i][0] = 0;
// B1[i][1] = 1;
// B1[i][2] = 0;
// B1[i][3] = p1[index].Y;
// B1[i][4] = p1[index].Z;
// B1[i][5] = 0;
// B1[i][6] = -p1[index].X;
// } else if (i % 3 == 2) {
// int index = ((i - 2) / 3) as int;
// B1[i][0] = 0;
// B1[i][1] = 0;
// B1[i][2] = 1;
// B1[i][3] = p1[index].Z;
// B1[i][4] = -p1[index].Y;
// B1[i][5] = p1[index].X;
// B1[i][6] = 0;
// }
// }
// Matrix4 b1 = Matrix4.identity();
// BT = B1.transpose();
// N = BT.mmul(B1);
// InvN = inverse(N);
// BTL = BT.mmul(L);
// dx1 = InvN.mmul(BTL);
// double dx = dx1[0][0];
// double dy = dx1[1][0];
// double dz = dx1[2][0];
// double scale = dx1[3][0];
// double rotax = dx1[4][0] / dx1[3][0];
// double rotay = dx1[5][0] / dx1[3][0];
// double rotaz = dx1[6][0] / dx1[3][0];
// return TransOptions(
// dx: dx, dy: dy, dz: dz, k: scale, rx: rotax, ry: rotay, rz: rotaz);
// }
double sign(num number) {
if (number < 0) {
return -1.0;
} else if (number > 0) {
return 1.0;
} else {
return 0.0;
}
}
}