package bianjie; import java.util.ArrayList; import java.util.List; public class BoundaryAlgorithm { public static class Coordinate { public double x; // 东方向坐标 (米) public double y; // 北方向坐标 (米) public double lat; // 纬度 public double lon; // 经度 public Coordinate(double x, double y, double lat, double lon) { this.x = x; this.y = y; this.lat = lat; this.lon = lon; } @Override public String toString() { return String.format("(%.2f, %.2f)", x, y); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Coordinate that = (Coordinate) obj; return Math.abs(this.x - that.x) < 0.001 && Math.abs(this.y - that.y) < 0.001; } public Coordinate copy() { return new Coordinate(x, y, lat, lon); } } // 边界质量评估类 public static class BoundaryQuality { public boolean isClosed; public double closureError; public int selfIntersectionCount; public double smoothness; public double area; public int pointCount; @Override public String toString() { return String.format( "边界质量评估:\n" + "是否闭合: %s\n" + "闭合误差: %.3f米\n" + "自相交点数: %d\n" + "平均角度变化: %.2f度\n" + "面积: %.2f平方米\n" + "边界点数: %d", isClosed ? "是" : "否", closureError, selfIntersectionCount, smoothness, area, pointCount ); } } // 边界参数类 public static class BoundaryParameters { public final double interval; // 米 public final double angleThreshold; // 度 public final double simplificationTolerance; // 米 public BoundaryParameters(double interval, double angleThreshold, double simplificationTolerance) { this.interval = interval; this.angleThreshold = angleThreshold; this.simplificationTolerance = simplificationTolerance; } // 预设参数 public static final BoundaryParameters LARGE_FLAT = new BoundaryParameters(2.0, 10.0, 0.5); public static final BoundaryParameters COMPLEX_SMALL = new BoundaryParameters(0.5, 5.0, 0.2); public static final BoundaryParameters DEFAULT = new BoundaryParameters(1.0, 8.0, 0.3); } // 场景分析类 public static class SceneAnalysis { public double totalDistance; // 总行走距离 public double area; // 估算面积 public double complexity; // 边界复杂度 (0-1) public double elevationRange; // 高程变化范围 public double avgSpeed; // 平均行走速度 public String suggestedPreset; // 建议的预设场景 @Override public String toString() { return String.format( "场景分析结果:\n" + " 总距离: %.1f米\n" + " 估算面积: %.1f平方米\n" + " 边界复杂度: %.2f\n" + " 高程变化: %.1f米\n" + " 平均速度: %.1f米/秒\n" + " 建议场景: %s", totalDistance, area, complexity, elevationRange, avgSpeed, suggestedPreset ); } } // 处理边界数据的主要方法(增强版) public List processBoundaryData(String gnggaData, double baseLat, double baseLon, double interval, double angleThreshold) { return processBoundaryDataAdvanced(gnggaData, baseLat, baseLon, new BoundaryParameters(interval, angleThreshold, interval/2)); } // 高级处理边界数据方法 public List processBoundaryDataAdvanced(String gnggaData, double baseLat, double baseLon, BoundaryParameters params) { System.out.println("开始处理边界数据..."); System.out.println("基准站坐标: " + baseLat + ", " + baseLon); System.out.println("参数: 间隔=" + params.interval + "米, 角度阈值=" + params.angleThreshold + "度"); // 1. 解析GNGGA数据 List rawPoints = parseGNGGA(gnggaData, baseLat, baseLon); System.out.println("解析到原始点: " + rawPoints.size() + " 个"); if (rawPoints.size() < 3) { System.out.println("数据点不足,无法形成边界"); return new ArrayList<>(); } // 2. 过滤和平滑数据 List filteredPoints = filterAndSmoothPoints(rawPoints); System.out.println("过滤后点: " + filteredPoints.size() + " 个"); // 3. 高级边界点优化 List optimizedPoints = optimizeBoundaryPointsAdvanced(filteredPoints, params); System.out.println("优化后边界点: " + optimizedPoints.size() + " 个"); // 4. 边界质量评估 BoundaryQuality quality = evaluateBoundaryQuality(optimizedPoints); System.out.println(quality.toString()); return optimizedPoints; } // 分析GNGGA数据并自动选择场景 public SceneAnalysis analyzeGNGGAData(String gnggaData, double baseLat, double baseLon) { SceneAnalysis analysis = new SceneAnalysis(); // 1. 解析GNGGA数据获取原始点 List rawPoints = parseGNGGA(gnggaData, baseLat, baseLon); if (rawPoints.size() < 3) { analysis.suggestedPreset = "复杂小区域"; return analysis; } // 2. 计算基本统计信息 calculateBasicStatistics(rawPoints, analysis); // 3. 计算边界复杂度 calculateBoundaryComplexity(rawPoints, analysis); // 4. 分析高程变化 analyzeElevationData(gnggaData, analysis); // 5. 自动选择预设场景 selectPresetAutomatically(analysis); System.out.println(analysis.toString()); return analysis; } // 计算基本统计信息 private void calculateBasicStatistics(List points, SceneAnalysis analysis) { // 计算总距离 analysis.totalDistance = 0; for (int i = 1; i < points.size(); i++) { analysis.totalDistance += calculateDistance(points.get(i-1), points.get(i)); } // 计算面积 analysis.area = calculatePolygonArea(points); // 计算平均速度 (假设时间间隔为1秒) if (points.size() > 1) { analysis.avgSpeed = analysis.totalDistance / (points.size() - 1); } } // 计算边界复杂度 private void calculateBoundaryComplexity(List points, SceneAnalysis analysis) { if (points.size() < 3) { analysis.complexity = 0; return; } double totalAngleChange = 0; int angleCount = 0; for (int i = 1; i < points.size() - 1; i++) { double angleChange = Math.abs(calculateAngleChange( points.get(i-1), points.get(i), points.get(i+1) )); totalAngleChange += angleChange; angleCount++; } // 复杂度基于角度变化和边界长度 double avgAngleChange = angleCount > 0 ? totalAngleChange / angleCount : 0; // 标准化复杂度 (0-1范围) analysis.complexity = Math.min(1.0, avgAngleChange / 45.0); // 45度作为高复杂度阈值 } // 分析高程数据 private void analyzeElevationData(String gnggaData, SceneAnalysis analysis) { double minElevation = Double.MAX_VALUE; double maxElevation = Double.MIN_VALUE; int elevationCount = 0; String[] records = gnggaData.split("\\$GNGGA"); for (String record : records) { try { String trimmedRecord = record.trim(); if (trimmedRecord.isEmpty()) continue; if (!trimmedRecord.startsWith(",")) { trimmedRecord = "," + trimmedRecord; } String[] fields = trimmedRecord.split(","); if (fields.length >= 10) { // 第10个字段是海拔高度 String elevationStr = fields[9]; if (!elevationStr.isEmpty()) { double elevation = Double.parseDouble(elevationStr); minElevation = Math.min(minElevation, elevation); maxElevation = Math.max(maxElevation, elevation); elevationCount++; } } } catch (Exception e) { // 忽略解析错误 } } if (elevationCount > 0) { analysis.elevationRange = maxElevation - minElevation; } else { analysis.elevationRange = 0; } } // 自动选择预设场景 private void selectPresetAutomatically(SceneAnalysis analysis) { // 决策逻辑基于多个因素 double areaWeight = 0.4; double complexityWeight = 0.3; double elevationWeight = 0.2; double speedWeight = 0.1; // 计算综合得分 double score = 0; // 面积因素:面积越大,越适合大间隔 double areaScore = Math.min(1.0, analysis.area / 1000.0); // 1000平方米为基准 score += areaScore * areaWeight; // 复杂度因素:复杂度越高,越需要小间隔 double complexityScore = analysis.complexity; score += complexityScore * complexityWeight; // 高程因素:高程变化越大,越需要精细处理 double elevationScore = Math.min(1.0, analysis.elevationRange / 10.0); // 10米变化为基准 score += elevationScore * elevationWeight; // 速度因素:速度变化大可能表示复杂地形 double speedScore = Math.min(1.0, analysis.avgSpeed / 2.0); // 2米/秒为基准 score += speedScore * speedWeight; // 根据得分选择预设 if (score < 0.3) { analysis.suggestedPreset = "平坦大区域"; } else if (score < 0.6) { analysis.suggestedPreset = "常规区域"; } else { analysis.suggestedPreset = "复杂小区域"; } System.out.println("自动场景选择得分: " + String.format("%.2f", score) + " -> " + analysis.suggestedPreset); } // 根据场景名称获取参数 public BoundaryParameters getParametersForPreset(String presetName) { switch (presetName) { case "平坦大区域": return BoundaryParameters.LARGE_FLAT; case "复杂小区域": return BoundaryParameters.COMPLEX_SMALL; case "常规区域": default: return BoundaryParameters.DEFAULT; } } // 解析GNGGA格式数据 private List parseGNGGA(String gnggaData, double baseLat, double baseLon) { List points = new ArrayList<>(); if (gnggaData == null || gnggaData.trim().isEmpty()) { return points; } String[] records = gnggaData.split("\\$GNGGA"); System.out.println("找到GNGGA记录: " + (records.length - 1) + " 条"); for (String record : records) { try { String trimmedRecord = record.trim(); if (trimmedRecord.isEmpty()) continue; if (!trimmedRecord.startsWith(",")) { trimmedRecord = "," + trimmedRecord; } String[] fields = trimmedRecord.split(","); if (fields.length < 7) { System.out.println("记录字段不足: " + trimmedRecord); continue; } // 检查定位状态 int fixStatus; try { fixStatus = Integer.parseInt(fields[6]); } catch (NumberFormatException e) { System.out.println("定位状态格式错误: " + fields[6]); continue; } if (fixStatus != 4) { System.out.println("跳过非高精度定位点,状态: " + fixStatus); continue; } // 解析纬度和经度 String latStr = fields[2]; double latitude = parseCoordinate(latStr, fields[3]); String lonStr = fields[4]; double longitude = parseCoordinate(lonStr, fields[5]); // 转换为相对于基准站的坐标 Coordinate coord = convertToLocalCoordinates(latitude, longitude, baseLat, baseLon); points.add(coord); } catch (Exception e) { System.err.println("解析GNGGA数据失败: " + record + ", 错误: " + e.getMessage()); } } return points; } // 解析坐标字符串 (DDMM.MMMMM 格式) private double parseCoordinate(String coordStr, String direction) { try { if (coordStr == null || coordStr.isEmpty()) { return 0; } int dotIndex = coordStr.indexOf('.'); if (dotIndex < 2) { return 0; } int degrees = Integer.parseInt(coordStr.substring(0, dotIndex - 2)); double minutes = Double.parseDouble(coordStr.substring(dotIndex - 2)); double decimal = degrees + minutes / 60.0; if ("S".equals(direction) || "W".equals(direction)) { decimal = -decimal; } return decimal; } catch (Exception e) { System.err.println("坐标解析错误: " + coordStr); return 0; } } // 转换为局部坐标系 private Coordinate convertToLocalCoordinates(double lat, double lon, double baseLat, double baseLon) { double earthRadius = 6371000; double deltaLat = lat - baseLat; double northDistance = deltaLat * Math.PI / 180.0 * earthRadius; double deltaLon = lon - baseLon; double eastDistance = deltaLon * Math.PI / 180.0 * earthRadius * Math.cos(baseLat * Math.PI / 180.0); Coordinate coord = new Coordinate(eastDistance, northDistance, lat, lon); return coord; } // 过滤和平滑数据点 private List filterAndSmoothPoints(List points) { if (points.size() < 3) return points; List filtered = new ArrayList<>(); // 1. 速度过滤 - 移除移动过快的点(可能是噪声) filtered.add(points.get(0)); // 总是保留第一个点 for (int i = 1; i < points.size(); i++) { double speed = calculateDistance(points.get(i-1), points.get(i)); if (speed < 5.0) { // 最大合理速度为5米/秒 filtered.add(points.get(i)); } else { System.out.println("移除高速点: 速度=" + speed + "米/秒"); } } // 2. 简单平滑滤波 List smoothed = movingAverageFilter(filtered, 3); return smoothed; } // 移动平均滤波 private List movingAverageFilter(List points, int windowSize) { if (points.size() <= windowSize) return points; List smoothed = new ArrayList<>(); for (int i = 0; i < points.size(); i++) { double sumX = 0, sumY = 0; int count = 0; for (int j = Math.max(0, i - windowSize/2); j <= Math.min(points.size() - 1, i + windowSize/2); j++) { sumX += points.get(j).x; sumY += points.get(j).y; count++; } Coordinate smoothedPoint = new Coordinate( sumX / count, sumY / count, points.get(i).lat, points.get(i).lon ); smoothed.add(smoothedPoint); } return smoothed; } // 高级边界点优化算法 List optimizeBoundaryPointsAdvanced(List points, BoundaryParameters params) { if (points.size() < 3) return points; List optimized = new ArrayList<>(); // 1. 首先进行道格拉斯-普克算法简化 List simplified = douglasPeuckerSimplification(points, params.simplificationTolerance); System.out.println("道格拉斯-普克简化后: " + simplified.size() + " 个点"); // 2. 基于距离和角度的进一步优化 optimized.add(simplified.get(0)); Coordinate lastAdded = simplified.get(0); for (int i = 1; i < simplified.size(); i++) { Coordinate current = simplified.get(i); Coordinate next = (i < simplified.size() - 1) ? simplified.get(i + 1) : simplified.get(0); double distance = calculateDistance(lastAdded, current); double angleChange = calculateAngleChange(lastAdded, current, next); // 动态调整阈值 double dynamicInterval = adjustIntervalByCurvature(params.interval, angleChange); if (distance >= dynamicInterval || Math.abs(angleChange) > params.angleThreshold) { optimized.add(current); lastAdded = current; } } // 确保闭合 if (!optimized.get(0).equals(optimized.get(optimized.size() - 1))) { Coordinate first = optimized.get(0); Coordinate last = optimized.get(optimized.size() - 1); double closingDistance = calculateDistance(last, first); if (closingDistance > params.interval) { int segments = (int) Math.ceil(closingDistance / params.interval); for (int i = 1; i < segments; i++) { double ratio = (double) i / segments; Coordinate interpolatedPoint = interpolate(last, first, ratio); if (!interpolatedPoint.equals(last)) { optimized.add(interpolatedPoint); } } } optimized.add(first); } return removeConsecutiveDuplicates(optimized); } // 道格拉斯-普克算法 private List douglasPeuckerSimplification(List points, double epsilon) { if (points.size() < 3) return points; // 找到最大距离点 double maxDistance = 0; int index = 0; Coordinate start = points.get(0); Coordinate end = points.get(points.size() - 1); for (int i = 1; i < points.size() - 1; i++) { double distance = perpendicularDistance(points.get(i), start, end); if (distance > maxDistance) { maxDistance = distance; index = i; } } List result = new ArrayList<>(); if (maxDistance > epsilon) { // 递归简化 List firstPart = douglasPeuckerSimplification( points.subList(0, index + 1), epsilon); List secondPart = douglasPeuckerSimplification( points.subList(index, points.size()), epsilon); result.addAll(firstPart.subList(0, firstPart.size() - 1)); result.addAll(secondPart); } else { result.add(start); result.add(end); } return result; } // 计算垂直距离 private double perpendicularDistance(Coordinate point, Coordinate lineStart, Coordinate lineEnd) { double area = Math.abs( (lineEnd.y - lineStart.y) * point.x - (lineEnd.x - lineStart.x) * point.y + lineEnd.x * lineStart.y - lineEnd.y * lineStart.x ); double lineLength = calculateDistance(lineStart, lineEnd); return lineLength > 0 ? area / lineLength : 0; } // 根据曲率动态调整间隔 private double adjustIntervalByCurvature(double baseInterval, double angleChange) { double curvatureFactor = Math.max(0.3, Math.min(2.0, 1.0 - Math.abs(angleChange) / 90.0)); return baseInterval * curvatureFactor; } // 边界插值 public List interpolateBoundary(List boundary, double maxGap) { List interpolated = new ArrayList<>(); for (int i = 0; i < boundary.size(); i++) { interpolated.add(boundary.get(i)); Coordinate current = boundary.get(i); Coordinate next = boundary.get((i + 1) % boundary.size()); double distance = calculateDistance(current, next); if (distance > maxGap) { int segments = (int) Math.ceil(distance / maxGap); for (int j = 1; j < segments; j++) { double ratio = (double) j / segments; Coordinate interpolatedPoint = interpolate(current, next, ratio); interpolated.add(interpolatedPoint); } } } return interpolated; } private List removeConsecutiveDuplicates(List points) { if (points == null || points.isEmpty()) { return points; } List cleaned = new ArrayList<>(); Coordinate previous = null; for (Coordinate point : points) { if (previous == null || !point.equals(previous)) { cleaned.add(point); previous = point; } } if (!cleaned.isEmpty() && !cleaned.get(0).equals(cleaned.get(cleaned.size() - 1))) { cleaned.add(cleaned.get(0)); } return cleaned; } // 线性插值 private Coordinate interpolate(Coordinate p1, Coordinate p2, double ratio) { double x = p1.x + (p2.x - p1.x) * ratio; double y = p1.y + (p2.y - p1.y) * ratio; double lat = p1.lat + (p2.lat - p1.lat) * ratio; double lon = p1.lon + (p2.lon - p1.lon) * ratio; return new Coordinate(x, y, lat, lon); } // 边界质量评估 public BoundaryQuality evaluateBoundaryQuality(List boundary) { BoundaryQuality quality = new BoundaryQuality(); if (boundary.size() < 3) { return quality; } // 检查闭合 Coordinate first = boundary.get(0); Coordinate last = boundary.get(boundary.size() - 1); quality.isClosed = calculateDistance(first, last) < 1.0; quality.closureError = calculateDistance(first, last); // 检查自相交 quality.selfIntersectionCount = countSelfIntersections(boundary); // 计算平滑度 quality.smoothness = calculateBoundarySmoothness(boundary); // 计算面积 quality.area = calculatePolygonArea(boundary); quality.pointCount = boundary.size(); return quality; } // 计算边界平滑度 private double calculateBoundarySmoothness(List boundary) { double totalAngleChange = 0; int count = 0; for (int i = 1; i < boundary.size() - 1; i++) { double angleChange = calculateAngleChange( boundary.get(i-1), boundary.get(i), boundary.get(i+1) ); totalAngleChange += Math.abs(angleChange); count++; } return count > 0 ? totalAngleChange / count : 0; } // 计算自相交数量 private int countSelfIntersections(List boundary) { int intersections = 0; int n = boundary.size(); for (int i = 0; i < n; i++) { Coordinate a1 = boundary.get(i); Coordinate a2 = boundary.get((i + 1) % n); for (int j = i + 2; j < n; j++) { Coordinate b1 = boundary.get(j); Coordinate b2 = boundary.get((j + 1) % n); if (doLinesIntersect(a1, a2, b1, b2)) { intersections++; } } } return intersections; } // 检查两线段是否相交 private boolean doLinesIntersect(Coordinate a1, Coordinate a2, Coordinate b1, Coordinate b2) { double denominator = ((b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y)); if (denominator == 0) return false; // 平行 double ua = ((b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x)) / denominator; double ub = ((a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x)) / denominator; return ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1; } // 计算两点间距离 private double calculateDistance(Coordinate p1, Coordinate p2) { double dx = p2.x - p1.x; double dy = p2.y - p1.y; return Math.sqrt(dx * dx + dy * dy); } // 计算角度变化 private double calculateAngleChange(Coordinate prev, Coordinate current, Coordinate next) { double angle1 = Math.atan2(current.y - prev.y, current.x - prev.x); double angle2 = Math.atan2(next.y - current.y, next.x - current.x); double angleChange = Math.toDegrees(angle2 - angle1); // 规范化角度到 [-180, 180] while (angleChange > 180) angleChange -= 360; while (angleChange < -180) angleChange += 360; return angleChange; } // 计算多边形面积 public double calculatePolygonArea(List points) { if (points.size() < 3) return 0.0; double area = 0.0; int n = points.size(); for (int i = 0; i < n; i++) { Coordinate current = points.get(i); Coordinate next = points.get((i + 1) % n); area += (current.x * next.y - next.x * current.y); } return Math.abs(area) / 2.0; } // 边界编辑功能 public List editBoundary(List boundary, int pointIndex, Coordinate newPosition) { if (pointIndex < 0 || pointIndex >= boundary.size()) { return boundary; } List edited = new ArrayList<>(); for (int i = 0; i < boundary.size(); i++) { if (i == pointIndex) { edited.add(newPosition); } else { edited.add(boundary.get(i).copy()); } } // 自动平滑编辑点周围的区域 return smoothLocalArea(edited, pointIndex, 2); } // 平滑局部区域 private List smoothLocalArea(List boundary, int centerIndex, int radius) { List smoothed = new ArrayList<>(boundary); int n = boundary.size(); for (int i = Math.max(0, centerIndex - radius); i <= Math.min(n - 1, centerIndex + radius); i++) { if (i == 0 || i == n - 1) continue; // 不移动首尾点 double sumX = 0, sumY = 0; int count = 0; for (int j = Math.max(0, i - 1); j <= Math.min(n - 1, i + 1); j++) { sumX += boundary.get(j).x; sumY += boundary.get(j).y; count++; } Coordinate smoothedPoint = new Coordinate( sumX / count, sumY / count, boundary.get(i).lat, boundary.get(i).lon ); smoothed.set(i, smoothedPoint); } return smoothed; } }