From 99adfdca8002c631a4f53c55ba01242cc158c9d7 Mon Sep 17 00:00:00 2001 From: yuejiajun <1530620364@qq.com> Date: Mon, 29 Sep 2025 15:17:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=96=B9=E6=B3=95=E4=B8=8E?= =?UTF-8?q?=E5=A4=A7=E5=9C=B0=E5=9D=90=E6=A0=87=E7=B3=BB=E8=BD=AC=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/utils/CoordinateConverterUtil.java | 426 ++++++++++++++++++ .../util/CoordinateConverterUtilTest.java | 84 ++++ .../core/draft/ApplicationTest__01.java | 32 +- .../core/draft/ApplicationTest__02.java | 32 +- .../core/draft/ApplicationTest__03.java | 32 +- .../core/draft/ApplicationTest__04.java | 32 +- .../process/domain/TestLoopCountFix.java | 11 +- .../domain/draft/ConfigDataCreator__02.java | 30 +- .../domain/draft/ConfigDataCreator__03.java | 30 +- .../domain/draft/ConfigDataCreator__04.java | 30 +- .../parser/process/util/FormatUtilTest.java | 5 +- 11 files changed, 733 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/example/demo/common/utils/CoordinateConverterUtil.java create mode 100644 src/test/java/com/example/demo/common/util/CoordinateConverterUtilTest.java diff --git a/src/main/java/com/example/demo/common/utils/CoordinateConverterUtil.java b/src/main/java/com/example/demo/common/utils/CoordinateConverterUtil.java new file mode 100644 index 0000000..96db52e --- /dev/null +++ b/src/main/java/com/example/demo/common/utils/CoordinateConverterUtil.java @@ -0,0 +1,426 @@ +package com.example.demo.common.utils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * 坐标转换工具类,支持大地坐标与空间直角坐标的正算和反算 + * 支持多种椭球参数(WGS84、CGCS2000等) + * + *
大地坐标(Geodetic Coordinate):由纬度(B)、经度(L)和大地高(H)组成 + *
空间直角坐标(Cartesian Coordinate):由X、Y、Z三个分量组成 + * + * @author 岳佳君 (2025年9月29日 15:02:13) + * @version 1.0.0 + */ +public class CoordinateConverterUtil { + + /** + * 椭球参数接口,定义了椭球的基本参数 + */ + public interface Ellipsoid { + /** + * 获取椭球长半轴(赤道半径),单位:米 + * @return 长半轴值 + */ + double getSemiMajorAxis(); + + /** + * 获取椭球短半轴(极半径),单位:米 + * @return 短半轴值 + */ + double getSemiMinorAxis(); + + /** + * 获取第一偏心率平方 + * @return 第一偏心率平方值 + */ + double getFirstEccentricitySquared(); + + /** + * 获取第二偏心率平方 + * @return 第二偏心率平方值 + */ + double getSecondEccentricitySquared(); + } + + /** + * WGS84椭球参数实现 + *
WGS84是GPS系统使用的椭球参数 + */ + public static class WGS84Ellipsoid implements Ellipsoid { + // 长半轴,单位:米 + private static final double SEMI_MAJOR_AXIS = 6378137.0; + // 短半轴,单位:米 + private static final double SEMI_MINOR_AXIS = 6356752.314245179; + // 第一偏心率平方 + private static final double FIRST_ECCENTRICITY_SQUARED = 0.0066943799901413165; + // 第二偏心率平方 + private static final double SECOND_ECCENTRICITY_SQUARED = 0.006739496742276435; + + @Override + public double getSemiMajorAxis() { + return SEMI_MAJOR_AXIS; + } + + @Override + public double getSemiMinorAxis() { + return SEMI_MINOR_AXIS; + } + + @Override + public double getFirstEccentricitySquared() { + return FIRST_ECCENTRICITY_SQUARED; + } + + @Override + public double getSecondEccentricitySquared() { + return SECOND_ECCENTRICITY_SQUARED; + } + } + + /** + * CGCS2000椭球参数实现 + *
CGCS2000是中国国家大地坐标系使用的椭球参数
+ */
+ public static class CGCS2000Ellipsoid implements Ellipsoid {
+ // 长半轴,单位:米
+ private static final double SEMI_MAJOR_AXIS = 6378137.0;
+ // 短半轴,单位:米
+ private static final double SEMI_MINOR_AXIS = 6356752.314140356;
+ // 第一偏心率平方
+ private static final double FIRST_ECCENTRICITY_SQUARED = 0.006694380022903416;
+ // 第二偏心率平方
+ private static final double SECOND_ECCENTRICITY_SQUARED = 0.006739496775481663;
+
+ @Override
+ public double getSemiMajorAxis() {
+ return SEMI_MAJOR_AXIS;
+ }
+
+ @Override
+ public double getSemiMinorAxis() {
+ return SEMI_MINOR_AXIS;
+ }
+
+ @Override
+ public double getFirstEccentricitySquared() {
+ return FIRST_ECCENTRICITY_SQUARED;
+ }
+
+ @Override
+ public double getSecondEccentricitySquared() {
+ return SECOND_ECCENTRICITY_SQUARED;
+ }
+ }
+
+ /**
+ * 大地坐标模型类,包含纬度、经度和大地高
+ */
+ public static class GeodeticCoordinate {
+ // 纬度,单位:弧度(北半球为正,南半球为负)
+ private final double latitude;
+ // 经度,单位:弧度(东经为正,西经为负)
+ private final double longitude;
+ // 大地高,单位:米
+ private final double height;
+
+ /**
+ * 构造函数
+ * @param latitude 纬度(弧度)
+ * @param longitude 经度(弧度)
+ * @param height 大地高(米)
+ */
+ public GeodeticCoordinate(double latitude, double longitude, double height) {
+ this.latitude = latitude;
+ this.longitude = longitude;
+ this.height = height;
+ }
+
+ /**
+ * 获取纬度(弧度)
+ * @return 纬度值
+ */
+ public double getLatitude() {
+ return latitude;
+ }
+
+ /**
+ * 获取经度(弧度)
+ * @return 经度值
+ */
+ public double getLongitude() {
+ return longitude;
+ }
+
+ /**
+ * 获取大地高(米)
+ * @return 大地高值
+ */
+ public double getHeight() {
+ return height;
+ }
+
+ @Override
+ public String toString() {
+ // 转换为度格式显示
+ return String.format("GeodeticCoordinate [纬度=%.6f°, 经度=%.6f°, 大地高=%.2fm]",
+ Math.toDegrees(latitude), Math.toDegrees(longitude), height);
+ }
+ }
+
+ /**
+ * 空间直角坐标模型类,包含X、Y、Z三个分量
+ */
+ public static class CartesianCoordinate {
+ // X坐标,单位:米
+ private final double x;
+ // Y坐标,单位:米
+ private final double y;
+ // Z坐标,单位:米
+ private final double z;
+
+ /**
+ * 构造函数
+ * @param x X坐标(米)
+ * @param y Y坐标(米)
+ * @param z Z坐标(米)
+ */
+ public CartesianCoordinate(double x, double y, double z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ /**
+ * 获取X坐标
+ * @return X坐标值
+ */
+ public double getX() {
+ return x;
+ }
+
+ /**
+ * 获取Y坐标
+ * @return Y坐标值
+ */
+ public double getY() {
+ return y;
+ }
+
+ /**
+ * 获取Z坐标
+ * @return Z坐标值
+ */
+ public double getZ() {
+ return z;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("CartesianCoordinate [X=%.2fm, Y=%.2fm, Z=%.2fm]", x, y, z);
+ }
+ }
+
+ // 默认椭球参数为WGS84
+ private static final Ellipsoid DEFAULT_ELLIPSOID = new WGS84Ellipsoid();
+ // 迭代计算精度,单位:弧度,约等于0.00000057度
+ private static final double ITERATION_PRECISION = 1e-10;
+ // 最大迭代次数
+ private static final int MAX_ITERATIONS = 10;
+
+ /**
+ * 大地坐标正算:将大地坐标转换为空间直角坐标
+ * 使用默认椭球参数(WGS84)
+ *
+ * @param geodetic 大地坐标(纬度、经度单位为弧度)
+ * @return 空间直角坐标
+ */
+ public static CartesianCoordinate geodeticToCartesian(GeodeticCoordinate geodetic) {
+ return geodeticToCartesian(geodetic, DEFAULT_ELLIPSOID);
+ }
+
+ /**
+ * 大地坐标正算:将大地坐标转换为空间直角坐标
+ *
+ * @param geodetic 大地坐标(纬度、经度单位为弧度)
+ * @param ellipsoid 椭球参数
+ * @return 空间直角坐标
+ */
+ public static CartesianCoordinate geodeticToCartesian(GeodeticCoordinate geodetic, Ellipsoid ellipsoid) {
+ // 验证输入参数
+ if (geodetic == null) {
+ throw new IllegalArgumentException("大地坐标不能为空");
+ }
+ if (ellipsoid == null) {
+ throw new IllegalArgumentException("椭球参数不能为空");
+ }
+
+ double B = geodetic.getLatitude(); // 纬度(弧度)
+ double L = geodetic.getLongitude(); // 经度(弧度)
+ double H = geodetic.getHeight(); // 大地高(米)
+
+ double a = ellipsoid.getSemiMajorAxis();
+ double e2 = ellipsoid.getFirstEccentricitySquared();
+
+ // 计算卯酉圈曲率半径N
+ double sinB = Math.sin(B);
+ double cosB = Math.cos(B);
+ double cosL = Math.cos(L);
+ double sinL = Math.sin(L);
+
+ double N = a / Math.sqrt(1 - e2 * sinB * sinB);
+
+ // 计算空间直角坐标
+ double X = (N + H) * cosB * cosL;
+ double Y = (N + H) * cosB * sinL;
+ double Z = (N * (1 - e2) + H) * sinB;
+
+ // 保留6位小数精度
+ X = round(X, 6);
+ Y = round(Y, 6);
+ Z = round(Z, 6);
+
+ return new CartesianCoordinate(X, Y, Z);
+ }
+
+ /**
+ * 大地坐标反算:将空间直角坐标转换为大地坐标
+ * 使用默认椭球参数(WGS84)
+ *
+ * @param cartesian 空间直角坐标
+ * @return 大地坐标(纬度、经度单位为弧度)
+ */
+ public static GeodeticCoordinate cartesianToGeodetic(CartesianCoordinate cartesian) {
+ return cartesianToGeodetic(cartesian, DEFAULT_ELLIPSOID);
+ }
+
+ /**
+ * 大地坐标反算:将空间直角坐标转换为大地坐标
+ *
+ * @param cartesian 空间直角坐标
+ * @param ellipsoid 椭球参数
+ * @return 大地坐标(纬度、经度单位为弧度)
+ */
+ public static GeodeticCoordinate cartesianToGeodetic(CartesianCoordinate cartesian, Ellipsoid ellipsoid) {
+ // 验证输入参数
+ if (cartesian == null) {
+ throw new IllegalArgumentException("空间直角坐标不能为空");
+ }
+ if (ellipsoid == null) {
+ throw new IllegalArgumentException("椭球参数不能为空");
+ }
+
+ double X = cartesian.getX();
+ double Y = cartesian.getY();
+ double Z = cartesian.getZ();
+
+ double a = ellipsoid.getSemiMajorAxis();
+ double b = ellipsoid.getSemiMinorAxis();
+ double e2 = ellipsoid.getFirstEccentricitySquared();
+ double ePrime2 = ellipsoid.getSecondEccentricitySquared();
+
+ // 计算经度L
+ double L;
+ if (X == 0 && Y == 0) {
+ L = 0; // 位于极点,经度无意义,设为0
+ } else {
+ L = Math.atan2(Y, X); // 计算经度(弧度)
+ }
+
+ // 计算点到地轴的距离r
+ double r = Math.sqrt(X * X + Y * Y);
+
+ // 特殊情况处理:如果r为0,说明在极点
+ if (r == 0) {
+ double B = (Z > 0) ? Math.PI / 2 : -Math.PI / 2; // 北极或南极
+ double H = Math.abs(Z) - b; // 大地高
+ return new GeodeticCoordinate(B, L, H);
+ }
+
+ // 迭代计算纬度B
+ double B = Math.atan2(Z, r * (1 - e2)); // 初始值
+ double previousB;
+ int iterations = 0;
+
+ do {
+ previousB = B;
+ double sinB = Math.sin(B);
+ // 计算卯酉圈曲率半径N
+ double N = a / Math.sqrt(1 - e2 * sinB * sinB);
+ // 迭代计算新的纬度
+ B = Math.atan2(Z + N * e2 * sinB, r);
+ iterations++;
+ } while (Math.abs(B - previousB) > ITERATION_PRECISION && iterations < MAX_ITERATIONS);
+
+ // 计算大地高H
+ double sinB = Math.sin(B);
+ double cosB = Math.cos(B);
+ double N = a / Math.sqrt(1 - e2 * sinB * sinB);
+ double H = (r / cosB) - N;
+
+ // 保留8位小数精度
+ B = round(B, 8);
+ L = round(L, 8);
+ H = round(H, 6);
+
+ return new GeodeticCoordinate(B, L, H);
+ }
+
+ /**
+ * 角度转换:度分秒转弧度
+ *
+ * @param degrees 度
+ * @param minutes 分
+ * @param seconds 秒
+ * @return 转换后的弧度值
+ */
+ public static double dmsToRadians(int degrees, int minutes, double seconds) {
+ if (degrees < 0 || minutes < 0 || minutes >= 60 || seconds < 0 || seconds >= 60) {
+ throw new IllegalArgumentException("无效的度分秒值");
+ }
+
+ double decimalDegrees = degrees + minutes / 60.0 + seconds / 3600.0;
+ return Math.toRadians(decimalDegrees);
+ }
+
+ /**
+ * 角度转换:弧度转度分秒表示的字符串
+ *
+ * @param radians 弧度值
+ * @return 度分秒格式的字符串(例如:30°15'22.5")
+ */
+ public static String radiansToDmsString(double radians) {
+ double degrees = Math.toDegrees(radians);
+ int deg = (int) Math.floor(Math.abs(degrees));
+ double minutes = (Math.abs(degrees) - deg) * 60;
+ int min = (int) Math.floor(minutes);
+ double sec = (minutes - min) * 60;
+
+ // 保留两位小数
+ sec = round(sec, 2);
+
+ // 处理符号
+ String sign = degrees >= 0 ? "" : "-";
+
+ return String.format("%s%d°%d'%f\"", sign, deg, min, sec);
+ }
+
+ /**
+ * 四舍五入保留指定小数位数
+ *
+ * @param value 要处理的数值
+ * @param decimalPlaces 保留的小数位数
+ * @return 处理后的数值
+ */
+ private static double round(double value, int decimalPlaces) {
+ if (decimalPlaces < 0) {
+ throw new IllegalArgumentException("小数位数不能为负数");
+ }
+
+ BigDecimal bd = new BigDecimal(Double.toString(value));
+ bd = bd.setScale(decimalPlaces, RoundingMode.HALF_UP);
+ return bd.doubleValue();
+ }
+
+}
diff --git a/src/test/java/com/example/demo/common/util/CoordinateConverterUtilTest.java b/src/test/java/com/example/demo/common/util/CoordinateConverterUtilTest.java
new file mode 100644
index 0000000..0c62fc8
--- /dev/null
+++ b/src/test/java/com/example/demo/common/util/CoordinateConverterUtilTest.java
@@ -0,0 +1,84 @@
+package com.example.demo.common.util;
+
+import com.example.demo.common.utils.CoordinateConverterUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.example.demo.common.utils.CoordinateConverterUtil.*;
+
+@SuppressWarnings("all")
+@Slf4j
+@DisplayName("CoordinateConverterUtil工具类测试")
+public class CoordinateConverterUtilTest {
+
+ private Map