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<DMCoordinate> originalCoordinates = new ArrayList<>(); // 原始坐标列表
|
private List<XYCoordinate> 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<DMCoordinate> getOriginalCoordinates() {
|
return originalCoordinates;
|
}
|
|
public void setOriginalCoordinates(List<DMCoordinate> 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<XYCoordinate> getXyCoordinates() {
|
return xyCoordinates;
|
}
|
|
public void setXyCoordinates(List<XYCoordinate> 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<Obstacle> 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<Obstacle> getObstacles() {
|
return obstacles;
|
}
|
|
public void setObstacles(List<Obstacle> 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<Plot> plots = new ArrayList<>();
|
|
public ConfigManager() {
|
}
|
|
// Getter和Setter方法
|
|
public List<Plot> getPlots() {
|
return plots;
|
}
|
|
public void setPlots(List<Plot> 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<String> 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<Obstacle> parsePlannerPayload(String payload, String landNumber) {
|
return ObstaclePayloadConverter.parse(payload, landNumber);
|
}
|
|
/**
|
* 将障碍物列表转换为路径规划使用的坐标字符串。
|
* @param obstacles 障碍物列表
|
* @return 坐标字符串,若列表为空返回null
|
*/
|
public static String buildPlannerPayload(List<Obstacle> obstacles) {
|
return ObstaclePayloadConverter.build(obstacles);
|
}
|
|
/**
|
* 障碍物坐标字符串与数据对象之间的转换工具。
|
*/
|
private static final class ObstaclePayloadConverter {
|
private ObstaclePayloadConverter() {
|
}
|
|
private static List<Obstacle> parse(String payload, String landNumber) {
|
List<Obstacle> 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<String> 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<XYCoordinate> 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<Obstacle> obstacles) {
|
if (obstacles == null || obstacles.isEmpty()) {
|
return null;
|
}
|
StringBuilder builder = new StringBuilder();
|
for (Obstacle obstacle : obstacles) {
|
if (obstacle == null) {
|
continue;
|
}
|
List<XYCoordinate> 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<XYCoordinate> parseCoordinates(String coordsSection) {
|
List<XYCoordinate> 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<DMCoordinate> 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<String> splitEntries(String data) {
|
List<String> 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);
|
}
|
}
|
}
|