| | |
| | | package zhangaiwu; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Locale; |
| | | |
| | | /** |
| | | * 割草机地块障碍物类 |
| | |
| | | this.baseStationLat = lat; |
| | | this.baseStationLon = lon; |
| | | } |
| | | |
| | | /** |
| | | * 清除基准站坐标 |
| | | */ |
| | | public void clearBaseStation() { |
| | | this.baseStationLat = null; |
| | | this.baseStationLon = null; |
| | | } |
| | | |
| | | /** |
| | | * 设置基准站坐标字符串 |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 将路径规划使用的障碍物坐标字符串解析为障碍物列表。 |
| | | * @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); |
| | | } |
| | | } |
| | | } |