From 6799351be12deb2f713f2c0a2b4c467a6d1098c3 Mon Sep 17 00:00:00 2001
From: 张世豪 <979909237@qq.com>
Date: 星期二, 02 十二月 2025 19:51:00 +0800
Subject: [PATCH] 2025122

---
 src/zhuye/MapRenderer.java |  335 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 322 insertions(+), 13 deletions(-)

diff --git a/src/zhuye/MapRenderer.java b/src/zhuye/MapRenderer.java
index aebc291..9cb5475 100644
--- a/src/zhuye/MapRenderer.java
+++ b/src/zhuye/MapRenderer.java
@@ -3,21 +3,27 @@
 import javax.swing.*;
 import java.awt.*;
 import java.awt.event.*;
+import java.awt.FontMetrics;
 import java.awt.geom.AffineTransform;
+import java.awt.geom.Ellipse2D;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 import gecaoji.Device;
 import gecaoji.Gecaoji;
 import dikuai.Dikuaiguanli;
 import dikuai.Dikuai;
 import zhangaiwu.Obstacledraw;
 import zhangaiwu.Obstacledge;
+import zhangaiwu.yulanzhangaiwu;
 
 /**
  * 鍦板浘娓叉煋鍣� - 璐熻矗鍧愭爣绯荤粯鍒躲�佽鍥惧彉鎹㈢瓑鍔熻兘
@@ -37,6 +43,8 @@
     private static final Color GRASS_BORDER_COLOR = new Color(60, 179, 113);
     private static final Color BOUNDARY_POINT_COLOR = new Color(128, 0, 128);
     private static final Color BOUNDARY_LABEL_COLOR = Color.BLACK;
+    private static final Color CIRCLE_SAMPLE_COLOR = new Color(220, 20, 60, 230);
+    private static final double CIRCLE_SAMPLE_SIZE = 0.54d;
     private static final double BOUNDARY_POINT_MERGE_THRESHOLD = 0.05;
     private static final SimpleDateFormat TIMESTAMP_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     
@@ -49,6 +57,7 @@
     private List<Obstacledge.Obstacle> currentObstacles;
     private Rectangle2D.Double obstacleBounds;
     private String selectedObstacleName;
+    private String currentObstacleLandNumber;
     private String boundaryName;
     private boolean boundaryPointsVisible;
     private String currentBoundaryLandNumber;
@@ -65,6 +74,8 @@
     private JLabel realtimeSpeedValueLabel;
     private JLabel headingValueLabel;
     private JLabel updateTimeValueLabel;
+    private CircleCaptureOverlay circleCaptureOverlay;
+    private final List<double[]> circleSampleMarkers = new ArrayList<>();
     
     public MapRenderer(JPanel visualizationPanel) {
         this.visualizationPanel = visualizationPanel;
@@ -218,6 +229,16 @@
             Obstacledraw.drawObstacles(g2d, currentObstacles, scale, selectedObstacleName);
         }
 
+        yulanzhangaiwu.renderPreview(g2d, scale);
+
+        if (!circleSampleMarkers.isEmpty()) {
+            drawCircleSampleMarkers(g2d, circleSampleMarkers, scale);
+        }
+
+        if (circleCaptureOverlay != null) {
+            drawCircleCaptureOverlay(g2d, circleCaptureOverlay, scale);
+        }
+
         if (hasPlannedPath) {
             drawCurrentPlannedPath(g2d);
         }
@@ -296,6 +317,137 @@
         lujingdraw.drawPlannedPath(g2d, currentPlannedPath, scale);
     }
 
+    private void drawCircleSampleMarkers(Graphics2D g2d, List<double[]> markers, double scale) {
+        if (markers == null || markers.isEmpty()) {
+            return;
+        }
+        Shape markerShape;
+        double half = CIRCLE_SAMPLE_SIZE / 2.0;
+        g2d.setColor(CIRCLE_SAMPLE_COLOR);
+        g2d.setStroke(new BasicStroke((float) (1.2f / scale), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+        Font originalFont = g2d.getFont();
+        float baseSize = (float) Math.max(12f / scale, 9f);
+        float reducedSize = Math.max(baseSize / 3f, 3f);
+        Font labelFont = originalFont.deriveFont(reducedSize);
+        g2d.setFont(labelFont);
+        FontMetrics metrics = g2d.getFontMetrics();
+        for (double[] pt : markers) {
+            if (pt == null || pt.length < 2 || !Double.isFinite(pt[0]) || !Double.isFinite(pt[1])) {
+                continue;
+            }
+            double x = pt[0];
+            double y = pt[1];
+            markerShape = new Ellipse2D.Double(x - half, y - half, CIRCLE_SAMPLE_SIZE, CIRCLE_SAMPLE_SIZE);
+            g2d.fill(markerShape);
+            String label = String.format(Locale.US, "%.2f,%.2f", x, y);
+            int textWidth = metrics.stringWidth(label);
+            float textX = (float) (x - textWidth / 2.0);
+            float textY = (float) (y - half - 0.2d) - metrics.getDescent();
+            g2d.setColor(new Color(33, 37, 41, 220));
+            g2d.drawString(label, textX, textY);
+            g2d.setColor(CIRCLE_SAMPLE_COLOR);
+        }
+        g2d.setFont(originalFont);
+    }
+
+    private void drawCircleCaptureOverlay(Graphics2D g2d, CircleCaptureOverlay overlay, double scale) {
+        double diameter = overlay.radius * 2.0;
+        Ellipse2D outline = new Ellipse2D.Double(
+                overlay.centerX - overlay.radius,
+                overlay.centerY - overlay.radius,
+                diameter,
+                diameter);
+
+        Color fillColor = new Color(255, 152, 0, 80);
+        Color borderColor = new Color(255, 87, 34, 230);
+        Color centerColor = new Color(46, 139, 87, 230);
+
+        g2d.setColor(fillColor);
+        g2d.fill(outline);
+
+        g2d.setColor(borderColor);
+        g2d.setStroke(new BasicStroke((float) (1.8f / scale), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+        g2d.draw(outline);
+
+        double markerSize = 0.18d;
+        double centerMarkerSize = 0.22d;
+
+        Ellipse2D centerMarker = new Ellipse2D.Double(
+                overlay.centerX - centerMarkerSize / 2.0,
+                overlay.centerY - centerMarkerSize / 2.0,
+                centerMarkerSize,
+                centerMarkerSize);
+        g2d.setColor(centerColor);
+        g2d.fill(centerMarker);
+    }
+
+    public void showCircleCaptureOverlay(double centerX, double centerY, double radius, List<double[]> samplePoints) {
+        List<double[]> copies = new ArrayList<>();
+        if (samplePoints != null) {
+            for (double[] pt : samplePoints) {
+                if (pt == null || pt.length < 2) {
+                    continue;
+                }
+                copies.add(new double[]{pt[0], pt[1]});
+            }
+        }
+        circleCaptureOverlay = new CircleCaptureOverlay(centerX, centerY, radius, copies);
+        updateCircleSampleMarkers(samplePoints);
+        if (visualizationPanel != null) {
+            visualizationPanel.repaint();
+        }
+    }
+
+    public void clearCircleCaptureOverlay() {
+        circleCaptureOverlay = null;
+        if (visualizationPanel != null) {
+            visualizationPanel.repaint();
+        }
+    }
+
+    public void updateCircleSampleMarkers(List<double[]> samplePoints) {
+        circleSampleMarkers.clear();
+        if (samplePoints != null) {
+            for (double[] pt : samplePoints) {
+                if (pt == null || pt.length < 2) {
+                    continue;
+                }
+                double x = pt[0];
+                double y = pt[1];
+                if (!Double.isFinite(x) || !Double.isFinite(y)) {
+                    continue;
+                }
+                circleSampleMarkers.add(new double[]{x, y});
+            }
+        }
+        if (visualizationPanel != null) {
+            visualizationPanel.repaint();
+        }
+    }
+
+    public void clearCircleSampleMarkers() {
+        if (!circleSampleMarkers.isEmpty()) {
+            circleSampleMarkers.clear();
+            if (visualizationPanel != null) {
+                visualizationPanel.repaint();
+            }
+        }
+    }
+
+    private static final class CircleCaptureOverlay {
+        final double centerX;
+        final double centerY;
+        final double radius;
+        final List<double[]> samplePoints;
+
+        CircleCaptureOverlay(double centerX, double centerY, double radius, List<double[]> samplePoints) {
+            this.centerX = centerX;
+            this.centerY = centerY;
+            this.radius = radius;
+            this.samplePoints = samplePoints;
+        }
+    }
+
     private void showMowerInfo() {
         Device device = Device.getGecaoji();
         if (device == null) {
@@ -771,18 +923,24 @@
     }
 
     public void setCurrentObstacles(String obstaclesData, String landNumber) {
-        if (landNumber == null) {
-            clearObstacleData();
-            if (!hasRenderableBoundary() && !hasRenderablePlannedPath()) {
-                resetView();
-            } else {
-                visualizationPanel.repaint();
-            }
-            return;
-        }
-
         List<Obstacledge.Obstacle> parsed = parseObstacles(obstaclesData, landNumber);
-        if (parsed.isEmpty()) {
+        applyObstaclesToRenderer(parsed, landNumber);
+    }
+
+    public void setCurrentObstacles(List<Obstacledge.Obstacle> obstacles, String landNumber) {
+        List<Obstacledge.Obstacle> cloned = cloneObstacles(obstacles, landNumber);
+        applyObstaclesToRenderer(cloned, landNumber);
+    }
+
+    private void applyObstaclesToRenderer(List<Obstacledge.Obstacle> obstacles, String landNumber) {
+        List<Obstacledge.Obstacle> safeList = obstacles != null ? obstacles : Collections.emptyList();
+        String normalizedLand = (landNumber != null) ? landNumber.trim() : null;
+
+        if (normalizedLand != null && !normalizedLand.isEmpty()) {
+            safeList = filterObstaclesForLand(safeList, normalizedLand);
+        }
+
+        if (normalizedLand == null || normalizedLand.isEmpty()) {
             clearObstacleData();
             if (!hasRenderableBoundary() && !hasRenderablePlannedPath()) {
                 resetView();
@@ -792,8 +950,19 @@
             return;
         }
 
-        currentObstacles = parsed;
-        obstacleBounds = convertObstacleBounds(Obstacledraw.getAllObstaclesBounds(parsed));
+        if (safeList.isEmpty()) {
+            clearObstacleData();
+            if (!hasRenderableBoundary() && !hasRenderablePlannedPath()) {
+                resetView();
+            } else {
+                visualizationPanel.repaint();
+            }
+            return;
+        }
+
+        currentObstacles = Collections.unmodifiableList(new ArrayList<>(safeList));
+        currentObstacleLandNumber = normalizedLand;
+        obstacleBounds = convertObstacleBounds(Obstacledraw.getAllObstaclesBounds(currentObstacles));
         selectedObstacleName = null;
 
         if (!hasRenderableBoundary() && !hasRenderablePlannedPath() && obstacleBounds != null) {
@@ -807,10 +976,150 @@
         }
     }
 
+    private List<Obstacledge.Obstacle> cloneObstacles(List<Obstacledge.Obstacle> obstacles, String landNumber) {
+        List<Obstacledge.Obstacle> result = new ArrayList<>();
+        if (obstacles == null || obstacles.isEmpty()) {
+            return result;
+        }
+
+        String normalizedLandNumber = landNumber != null ? landNumber.trim() : null;
+        Set<String> usedNames = new LinkedHashSet<>();
+        int fallbackIndex = 1;
+
+        for (Obstacledge.Obstacle source : obstacles) {
+            if (source == null) {
+                continue;
+            }
+
+            if (normalizedLandNumber != null && !normalizedLandNumber.isEmpty()) {
+                String sourcePlotId = source.getPlotId();
+                if (sourcePlotId != null && !sourcePlotId.trim().isEmpty()
+                        && !normalizedLandNumber.equalsIgnoreCase(sourcePlotId.trim())) {
+                    continue;
+                }
+            }
+
+            Obstacledge.ObstacleShape shape = source.getShape();
+            List<Obstacledge.XYCoordinate> xySource = source.getXyCoordinates();
+            if (shape == null || xySource == null) {
+                continue;
+            }
+
+            List<Obstacledge.XYCoordinate> xyCopy = copyXYCoordinates(xySource);
+            if (shape == Obstacledge.ObstacleShape.CIRCLE && xyCopy.size() < 2) {
+                continue;
+            }
+            if (shape == Obstacledge.ObstacleShape.POLYGON && xyCopy.size() < 3) {
+                continue;
+            }
+
+            String desiredName = source.getObstacleName();
+            if (desiredName == null || desiredName.trim().isEmpty()) {
+                desiredName = "闅滅鐗�" + fallbackIndex++;
+            }
+            String uniqueName = ensureUniqueName(usedNames, desiredName.trim());
+
+            Obstacledge.Obstacle copy = new Obstacledge.Obstacle(
+                normalizedLandNumber != null ? normalizedLandNumber : source.getPlotId(),
+                uniqueName,
+                shape
+            );
+
+            copy.setXyCoordinates(xyCopy);
+
+            List<Obstacledge.DMCoordinate> dmCopy = copyDMCoordinates(source.getOriginalCoordinates());
+            if (dmCopy.isEmpty()) {
+                populateDummyOriginalCoordinates(copy, xyCopy.size());
+            } else {
+                copy.setOriginalCoordinates(dmCopy);
+            }
+
+            result.add(copy);
+        }
+
+        return result;
+    }
+
+    private List<Obstacledge.Obstacle> filterObstaclesForLand(List<Obstacledge.Obstacle> obstacles, String landNumber) {
+        if (obstacles == null || obstacles.isEmpty()) {
+            return Collections.emptyList();
+        }
+        if (landNumber == null || landNumber.trim().isEmpty()) {
+            return Collections.emptyList();
+        }
+        String normalized = landNumber.trim();
+        List<Obstacledge.Obstacle> filtered = new ArrayList<>();
+        for (Obstacledge.Obstacle obstacle : obstacles) {
+            if (obstacle == null) {
+                continue;
+            }
+            String plotId = obstacle.getPlotId();
+            if (plotId == null || plotId.trim().isEmpty()) {
+                filtered.add(obstacle);
+                continue;
+            }
+            if (normalized.equalsIgnoreCase(plotId.trim())) {
+                filtered.add(obstacle);
+            }
+        }
+        return filtered;
+    }
+
+    private String ensureUniqueName(Set<String> usedNames, String preferredName) {
+        String base = (preferredName == null || preferredName.trim().isEmpty()) ? "闅滅鐗�" : preferredName.trim();
+        String normalized = base.toLowerCase(Locale.ROOT);
+        if (usedNames.add(normalized)) {
+            return base;
+        }
+        int suffix = 2;
+        while (true) {
+            String attempt = base + suffix;
+            String attemptKey = attempt.toLowerCase(Locale.ROOT);
+            if (usedNames.add(attemptKey)) {
+                return attempt;
+            }
+            suffix++;
+        }
+    }
+
+    private List<Obstacledge.XYCoordinate> copyXYCoordinates(List<Obstacledge.XYCoordinate> source) {
+        List<Obstacledge.XYCoordinate> copy = new ArrayList<>();
+        if (source == null) {
+            return copy;
+        }
+        for (Obstacledge.XYCoordinate coord : source) {
+            if (coord == null) {
+                continue;
+            }
+            double x = coord.getX();
+            double y = coord.getY();
+            if (!Double.isFinite(x) || !Double.isFinite(y)) {
+                continue;
+            }
+            copy.add(new Obstacledge.XYCoordinate(x, y));
+        }
+        return copy;
+    }
+
+    private List<Obstacledge.DMCoordinate> copyDMCoordinates(List<Obstacledge.DMCoordinate> source) {
+        List<Obstacledge.DMCoordinate> copy = new ArrayList<>();
+        if (source == null) {
+            return copy;
+        }
+        for (Obstacledge.DMCoordinate coord : source) {
+            if (coord == null) {
+                continue;
+            }
+            copy.add(new Obstacledge.DMCoordinate(coord.getDegreeMinute(), coord.getDirection()));
+        }
+        return copy;
+    }
+
     private void clearObstacleData() {
         currentObstacles = null;
         obstacleBounds = null;
         selectedObstacleName = null;
+        currentObstacleLandNumber = null;
     }
 
     private List<Obstacledge.Obstacle> parseObstacles(String obstaclesData, String landNumber) {

--
Gitblit v1.10.0