From 85b8427fb612fedd98bf29970958f8129a124655 Mon Sep 17 00:00:00 2001 From: jiang Date: Sat, 11 Oct 2025 10:00:18 +0800 Subject: [PATCH] =?UTF-8?q?=E9=97=AE=E9=A2=98=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/controller/GNSSCoordinateTool.java | 225 ++++++++++++++++++ .../iot/controller/IotDataController.java | 44 ++-- 2 files changed, 254 insertions(+), 15 deletions(-) create mode 100644 bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/iot/controller/GNSSCoordinateTool.java diff --git a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/iot/controller/GNSSCoordinateTool.java b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/iot/controller/GNSSCoordinateTool.java new file mode 100644 index 0000000..d383773 --- /dev/null +++ b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/iot/controller/GNSSCoordinateTool.java @@ -0,0 +1,225 @@ +package com.bonus.material.iot.controller; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +public class GNSSCoordinateTool { + // 坐标系转换所需常量 + private static double x_pi = 3.14159265358979324 * 3000.0 / 180.0; + private static double pi = 3.1415926535897932384626; + private static double a = 6378245.0; + private static double ee = 0.00669342162296594323; + + /** + * 将GNSS输出的纬度格式(DDMM.MMMMMM)转换为十进制度(DD.DDDDDD) + * @param latStr GNSS纬度字符串,格式为DDMM.MMMMMM + * @return 十进制纬度值 + */ + public static double gnssLatToDecimal(String latStr) { + if (latStr == null || latStr.trim().isEmpty()) { + throw new IllegalArgumentException("纬度数据不能为空"); + } + + try { + // 提取度(前两位) + int degrees = Integer.parseInt(latStr.substring(0, 2)); + // 提取分及小数部分 + BigDecimal minutes = new BigDecimal(latStr.substring(2)); + // 计算十进制纬度 + return new BigDecimal(degrees) + .add(minutes.divide(new BigDecimal(60), 8, RoundingMode.HALF_UP)) + .doubleValue(); + } catch (Exception e) { + throw new IllegalArgumentException("无效的纬度格式: " + latStr + ",正确格式应为DDMM.MMMMMM", e); + } + } + + /** + * 将GNSS输出的经度格式(DDDMM.MMMMMM)转换为十进制度(DDD.DDDDDD) + * @param lonStr GNSS经度字符串,格式为DDDMM.MMMMMM + * @return 十进制经度值 + */ + public static double gnssLonToDecimal(String lonStr) { + if (lonStr == null || lonStr.trim().isEmpty()) { + throw new IllegalArgumentException("经度数据不能为空"); + } + try { + // 提取度(前三位) + int degrees = Integer.parseInt(lonStr.substring(0, 3)); + // 提取分及小数部分 + BigDecimal minutes = new BigDecimal(lonStr.substring(3)); + // 计算十进制经度 + return new BigDecimal(degrees) + .add(minutes.divide(new BigDecimal(60), 8, RoundingMode.HALF_UP)) + .doubleValue(); + } catch (Exception e) { + throw new IllegalArgumentException("无效的经度格式: " + lonStr + ",正确格式应为DDDMM.MMMMMM", e); + } + } + + /** + * 检查坐标是否在中国境外 + */ + private static boolean outOfChina(double lon, double lat) { + if (lon < 72.004 || lon > 137.8347) { + return true; + } else if (lat < 0.8293 || lat > 55.8271) { + return true; + } + return false; + } + + /** + * 纬度转换 + */ + private static double transformLat(double lon, double lat) { + double ret = -100.0 + 2.0 * lon + 3.0 * lat + 0.2 * lat * lat + 0.1 * lon * lat + + 0.2 * Math.sqrt(Math.abs(lon)); + ret += (20.0 * Math.sin(6.0 * lon * pi) + 20.0 * Math.sin(2.0 * lon * pi)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(lat * pi) + 40.0 * Math.sin(lat / 3.0 * pi)) * 2.0 / 3.0; + ret += (160.0 * Math.sin(lat / 12.0 * pi) + 320 * Math.sin(lat * pi / 30.0)) * 2.0 / 3.0; + return ret; + } + + /** + * 经度转换 + */ + private static double transformLng(double lon, double lat) { + double ret = 300.0 + lon + 2.0 * lat + 0.1 * lon * lon + 0.1 * lon * lat + + 0.1 * Math.sqrt(Math.abs(lon)); + ret += (20.0 * Math.sin(6.0 * lon * pi) + 20.0 * Math.sin(2.0 * lon * pi)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(lon * pi) + 40.0 * Math.sin(lon / 3.0 * pi)) * 2.0 / 3.0; + ret += (150.0 * Math.sin(lon / 12.0 * pi) + 300.0 * Math.sin(lon / 30.0 * pi)) * 2.0 / 3.0; + return ret; + } + + /** + * WGS84转GCJ02(火星坐标系) + */ + public static double[] wgs84ToGcj02(double wgsLon, double wgsLat) { + if (outOfChina(wgsLon, wgsLat)) { + return new double[] { wgsLon, wgsLat }; + } + + double dLat = transformLat(wgsLon - 105.0, wgsLat - 35.0); + double dLng = transformLng(wgsLon - 105.0, wgsLat - 35.0); + double radLat = wgsLat / 180.0 * pi; + double magic = Math.sin(radLat); + magic = 1 - ee * magic * magic; + double sqrtMagic = Math.sqrt(magic); + + dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi); + dLng = (dLng * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi); + + return new double[] { wgsLon + dLng, wgsLat + dLat }; + } + + /** + * GCJ02(火星坐标系)转WGS84 + */ + public static double[] gcj02ToWgs84(double gcjLon, double gcjLat) { + if (outOfChina(gcjLon, gcjLat)) { + return new double[] { gcjLon, gcjLat }; + } + + double dLat = transformLat(gcjLon - 105.0, gcjLat - 35.0); + double dLng = transformLng(gcjLon - 105.0, gcjLat - 35.0); + double radLat = gcjLat / 180.0 * pi; + double magic = Math.sin(radLat); + magic = 1 - ee * magic * magic; + double sqrtMagic = Math.sqrt(magic); + + dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi); + dLng = (dLng * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi); + + double mglat = gcjLat + dLat; + double mglng = gcjLon + dLng; + + return new double[] { gcjLon * 2 - mglng, gcjLat * 2 - mglat }; + } + + /** + * 火星坐标系(GCJ-02)转百度坐标系(BD-09) + */ + public static double[] gcj02ToBd09(double gcjLon, double gcjLat) { + double z = Math.sqrt(gcjLon * gcjLon + gcjLat * gcjLat) + 0.00002 * Math.sin(gcjLat * x_pi); + double theta = Math.atan2(gcjLat, gcjLon) + 0.000003 * Math.cos(gcjLon * x_pi); + + return new double[] { + z * Math.cos(theta) + 0.0065, + z * Math.sin(theta) + 0.006 + }; + } + + /** + * 百度坐标系(BD-09)转火星坐标系(GCJ-02) + */ + public static double[] bd09ToGcj02(double bdLon, double bdLat) { + double x = bdLon - 0.0065; + double y = bdLat - 0.006; + double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi); + double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi); + + return new double[] { z * Math.cos(theta), z * Math.sin(theta) }; + } + + /** + * WGS84转百度坐标系(BD-09) + */ + public static double[] wgs84ToBd09(double wgsLon, double wgsLat) { + double[] gcj = wgs84ToGcj02(wgsLon, wgsLat); + return gcj02ToBd09(gcj[0], gcj[1]); + } + + /** + * 百度坐标系(BD-09)转WGS84 + */ + public static double[] bd09ToWgs84(double bdLon, double bdLat) { + double[] gcj = bd09ToGcj02(bdLon, bdLat); + return gcj02ToWgs84(gcj[0], gcj[1]); + } + + /** + * 格式化坐标为指定小数位数的字符串 + */ + public static String formatCoordinate(double coordinate, int decimalPlaces) { + return new BigDecimal(coordinate) + .setScale(decimalPlaces, RoundingMode.HALF_UP) + .toString(); + } + + // 测试方法 + public static void main(String[] args) { + try { + // 示例:GNSS输出的坐标 + String gnssLat = "3149.332558"; // 纬度 DDMM.MMMMMM格式 + String gnssLon = "11706.912570"; // 经度 DDDMM.MMMMMM格式 + + // 转换为十进制WGS84坐标 + double wgsLat = gnssLatToDecimal(gnssLat); + double wgsLon = gnssLonToDecimal(gnssLon); + + System.out.println("GNSS原始坐标:"); + System.out.println("纬度: " + gnssLat + " (DDMM.MMMMMM)"); + System.out.println("经度: " + gnssLon + " (DDDMM.MMMMMM)"); + + System.out.println("\n转换为WGS84十进制坐标:"); + System.out.println("纬度: " + formatCoordinate(wgsLat, 6)); + System.out.println("经度: " + formatCoordinate(wgsLon, 6)); + + // 转换为其他地图坐标系 + double[] gcj02 = wgs84ToGcj02(wgsLon, wgsLat); + double[] bd09 = wgs84ToBd09(wgsLon, wgsLat); + + System.out.println("\n各地图坐标系坐标:"); + System.out.println("谷歌/高德地图 (GCJ-02): " + + formatCoordinate(gcj02[1], 6) + ", " + formatCoordinate(gcj02[0], 6)); + System.out.println("百度地图 (BD-09): " + + bd09[1] + ", " + formatCoordinate(bd09[0], 6)); + + } catch (Exception e) { + System.err.println("转换出错: " + e.getMessage()); + } + } +} + \ No newline at end of file diff --git a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/iot/controller/IotDataController.java b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/iot/controller/IotDataController.java index 69a176c..e0d6f73 100644 --- a/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/iot/controller/IotDataController.java +++ b/bonus-modules/bonus-material-mall/src/main/java/com/bonus/material/iot/controller/IotDataController.java @@ -1,7 +1,6 @@ package com.bonus.material.iot.controller; import com.alibaba.fastjson.JSON; -import com.bonus.common.core.web.domain.AjaxResult; import com.bonus.material.iot.dto.GpsData; import com.bonus.material.iot.dto.PhaseData; import com.fasterxml.jackson.databind.JsonNode; @@ -12,6 +11,9 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -213,17 +215,18 @@ public class IotDataController { String gnssStr = rootNode.get("position").get("gnss").asText(); parseGnssData(gnssStr, data); } - + if ((data.getLatitude() == null || data.getLatitude().isEmpty()) && rootNode.has("wifi")) { + String wifiStr = rootNode.get("wifi").asText(); + parseLbsWifiData(wifiStr, data, "wifi"); + } + // 如果没有有效的 GNSS 数据,尝试解析 LBS 或 WiFi if ((data.getLatitude() == null || data.getLatitude().isEmpty()) && rootNode.has("lbs")) { String lbsStr = rootNode.get("lbs").asText(); parseLbsWifiData(lbsStr, data, "lbs"); } - if ((data.getLatitude() == null || data.getLatitude().isEmpty()) && rootNode.has("wifi")) { - String wifiStr = rootNode.get("wifi").asText(); - parseLbsWifiData(wifiStr, data, "wifi"); - } + } /** @@ -268,9 +271,14 @@ public class IotDataController { // 安全设置字段 if (gnrmcParts.length > 0) data.setUtcTime(gnrmcParts[0]); if (gnrmcParts.length > 1) data.setStatus(gnrmcParts[1]); - if (gnrmcParts.length > 2) data.setLatitude(gnrmcParts[2]); + + double latitude = GNSSCoordinateTool.gnssLatToDecimal(gnrmcParts[2]); + double longitude = GNSSCoordinateTool.gnssLonToDecimal(gnrmcParts[4]); + double[] bd09 = GNSSCoordinateTool.wgs84ToBd09(longitude, latitude); + + if (gnrmcParts.length > 2) data.setLatitude(String.valueOf(bd09[0])); if (gnrmcParts.length > 3) data.setLatDirection(gnrmcParts[3]); - if (gnrmcParts.length > 4) data.setLongitude(gnrmcParts[4]); + if (gnrmcParts.length > 4) data.setLongitude(String.valueOf(bd09[1])); if (gnrmcParts.length > 5) data.setLonDirection(gnrmcParts[5]); if (gnrmcParts.length > 6) data.setSpeedOverGround(gnrmcParts[6]); if (gnrmcParts.length > 7) data.setCourseOverGround(gnrmcParts[7]); @@ -293,9 +301,14 @@ public class IotDataController { String[] gnggaParts = gngga.split(":", -1); if (gnggaParts.length > 0) data.setUtcTime(gnggaParts[0]); - if (gnggaParts.length > 1) data.setLatitude(gnggaParts[1]); + + double latitude = GNSSCoordinateTool.gnssLatToDecimal(gnggaParts[1]); + double longitude = GNSSCoordinateTool.gnssLonToDecimal(gnggaParts[3]); + + double[] bd09 = GNSSCoordinateTool.wgs84ToBd09(longitude, latitude); + if (gnggaParts.length > 1) data.setLatitude(String.valueOf(bd09[0])); if (gnggaParts.length > 2) data.setLatDirection(gnggaParts[2]); - if (gnggaParts.length > 3) data.setLongitude(gnggaParts[3]); + if (gnggaParts.length > 3) data.setLongitude(String.valueOf(bd09[1])); if (gnggaParts.length > 4) data.setLonDirection(gnggaParts[4]); if (gnggaParts.length > 5) data.setQuality(gnggaParts[5]); if (gnggaParts.length > 6) data.setNumSatellitesUsed(gnggaParts[6]); @@ -303,7 +316,6 @@ public class IotDataController { if (gnggaParts.length > 8) data.setAltitude(gnggaParts[8]); if (gnggaParts.length > 10) data.setGeoidSeparation(gnggaParts[10]); } - /** * 解析LBS或WiFi位置数据 */ @@ -311,13 +323,15 @@ public class IotDataController { try { String[] parts = dataStr.split(","); if (parts.length == 3) { - data.setLatitude(parts[0]); - data.setLongitude(parts[1]); + double[] bd09 = GNSSCoordinateTool.wgs84ToBd09(Double.parseDouble(parts[1]), Double.parseDouble(parts[0])); + data.setLatitude(String.valueOf(bd09[1])); + data.setLongitude(String.valueOf(bd09[0])); data.setAccuracy(parts[2]); data.setPositionSource(source); } else if (parts.length == 2) { - data.setLatitude(parts[0]); - data.setLongitude(parts[1]); + double[] bd09 = GNSSCoordinateTool.wgs84ToBd09(Double.parseDouble(parts[1]), Double.parseDouble(parts[0])); + data.setLatitude(String.valueOf(bd09[1])); + data.setLongitude(String.valueOf(bd09[0])); data.setPositionSource(source); } else { logger.warn("{}数据格式不正确,部分不足: {}", source, dataStr);