package zhangaiwu; import java.util.ArrayList; import java.util.List; /** * 割草机地块障碍物类 * 用于保存和管理地块障碍物的相关信息 */ 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; } /** * 设置基准站坐标字符串 * @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; } } } }