package zhangaiwu; import java.util.ArrayList; import java.util.List; import java.util.Locale; /** * 割草机地块障碍物类 * 用于保存和管理地块障碍物的相关信息 */ public class Obstacledge { /** * 障碍物形状枚举 */ public enum ObstacleShape { CIRCLE(0, "圆形"), POLYGON(1, "多边形"); private final int code; private final String description; ObstacleShape(int code, String description) { this.code = code; this.description = description; } public int getCode() { return code; } public String getDescription() { return description; } /** * 根据代码获取形状枚举 * @param code 形状代码 * @return 对应的形状枚举,如果找不到返回null */ public static ObstacleShape getByCode(int code) { for (ObstacleShape shape : values()) { if (shape.getCode() == code) { return shape; } } return null; } } /** * 坐标点类 - 用于存储度分格式的坐标 */ public static class DMCoordinate { private double degreeMinute; // 度分值,如2324.200273 private char direction; // 方向,N/S/E/W public DMCoordinate() { } public DMCoordinate(double degreeMinute, char direction) { this.degreeMinute = degreeMinute; this.direction = direction; } public double getDegreeMinute() { return degreeMinute; } public void setDegreeMinute(double degreeMinute) { this.degreeMinute = degreeMinute; } public char getDirection() { return direction; } public void setDirection(char direction) { this.direction = direction; } /** * 将度分格式转换为度格式 * @return 十进制度数 */ public double toDecimalDegree() { // 分离度和分 double dm = Math.abs(degreeMinute); int degrees = (int)(dm / 100); // 度在百位以上 double minutes = dm - degrees * 100; // 计算十进制度数 double decimalDegrees = degrees + minutes / 60.0; // 根据方向确定正负 if (direction == 'S' || direction == 'W') { decimalDegrees = -decimalDegrees; } return decimalDegrees; } /** * 将坐标点转换为字符串格式 * @return 格式如 "2324.200273,N" */ @Override public String toString() { return String.format("%.6f,%c", degreeMinute, direction); } /** * 从字符串解析坐标点 * @param coordStr 格式如 "2324.200273,N" * @return DMCoordinate对象 */ public static DMCoordinate fromString(String coordStr) { String[] parts = coordStr.split(","); if (parts.length != 2) { throw new IllegalArgumentException("Invalid coordinate format: " + coordStr); } DMCoordinate coord = new DMCoordinate(); coord.setDegreeMinute(Double.parseDouble(parts[0])); coord.setDirection(parts[1].charAt(0)); return coord; } } /** * XY坐标点类 - 用于存储相对于基准站的平面坐标 */ public static class XYCoordinate { private double x; // X坐标(东方向) private double y; // Y坐标(北方向) public XYCoordinate() { } public XYCoordinate(double x, double y) { this.x = x; this.y = y; } public double getX() { return x; } public void setX(double x) { this.x = x; } public double getY() { return y; } public void setY(double y) { this.y = y; } @Override public String toString() { return String.format("%.2f,%.2f", x, y); } /** * 从字符串解析XY坐标点 * @param xyStr 格式如 "5.2,3.8" * @return XYCoordinate对象 */ public static XYCoordinate fromString(String xyStr) { String[] parts = xyStr.split(","); if (parts.length != 2) { throw new IllegalArgumentException("Invalid XY coordinate format: " + xyStr); } XYCoordinate coord = new XYCoordinate(); coord.setX(Double.parseDouble(parts[0])); coord.setY(Double.parseDouble(parts[1])); return coord; } } /** * 障碍物类 */ public static class Obstacle { private String plotId; // 地块编号 private String obstacleName; // 障碍物名称(唯一) private ObstacleShape shape; // 障碍物形状 private List originalCoordinates = new ArrayList<>(); // 原始坐标列表 private List xyCoordinates = new ArrayList<>(); // XY坐标列表 public Obstacle() { } public Obstacle(String plotId, String obstacleName, ObstacleShape shape) { this.plotId = plotId; this.obstacleName = obstacleName; this.shape = shape; } // Getter和Setter方法 public String getPlotId() { return plotId; } public void setPlotId(String plotId) { this.plotId = plotId; } public String getObstacleName() { return obstacleName; } public void setObstacleName(String obstacleName) { this.obstacleName = obstacleName; } public ObstacleShape getShape() { return shape; } public void setShape(ObstacleShape shape) { this.shape = shape; } /** * 设置形状(通过代码) * @param shapeCode 形状代码(0=圆形,1=多边形) */ public void setShapeByCode(int shapeCode) { this.shape = ObstacleShape.getByCode(shapeCode); } public List getOriginalCoordinates() { return originalCoordinates; } public void setOriginalCoordinates(List originalCoordinates) { this.originalCoordinates = originalCoordinates; } /** * 添加原始坐标点 * @param coordinate 度分格式坐标点 */ public void addOriginalCoordinate(DMCoordinate coordinate) { this.originalCoordinates.add(coordinate); } /** * 添加原始坐标点 * @param degreeMinute 度分值 * @param direction 方向 */ public void addOriginalCoordinate(double degreeMinute, char direction) { this.originalCoordinates.add(new DMCoordinate(degreeMinute, direction)); } public List getXyCoordinates() { return xyCoordinates; } public void setXyCoordinates(List xyCoordinates) { this.xyCoordinates = xyCoordinates; } /** * 添加XY坐标点 * @param coordinate XY坐标点 */ public void addXyCoordinate(XYCoordinate coordinate) { this.xyCoordinates.add(coordinate); } /** * 添加XY坐标点 * @param x X坐标 * @param y Y坐标 */ public void addXyCoordinate(double x, double y) { this.xyCoordinates.add(new XYCoordinate(x, y)); } /** * 获取原始坐标字符串 * @return 格式如 "2324.200273,N,11330.666730,E;2324.200400,N,11330.666850,E" */ public String getOriginalCoordsString() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < originalCoordinates.size(); i++) { if (i > 0) { sb.append(";"); } sb.append(originalCoordinates.get(i).toString()); } return sb.toString(); } /** * 设置原始坐标字符串 * @param coordsStr 格式如 "2324.200273,N,11330.666730,E;2324.200400,N,11330.666850,E" */ public void setOriginalCoordsString(String coordsStr) { originalCoordinates.clear(); String[] points = coordsStr.split(";"); for (String point : points) { // 每个点可能是两个DMCoordinate的组合(纬度+经度) String[] coords = point.split(","); if (coords.length == 4) { // 格式:纬度度分,纬度方向,经度度分,经度方向 double latDM = Double.parseDouble(coords[0]); char latDir = coords[1].charAt(0); double lonDM = Double.parseDouble(coords[2]); char lonDir = coords[3].charAt(0); // 注意:实际存储时需要分别存储纬度和经度坐标点 // 这里简化处理,存储为两个DMCoordinate对象 addOriginalCoordinate(latDM, latDir); addOriginalCoordinate(lonDM, lonDir); } else if (coords.length == 2) { // 格式:度分,方向 originalCoordinates.add(DMCoordinate.fromString(point)); } } } /** * 获取XY坐标字符串 * @return 格式如 "5.2,3.8;7.5,6.2" */ public String getXyCoordsString() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < xyCoordinates.size(); i++) { if (i > 0) { sb.append(";"); } sb.append(xyCoordinates.get(i).toString()); } return sb.toString(); } /** * 设置XY坐标字符串 * @param xyCoordsStr 格式如 "5.2,3.8;7.5,6.2" */ public void setXyCoordsString(String xyCoordsStr) { xyCoordinates.clear(); String[] points = xyCoordsStr.split(";"); for (String point : points) { xyCoordinates.add(XYCoordinate.fromString(point)); } } /** * 验证障碍物数据的有效性 * @return 是否有效 */ public boolean isValid() { if (plotId == null || plotId.isEmpty()) { return false; } if (obstacleName == null || obstacleName.isEmpty()) { return false; } if (shape == null) { return false; } // 根据形状验证坐标点数量 if (shape == ObstacleShape.CIRCLE) { // 圆形需要至少2个坐标点(圆心和圆上一点) return originalCoordinates.size() >= 2 && xyCoordinates.size() >= 2; } else if (shape == ObstacleShape.POLYGON) { // 多边形需要至少3个坐标点 return originalCoordinates.size() >= 6 && xyCoordinates.size() >= 3; // 原始坐标每个点有纬度和经度,所以是2倍 } return false; } /** * 计算圆形障碍物的半径(如果形状是圆形) * @return 半径(米),如果不是圆形返回-1 */ public double calculateRadius() { if (shape != ObstacleShape.CIRCLE || xyCoordinates.size() < 2) { return -1; } XYCoordinate center = xyCoordinates.get(0); XYCoordinate pointOnCircle = xyCoordinates.get(1); double dx = pointOnCircle.getX() - center.getX(); double dy = pointOnCircle.getY() - center.getY(); return Math.sqrt(dx * dx + dy * dy); } /** * 计算多边形障碍物的面积(如果形状是多边形) * @return 面积(平方米),如果不是多边形返回-1 */ public double calculateArea() { if (shape != ObstacleShape.POLYGON || xyCoordinates.size() < 3) { return -1; } double area = 0; int n = xyCoordinates.size(); for (int i = 0; i < n; i++) { XYCoordinate current = xyCoordinates.get(i); XYCoordinate next = xyCoordinates.get((i + 1) % n); area += current.getX() * next.getY(); area -= current.getY() * next.getX(); } return Math.abs(area) / 2.0; } @Override public String toString() { return String.format("Obstacle{plotId='%s', name='%s', shape=%s, points=%d}", plotId, obstacleName, shape.getDescription(), originalCoordinates.size() / 2); } } /** * 地块类 */ public static class Plot { private String plotId; // 地块编号(唯一) private DMCoordinate baseStationLat; // 基准站纬度 private DMCoordinate baseStationLon; // 基准站经度 private List obstacles = new ArrayList<>(); // 地块内的障碍物列表 public Plot() { } public Plot(String plotId) { this.plotId = plotId; } // Getter和Setter方法 public String getPlotId() { return plotId; } public void setPlotId(String plotId) { this.plotId = plotId; } public DMCoordinate getBaseStationLat() { return baseStationLat; } public void setBaseStationLat(DMCoordinate baseStationLat) { this.baseStationLat = baseStationLat; } public DMCoordinate getBaseStationLon() { return baseStationLon; } public void setBaseStationLon(DMCoordinate baseStationLon) { this.baseStationLon = baseStationLon; } /** * 设置基准站坐标 * @param lat 纬度度分坐标 * @param lon 经度度分坐标 */ public void setBaseStation(DMCoordinate lat, DMCoordinate lon) { this.baseStationLat = lat; this.baseStationLon = lon; } /** * 清除基准站坐标 */ public void clearBaseStation() { this.baseStationLat = null; this.baseStationLon = null; } /** * 设置基准站坐标字符串 * @param baseStationStr 格式如 "2324.200273,N,11330.666730,E" */ public void setBaseStationString(String baseStationStr) { String[] parts = baseStationStr.split(","); if (parts.length != 4) { throw new IllegalArgumentException("Invalid base station format: " + baseStationStr); } double latDM = Double.parseDouble(parts[0]); char latDir = parts[1].charAt(0); double lonDM = Double.parseDouble(parts[2]); char lonDir = parts[3].charAt(0); this.baseStationLat = new DMCoordinate(latDM, latDir); this.baseStationLon = new DMCoordinate(lonDM, lonDir); } /** * 获取基准站坐标字符串 * @return 格式如 "2324.200273,N,11330.666730,E" */ public String getBaseStationString() { if (baseStationLat == null || baseStationLon == null) { return ""; } return String.format("%s,%s", baseStationLat.toString(), baseStationLon.toString()); } public List getObstacles() { return obstacles; } public void setObstacles(List obstacles) { this.obstacles = obstacles; } /** * 添加障碍物 * @param obstacle 障碍物对象 */ public void addObstacle(Obstacle obstacle) { this.obstacles.add(obstacle); } /** * 根据障碍物名称获取障碍物 * @param obstacleName 障碍物名称 * @return 障碍物对象,如果找不到返回null */ public Obstacle getObstacleByName(String obstacleName) { for (Obstacle obstacle : obstacles) { if (obstacle.getObstacleName().equals(obstacleName)) { return obstacle; } } return null; } /** * 根据障碍物名称移除障碍物 * @param obstacleName 障碍物名称 * @return 是否成功移除 */ public boolean removeObstacleByName(String obstacleName) { return obstacles.removeIf(obstacle -> obstacle.getObstacleName().equals(obstacleName)); } /** * 验证地块数据的有效性 * @return 是否有效 */ public boolean isValid() { if (plotId == null || plotId.isEmpty()) { return false; } if (baseStationLat == null || baseStationLon == null) { return false; } // 验证所有障碍物 for (Obstacle obstacle : obstacles) { if (!obstacle.isValid()) { return false; } } return true; } /** * 获取地块中障碍物的数量 * @return 障碍物数量 */ public int getObstacleCount() { return obstacles.size(); } @Override public String toString() { return String.format("Plot{id='%s', baseStation=%s, obstacles=%d}", plotId, getBaseStationString(), obstacles.size()); } } /** * 配置文件管理器类 */ public static class ConfigManager { private List plots = new ArrayList<>(); public ConfigManager() { } // Getter和Setter方法 public List getPlots() { return plots; } public void setPlots(List plots) { this.plots = plots; } /** * 添加地块 * @param plot 地块对象 */ public void addPlot(Plot plot) { this.plots.add(plot); } /** * 根据地块编号获取地块 * @param plotId 地块编号 * @return 地块对象,如果找不到返回null */ public Plot getPlotById(String plotId) { for (Plot plot : plots) { if (plot.getPlotId().equals(plotId)) { return plot; } } return null; } /** * 根据地块编号移除地块 * @param plotId 地块编号 * @return 是否成功移除 */ public boolean removePlotById(String plotId) { return plots.removeIf(plot -> plot.getPlotId().equals(plotId)); } /** * 获取所有地块中的障碍物总数 * @return 障碍物总数 */ public int getTotalObstacleCount() { int count = 0; for (Plot plot : plots) { count += plot.getObstacleCount(); } return count; } /** * 验证所有地块数据的有效性 * @return 是否所有数据都有效 */ public boolean validateAll() { for (Plot plot : plots) { if (!plot.isValid()) { return false; } } return true; } /** * 转换为properties文件格式字符串 * @return properties格式字符串 */ public String toPropertiesString() { StringBuilder sb = new StringBuilder(); sb.append("# 割草机地块障碍物配置文件\n"); sb.append("# 生成时间:").append(java.time.LocalDateTime.now()).append("\n"); sb.append("# 坐标系:WGS84(度分格式)\n\n"); sb.append("# ============ 地块基准站配置 ============\n"); sb.append("# 格式:plot.[地块编号].baseStation=[经度],[N/S],[纬度],[E/W]\n"); // 添加基准站配置 for (Plot plot : plots) { sb.append("plot.").append(plot.getPlotId()) .append(".baseStation=").append(plot.getBaseStationString()) .append("\n"); } sb.append("\n# ============ 障碍物配置 ============\n"); sb.append("# 格式:plot.[地块编号].obstacle.[障碍物名称].shape=[0|1]\n"); sb.append("# 格式:plot.[地块编号].obstacle.[障碍物名称].originalCoords=[坐标串]\n"); sb.append("# 格式:plot.[地块编号].obstacle.[障碍物名称].xyCoords=[坐标串]\n\n"); // 添加障碍物配置 for (Plot plot : plots) { if (plot.getObstacleCount() > 0) { sb.append("# --- 地块").append(plot.getPlotId()).append("的障碍物 ---\n"); for (Obstacle obstacle : plot.getObstacles()) { sb.append("plot.").append(plot.getPlotId()) .append(".obstacle.").append(obstacle.getObstacleName()) .append(".shape=").append(obstacle.getShape().getCode()) .append("\n"); sb.append("plot.").append(plot.getPlotId()) .append(".obstacle.").append(obstacle.getObstacleName()) .append(".originalCoords=").append(obstacle.getOriginalCoordsString()) .append("\n"); sb.append("plot.").append(plot.getPlotId()) .append(".obstacle.").append(obstacle.getObstacleName()) .append(".xyCoords=").append(obstacle.getXyCoordsString()) .append("\n"); } sb.append("\n"); } } return sb.toString(); } /** * 保存配置到文件 * @param filePath 文件路径 * @return 是否保存成功 */ public boolean saveToFile(String filePath) { try { String propertiesContent = toPropertiesString(); java.nio.file.Files.write( java.nio.file.Paths.get(filePath), propertiesContent.getBytes(java.nio.charset.StandardCharsets.UTF_8) ); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 从properties文件加载配置 * @param filePath 文件路径 * @return 是否加载成功 */ public boolean loadFromFile(String filePath) { try { List lines = java.nio.file.Files.readAllLines( java.nio.file.Paths.get(filePath), java.nio.charset.StandardCharsets.UTF_8 ); plots.clear(); Plot currentPlot = null; Obstacle currentObstacle = null; for (String line : lines) { line = line.trim(); // 跳过空行和注释 if (line.isEmpty() || line.startsWith("#")) { continue; } // 解析键值对 if (line.contains("=")) { String[] parts = line.split("=", 2); String key = parts[0].trim(); String value = parts[1].trim(); // 解析键的结构 String[] keyParts = key.split("\\."); if (keyParts.length >= 3) { String plotId = keyParts[1]; // 查找或创建地块 if (currentPlot == null || !currentPlot.getPlotId().equals(plotId)) { currentPlot = getPlotById(plotId); if (currentPlot == null) { currentPlot = new Plot(plotId); addPlot(currentPlot); } } if ("baseStation".equals(keyParts[2])) { // 基准站坐标 currentPlot.setBaseStationString(value); } else if ("obstacle".equals(keyParts[2]) && keyParts.length >= 5) { String obstacleName = keyParts[3]; String property = keyParts[4]; // 查找或创建障碍物 if (currentObstacle == null || !currentObstacle.getObstacleName().equals(obstacleName)) { currentObstacle = currentPlot.getObstacleByName(obstacleName); if (currentObstacle == null) { currentObstacle = new Obstacle(); currentObstacle.setPlotId(plotId); currentObstacle.setObstacleName(obstacleName); currentPlot.addObstacle(currentObstacle); } } // 设置障碍物属性 switch (property) { case "shape": currentObstacle.setShapeByCode(Integer.parseInt(value)); break; case "originalCoords": currentObstacle.setOriginalCoordsString(value); break; case "xyCoords": currentObstacle.setXyCoordsString(value); break; } } } } } return true; } catch (Exception e) { e.printStackTrace(); return false; } } } /** * 将路径规划使用的障碍物坐标字符串解析为障碍物列表。 * @param payload 障碍物坐标字符串 * @param landNumber 地块编号 * @return 解析得到的障碍物列表 */ public static List parsePlannerPayload(String payload, String landNumber) { return ObstaclePayloadConverter.parse(payload, landNumber); } /** * 将障碍物列表转换为路径规划使用的坐标字符串。 * @param obstacles 障碍物列表 * @return 坐标字符串,若列表为空返回null */ public static String buildPlannerPayload(List obstacles) { return ObstaclePayloadConverter.build(obstacles); } /** * 障碍物坐标字符串与数据对象之间的转换工具。 */ private static final class ObstaclePayloadConverter { private ObstaclePayloadConverter() { } private static List parse(String payload, String landNumber) { List obstacles = new ArrayList<>(); if (payload == null) { return obstacles; } String normalizedPayload = stripInlineComment(payload.trim()); if (normalizedPayload.isEmpty() || "-1".equals(normalizedPayload)) { return obstacles; } String normalizedLand = landNumber != null ? landNumber.trim() : null; List entries = splitEntries(normalizedPayload); int defaultIndex = 1; for (String entry : entries) { String trimmedEntry = stripInlineComment(entry); if (trimmedEntry.isEmpty()) { continue; } String nameToken = null; String shapeToken = null; String coordsSection = trimmedEntry; if (trimmedEntry.contains("::")) { String[] parts = trimmedEntry.split("::", 3); if (parts.length == 3) { nameToken = parts[0].trim(); shapeToken = parts[1].trim(); coordsSection = parts[2].trim(); } } else if (trimmedEntry.contains("@")) { String[] parts = trimmedEntry.split("@", 3); if (parts.length == 3) { nameToken = parts[0].trim(); shapeToken = parts[1].trim(); coordsSection = parts[2].trim(); } else if (parts.length == 2) { shapeToken = parts[0].trim(); coordsSection = parts[1].trim(); } } else if (trimmedEntry.contains(":")) { String[] parts = trimmedEntry.split(":", 3); if (parts.length == 3) { nameToken = parts[0].trim(); shapeToken = parts[1].trim(); coordsSection = parts[2].trim(); } else if (parts.length == 2) { if (looksLikeShapeToken(parts[0])) { shapeToken = parts[0].trim(); coordsSection = parts[1].trim(); } else { nameToken = parts[0].trim(); coordsSection = parts[1].trim(); } } } List xyCoordinates = parseCoordinates(coordsSection); if (xyCoordinates.size() < 2) { continue; } ObstacleShape shape = resolveShape(shapeToken, xyCoordinates.size()); if (shape == null) { continue; } String obstacleName; if (nameToken != null && !nameToken.isEmpty()) { obstacleName = nameToken; } else { obstacleName = "障碍物" + defaultIndex++; } Obstacle obstacle = new Obstacle(normalizedLand, obstacleName, shape); obstacle.setPlotId(normalizedLand); obstacle.setObstacleName(obstacleName); obstacle.setShape(shape); obstacle.setXyCoordinates(new ArrayList<>(xyCoordinates)); populatePlaceholderOriginalCoords(obstacle, xyCoordinates.size()); if (obstacle.isValid()) { obstacles.add(obstacle); } } return obstacles; } private static String build(List obstacles) { if (obstacles == null || obstacles.isEmpty()) { return null; } StringBuilder builder = new StringBuilder(); for (Obstacle obstacle : obstacles) { if (obstacle == null) { continue; } List coords = obstacle.getXyCoordinates(); if (coords == null || coords.isEmpty()) { continue; } if (builder.length() > 0) { builder.append(' '); } builder.append('('); for (int i = 0; i < coords.size(); i++) { XYCoordinate coord = coords.get(i); if (coord == null) { continue; } if (i > 0) { builder.append(';'); } builder.append(formatDouble(coord.getX())).append(',').append(formatDouble(coord.getY())); } builder.append(')'); } return builder.length() == 0 ? null : builder.toString(); } private static boolean looksLikeShapeToken(String token) { if (token == null) { return false; } String normalized = token.trim().toLowerCase(Locale.ROOT); return "circle".equals(normalized) || "polygon".equals(normalized) || "圆形".equals(normalized) || "多边形".equals(normalized) || "0".equals(normalized) || "1".equals(normalized); } private static List parseCoordinates(String coordsSection) { List coords = new ArrayList<>(); if (coordsSection == null) { return coords; } String sanitized = stripInlineComment(coordsSection.trim()); if (sanitized.isEmpty() || "-1".equals(sanitized)) { return coords; } // Remove wrapper characters like parentheses that surround persisted payloads sanitized = sanitized.replace("(", "").replace(")", ""); String[] pairs = sanitized.split(";"); for (String pair : pairs) { if (pair == null) { continue; } String trimmed = stripInlineComment(pair.trim()); if (trimmed.isEmpty()) { continue; } trimmed = trimmed.replace("(", "").replace(")", ""); if (trimmed.isEmpty()) { continue; } String[] parts = trimmed.split(","); if (parts.length < 2) { continue; } try { double x = Double.parseDouble(parts[0].trim()); double y = Double.parseDouble(parts[1].trim()); coords.add(new XYCoordinate(x, y)); } catch (NumberFormatException ignored) { // 忽略格式错误的坐标点 } } return coords; } private static ObstacleShape resolveShape(String shapeToken, int coordinateCount) { if (shapeToken != null && !shapeToken.trim().isEmpty()) { String normalized = shapeToken.trim().toLowerCase(Locale.ROOT); if ("circle".equals(normalized) || "圆形".equals(normalized) || "0".equals(normalized)) { return ObstacleShape.CIRCLE; } if ("polygon".equals(normalized) || "多边形".equals(normalized) || "1".equals(normalized)) { return ObstacleShape.POLYGON; } } if (coordinateCount == 2) { return ObstacleShape.CIRCLE; } if (coordinateCount >= 3) { return ObstacleShape.POLYGON; } return null; } private static void populatePlaceholderOriginalCoords(Obstacle obstacle, int xyCount) { List dmCoordinates = new ArrayList<>(); int points = Math.max(1, xyCount); for (int i = 0; i < points; i++) { dmCoordinates.add(new DMCoordinate(0.0, 'N')); dmCoordinates.add(new DMCoordinate(0.0, 'E')); } obstacle.setOriginalCoordinates(dmCoordinates); } private static String stripInlineComment(String text) { if (text == null) { return ""; } int hashIndex = text.indexOf('#'); if (hashIndex >= 0) { return text.substring(0, hashIndex).trim(); } return text.trim(); } private static List splitEntries(String data) { List entries = new ArrayList<>(); if (data.indexOf('|') >= 0) { String[] parts = data.split("\\|"); for (String part : parts) { if (part != null && !part.trim().isEmpty()) { entries.add(part.trim()); } } } else if (data.contains("\n")) { String[] lines = data.split("\r?\n"); for (String line : lines) { if (line != null && !line.trim().isEmpty()) { entries.add(line.trim()); } } } else { entries.add(data); } return entries; } private static String formatDouble(double value) { return String.format(Locale.ROOT, "%.2f", value); } } }