package publicsWay; // 包名:公共路径相关类
|
|
import dell_map.Dell_Map; // 导入地图数据类
|
import targets.Csxy; // 导入坐标系统类
|
import java.text.DecimalFormat; // 格式化数字工具
|
import java.util.Locale; // 本地化工具
|
import java.util.Map; // 映射接口
|
import java.util.concurrent.ConcurrentHashMap; // 线程安全哈希映射
|
|
public class CoordinateTranslator { // 坐标转换工具类
|
static final double a = 6378.137; // WGS84椭球体长半轴(公里)
|
static final double e = 0.0818192; // 第一偏心率
|
static final double k0 = 0.9996; // UTM比例因子
|
static final double e2 = e * e; // 偏心率的平方
|
static final double e4 = e2 * e2; // 偏心率的四次方
|
static final double e6 = e4 * e2; // 偏心率的六次方
|
static final double E0 = 500; // UTM东向偏移值(公里)
|
static final double N0 = 0; // UTM北向偏移值(公里)
|
static DecimalFormat df = new DecimalFormat("0.0000000"); // 7位小数格式化器
|
|
// UTM坐标缓存<经纬度组合键, [东坐标, 北坐标, 带号]>
|
private static final Map<Long, double[]> UTM_CACHE = new ConcurrentHashMap<>();
|
|
// 最近计算结果的缓存
|
private static double lastLat = Double.NaN; // 上次计算的纬度
|
private static double lastLon = Double.NaN; // 上次计算的经度
|
private static int[] lastResult = null; // 上次转换结果
|
|
// 计算UTM坐标(纬度,经度 -> [东坐标, 北坐标, 带号])
|
public static double[] utm(double lat, double lon) {
|
double[] dxy = new double[3]; // 返回数组[东坐标,北坐标,带号]
|
double zonenum1 = lon / 6; // 计算基础带号
|
zonenum1 = (zonenum1 > 0) ? Math.floor(zonenum1) : Math.ceil(zonenum1); // 取整处理
|
double Zonenum = zonenum1 + 31; // 实际带号(+31偏移)
|
double lambda0 = ((Zonenum - 1) * 6 - 180 + 3) * Math.PI / 180; // 中央经线(弧度)
|
double phi = lat * Math.PI / 180; // 纬度转弧度
|
double lambda = lon * Math.PI / 180; // 经度转弧度
|
double v = 1 / Math.sqrt((1 - e2 * Math.pow(Math.sin(phi), 2))); // 曲率半径
|
double A = (lambda - lambda0) * Math.cos(phi); // 经差投影
|
double A2 = A * A; // A的平方
|
double A3 = A2 * A; // A的立方
|
double A4 = A3 * A; // A的四次方
|
double A5 = A4 * A; // A的五次方
|
double A6 = A5 * A; // A的六次方
|
double T = Math.tan(phi) * Math.tan(phi); // tan²φ
|
double T2 = T * T; // tan⁴φ
|
double C = e2 * Math.cos(phi) * Math.cos(phi) / (1 - e2); // 辅助系数
|
double C2 = C * C; // C的平方
|
double s = (1 - e2/4 - 3*e4/64 - 5*e6/256) * phi - // 子午线弧长
|
(3*e2/8 + 3*e4/32 + 45*e6/1024) * Math.sin(2*phi) +
|
(15*e4/256 + 45*e6/1024) * Math.sin(4* phi) -
|
35*e6/3072 * Math.sin(6*phi);
|
dxy[0] = E0 + k0 * a * v * (A + (1 - T + C) * A3/6 + (5 - 18*T + T2) * A5/120); // 东坐标计算
|
dxy[1] = N0 + k0 * a * (s + v * Math.tan(phi) * (A2/2 + (5 - T + 9*C + 4*C2) * A4/24 + (61 - 58*T + T2) * A6/720)); // 北坐标计算
|
dxy[2] = Zonenum; // 带号
|
return dxy; // 返回UTM坐标数组
|
}
|
|
// 度分格式转十进制度 (ddmm.mmmm -> dd.dddddd)
|
public static double ublox_dm2d(double ddmm_dot_m) {
|
double d0 = Math.floor(ddmm_dot_m / 100); // 提取度数
|
return d0 + (ddmm_dot_m - d0 * 100) / 60; // 转换分值为度
|
}
|
|
// 度分格式转字符串 (ddmm.mmmm -> "dd.dddddd")
|
public static String ublox_dm2dstr(double ddmm_dot_m) {
|
double d0 = Math.floor(ddmm_dot_m / 100); // 提取度数
|
double d = d0 + (ddmm_dot_m - d0 * 100) / 60; // 转换分值为度
|
return df.format(d); // 格式化为字符串
|
}
|
|
// 初始化原点坐标系 [原点X, 原点Y, cosθ, sinθ]
|
public static double[] initiize_ublox_zeropoint(double lat, double lon, double th) {
|
double[] xycs = new double[4]; // 返回数组[原点X,原点Y,cosθ,sinθ]
|
double[] utm = ubloxraw2xy(lat, lon); // 获取UTM坐标
|
xycs[0] = utm[0]; // 原点X(米)
|
xycs[1] = utm[1]; // 原点Y(米)
|
double th1 = th * Math.PI / 180; // 角度转弧度
|
xycs[2] = Math.cos(th1); // 旋转矩阵cos分量
|
xycs[3] = Math.sin(th1); // 旋转矩阵sin分量
|
return xycs; // 返回坐标系参数
|
}
|
|
// 通过两点初始化坐标系(简易版)[原点X,原点Y,cosθ,sinθ,角度,带号]
|
public static double[] initiize_ublox_AB(
|
double lat0, // 原点纬度
|
double lon0, // 原点经度
|
double latB, // B点纬度
|
double lonB, // B点经度
|
double xB, // B点局部坐标X
|
double yB) { // B点局部坐标Y
|
double[] xycstf = new double[6]; // 返回数组[原点X,原点Y,cosθ,sinθ,角度,带号]
|
double[] a = ubloxraw2xy(lat0, lon0); // 原点UTM坐标
|
double[] b = ubloxraw2xy(latB, lonB); // B点UTM坐标
|
double[] ab = {b[0] - a[0], b[1] - a[1]}; // UTM坐标差向量
|
double the = Math.atan2(ab[0], ab[1]); // UTM向量角度
|
double thp = Math.atan2(yB, xB); // 局部坐标向量角度
|
double th = the - thp; // 旋转角差
|
xycstf[0] = a[0]; // 原点X(米)
|
xycstf[1] = a[1]; // 原点Y(米)
|
xycstf[2] = Math.cos(th); // 旋转矩阵cos分量
|
xycstf[3] = Math.sin(th); // 旋转矩阵sin分量
|
xycstf[4] = th * 180 / Math.PI; // 旋转角度(度)
|
xycstf[5] = a[2]; // UTM带号
|
return xycstf; // 返回坐标系参数
|
}
|
|
// 通过两点初始化坐标系(完整版)[原点X,原点Y,cosθ,sinθ,角度,带号]
|
/**
|
* 通过两个已知点计算坐标转换参数
|
* [原点X(m), 原点Y(m), cosθ, sinθ, 旋转角度(°), UTM带号]
|
*
|
* @param latA 点A纬度 (度分格式)
|
* @param lonA 点A经度 (度分格式)
|
* @param latB 点B纬度 (度分格式)
|
* @param lonB 点B经度 (度分格式)
|
* @param localXA 点A局部X坐标 (cm)
|
* @param localYA 点A局部Y坐标 (cm)
|
* @param localXB 点B局部X坐标 (cm)
|
* @param localYB 点B局部Y坐标 (cm)
|
* @return 转换参数数组 [x0, y0, cosθ, sinθ, θ_deg, zone]
|
*/
|
public static double[] computeTransformParamsWithTwoPoints(
|
double latA, double lonA,
|
double latB, double lonB,
|
double localXA, double localYA,
|
double localXB, double localYB) {
|
|
// 1. 单位转换 (cm → m)
|
double xA = localXA / 100;
|
double yA = localYA / 100;
|
double xB = localXB / 100;
|
double yB = localYB / 100;
|
|
// 2. 获取UTM坐标
|
double[] utmA = ubloxraw2xy(latA, lonA);
|
double[] utmB = ubloxraw2xy(latB, lonB);
|
|
// 3. 计算UTM向量差
|
double dxUTM = utmB[0] - utmA[0];
|
double dyUTM = utmB[1] - utmA[1];
|
|
// 4. 计算局部坐标向量差
|
double dxLocal = xB - xA;
|
double dyLocal = yB - yA;
|
|
// 5. 计算旋转角度差
|
double thetaUTM = Math.atan2(dxUTM, dyUTM); // UTM向量角度
|
double thetaLocal = Math.atan2(dyLocal, dxLocal); // 局部向量角度
|
double theta = thetaUTM - thetaLocal; // 旋转角差
|
|
// 6. 计算旋转矩阵分量
|
double cosTheta = Math.cos(theta);
|
double sinTheta = Math.sin(theta);
|
|
// 7. 计算实际原点坐标
|
double rotatedXA = cosTheta * xA - sinTheta * yA;
|
double rotatedYA = sinTheta * xA + cosTheta * yA;
|
double originX = utmA[0] - rotatedXA;
|
double originY = utmA[1] - rotatedYA;
|
|
return new double[] {
|
originX, // 实际原点X (m)
|
originY, // 实际原点Y (m)
|
cosTheta, // 旋转矩阵cos分量
|
sinTheta, // 旋转矩阵sin分量
|
theta * 180 / Math.PI, // 旋转角度(°)
|
utmA[2] // UTM带号
|
};
|
}
|
|
// 原始格式转UTM坐标(带缓存)[东坐标(米), 北坐标(米), 带号]
|
public static double[] ubloxraw2xy(double lat, double lon) {
|
// 生成缓存键(1e7精度约1.1cm)
|
long key = ((long)(lat * 1e7) << 32) | ((long)(lon * 1e7)) & 0xFFFFFFFFL;
|
|
return UTM_CACHE.computeIfAbsent(key, k -> { // 缓存查询/计算
|
double lond = ublox_dm2d(lon); // 度分转度
|
double latd = ublox_dm2d(lat); // 度分转度
|
double[] dxy = utm(latd, lond); // 计算UTM坐标
|
return new double[] { // 返回结果(米)
|
dxy[0] * 1000, // 公里转米
|
dxy[1] * 1000, // 公里转米
|
dxy[2] // 带号不变
|
};
|
});
|
}
|
|
// GPS转局部坐标 [局部Y(cm), 局部X(cm), 高度, 楼层]
|
public static int[] run_gps2xy(double lat, double lon) {
|
// 检查缓存是否可用
|
if (Double.compare(lat, lastLat) == 0 &&
|
Double.compare(lon, lastLon) == 0 &&
|
lastResult != null) {
|
return lastResult; // 直接返回缓存结果
|
}
|
|
// 1. 计算当前点UTM坐标
|
double[] currentUtm = ubloxraw2xy(lat, lon);
|
double currentX = currentUtm[0]; // 当前点东坐标(米)
|
double currentY = currentUtm[1]; // 当前点北坐标(米)
|
|
// 2. 查找最近基站
|
double minDistanceSq = Double.MAX_VALUE; // 最小距离平方
|
Csxy nearestCsxy = null; // 最近基站对象
|
|
for (Csxy cs : Dell_Map.csxyvc) { // 遍历基站集合
|
double dx = currentX - cs.getOriginUtmX(); // X方向差
|
double dy = currentY - cs.getOriginUtmY(); // Y方向差
|
double distanceSq = dx * dx + dy * dy; // 距离平方
|
|
if (distanceSq < minDistanceSq) { // 找到更近基站
|
minDistanceSq = distanceSq;
|
nearestCsxy = cs; // 更新最近基站
|
}
|
}
|
|
if (nearestCsxy == null) return null; // 无基站时返回空
|
|
// 3. 坐标转换
|
double[] xycs = nearestCsxy.getXycs(); // 获取基站坐标系参数
|
double x0 = xycs[0]; // 基站原点X
|
double y0 = xycs[1]; // 基站原点Y
|
double c = xycs[2]; // 旋转cos
|
double s = xycs[3]; // 旋转sin
|
|
double x = currentX - x0; // 平移X
|
double y = currentY - y0; // 平移Y
|
|
// 4. 旋转变换
|
double rotatedX = c * x - s * y; // 新坐标系X
|
double rotatedY = s * x + c * y; // 新坐标系Y
|
|
// 5. 更新缓存
|
lastLat = lat; // 缓存纬度
|
lastLon = lon; // 缓存经度
|
lastResult = new int[] { // 转换结果(cm)
|
(int)(rotatedY * 100), // Y坐标转厘米
|
(int)(rotatedX * 100), // X坐标转厘米
|
150, // 固定高度值
|
nearestCsxy.getFloorInt() // 楼层信息
|
};
|
|
return lastResult; // 返回转换结果
|
}
|
|
// 字符串转双精度(保留1位小数)
|
public static double stringToDouble(String a) {
|
double b = Double.valueOf(a); // 字符串转double
|
DecimalFormat df = new DecimalFormat("#.0"); // 1位小数格式化
|
return Double.valueOf(df.format(b)); // 格式化后返回
|
}
|
|
// GPS转局部坐标(双点标定版)[局部Y(cm), 局部X(cm)]
|
public static int[] gps_xy(
|
String[] A, // A点参数[经,纬,X,Y]
|
String[] B, // B点参数[经,纬,X,Y]
|
String jd1, // 当前经度
|
String wd1) { // 当前纬度
|
double lon_a1 = Double.parseDouble(A[0]); // A点经度
|
double lat_a1 = Double.parseDouble(A[1]); // A点纬度
|
double xa1 = 0; // A点局部X默认值
|
double ya1 = 0; // A点局部Y默认值
|
int size2 = A.length; // A点参数数量
|
if (size2 == 4) { // 如果有局部坐标
|
xa1 = Double.parseDouble(A[2]); // A点局部X(cm)
|
ya1 = Double.parseDouble(A[3]); // A点局部Y(cm)
|
}
|
double lon_b1 = Double.parseDouble(B[0]); // B点经度
|
double lat_b1 = Double.parseDouble(B[1]); // B点纬度
|
double xb1 = Double.parseDouble(B[2]); // B点局部X(cm)
|
double yb1 = Double.parseDouble(B[3]); // B点局部Y(cm)
|
double lat = Double.parseDouble(wd1); // 当前纬度
|
double lon = Double.parseDouble(jd1); // 当前经度
|
int[] realxy = new int[2]; // 返回结果[Y,X](cm)
|
double[] xycs; // 坐标系参数
|
if (size2 == 4) { // 完整标定模式
|
xycs = computeTransformParamsWithTwoPoints(lat_a1, lon_a1, lat_b1, lon_b1, xa1, ya1, xb1, yb1);
|
} else { // 简易标定模式
|
xycs = initiize_ublox_AB(lat_a1, lon_a1, lat_b1, lon_b1, xb1, yb1);
|
}
|
double x0 = xycs[0]; // 原点X(米)
|
double y0 = xycs[1]; // 原点Y(米)
|
double c = xycs[2]; // 旋转cos
|
double s = xycs[3]; // 旋转sin
|
double x = ubloxraw2xy(lat, lon)[0] - x0; // 平移X(米)
|
double y = ubloxraw2xy(lat, lon)[1] - y0; // 平移Y(米)
|
double rotatedX = c * x - s * y; // 旋转后X(米)
|
double rotatedY = s * x + c * y; // 旋转后Y(米)
|
realxy[0] = (int)(rotatedX * 100); //X转厘米
|
realxy[1] = (int)(rotatedY * 100); //Y转厘米
|
return realxy; // 返回结果
|
}
|
|
// 计算两点距离(cm)(字符串参数)
|
public static int distance(String jd0, String wd0, String jd1, String wd1) {
|
double lat_x0 = Double.parseDouble(wd0); // 点1纬度
|
double lon_y0 = Double.parseDouble(jd0); // 点1经度
|
double lat = Double.parseDouble(wd1); // 点2纬度
|
double lon = Double.parseDouble(jd1); // 点2经度
|
double[] xycs = initiize_ublox_zeropoint(lat_x0, lon_y0, 0); // 初始化原点坐标系
|
double x0 = xycs[0]; // 原点X(米)
|
double y0 = xycs[1]; // 原点Y(米)
|
double c = xycs[2]; // 旋转cos
|
double s = xycs[3]; // 旋转sin
|
double x = ubloxraw2xy(lat, lon)[0] - x0; // 平移X(米)
|
double y = ubloxraw2xy(lat, lon)[1] - y0; // 平移Y(米)
|
double rotatedX = c * x - s * y; // 旋转后X(米)
|
double rotatedY = s * x + c * y; // 旋转后Y(米)
|
double disd = Math.sqrt(rotatedX * rotatedX + rotatedY * rotatedY) * 100; // 计算距离(cm)
|
return (int)disd; // 返回整型距离
|
}
|
|
// 计算两点距离(cm)(双精度参数)
|
public static int distance2(double lon_y0, double lat_x0, double lon, double lat) {
|
double[] xycs = initiize_ublox_zeropoint(lat_x0, lon_y0, 0); // 初始化原点坐标系
|
double x0 = xycs[0]; // 原点X(米)
|
double y0 = xycs[1]; // 原点Y(米)
|
double c = xycs[2]; // 旋转cos
|
double s = xycs[3]; // 旋转sin
|
double x = ubloxraw2xy(lat, lon)[0] - x0; // 平移X(米)
|
double y = ubloxraw2xy(lat, lon)[1] - y0; // 平移Y(米)
|
double rotatedX = c * x - s * y; // 旋转后X(米)
|
double rotatedY = s * x + c * y; // 旋转后Y(米)
|
double disd = Math.sqrt(rotatedX * rotatedX + rotatedY * rotatedY) * 100; // 计算距离(cm)
|
return (int)disd; // 返回整型距离
|
}
|
|
// GPS转局部坐标(原点标定版)[局部Y(cm), 局部X(cm)]
|
public static int[] run_gps2xyurt(double lat0, double lon0, double lat, double lon) {
|
double[] xycs = initiize_ublox_zeropoint(lat0, lon0, 0); // 初始化原点坐标系
|
double x0 = xycs[0]; // 原点X(米)
|
double y0 = xycs[1]; // 原点Y(米)
|
double c = xycs[2]; // 旋转cos
|
double s = xycs[3]; // 旋转sin
|
double x = ubloxraw2xy(lat, lon)[0] - x0; // 平移X(米)
|
double y = ubloxraw2xy(lat, lon)[1] - y0; // 平移Y(米)
|
double rotatedX = c * x - s * y; // 旋转后X(米)
|
double rotatedY = s * x + c * y; // 旋转后Y(米)
|
return new int[] { // 返回结果(cm)
|
(int)(rotatedY * 100), // Y转厘米
|
(int)(rotatedX * 100) // X转厘米
|
};
|
}
|
|
// 计算字符串格式坐标的距离
|
public static double disab(String jwd) {
|
String[] bb = jwd.split(";"); // 分割字符串
|
double jd0 = Double.parseDouble(bb[1]); // 点1经度
|
double wd0 = Double.parseDouble(bb[0]); // 点1纬度
|
double jd1 = Double.parseDouble(bb[3]); // 点2经度
|
double wd1 = Double.parseDouble(bb[2]); // 点2纬度
|
int[] a = run_gps2xyurt(wd0, jd0, wd1, jd1); // 坐标转换
|
double disa = a[0] * a[0] + a[1] * a[1]; // 距离平方
|
return Math.sqrt(disa); // 返回实际距离(cm)
|
}
|
|
// 度分秒转十进制度
|
public static String todufen(String Du, String Fen, String Miao) {
|
Float strDu = Float.valueOf(Du); // 度
|
Float strFen = Float.valueOf(Fen) / 60; // 分转度
|
Float strMiao = Float.valueOf(Miao) / 3600; // 秒转度
|
Float dufenmiao = strDu + strFen + strMiao; // 总和
|
return String.format(Locale.US, "%.6f", dufenmiao); // 格式化为6位小数
|
}
|
}
|