From 3ad76f98fa8b4a9d3d95207cfb4ae4706087c964 Mon Sep 17 00:00:00 2001
From: 张世豪 <979909237@qq.com>
Date: 星期四, 04 十二月 2025 19:14:15 +0800
Subject: [PATCH] 新增20251204

---
 src/zhuye/MapRenderer.java |  166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 162 insertions(+), 4 deletions(-)

diff --git a/src/zhuye/MapRenderer.java b/src/zhuye/MapRenderer.java
index 3352dc2..b5665ee 100644
--- a/src/zhuye/MapRenderer.java
+++ b/src/zhuye/MapRenderer.java
@@ -52,11 +52,13 @@
     private static final Color HANDHELD_BOUNDARY_BORDER = new Color(51, 102, 204, 220);
     private static final Color HANDHELD_BOUNDARY_POINT = new Color(51, 102, 204);
     private static final Color HANDHELD_BOUNDARY_LABEL = new Color(22, 62, 138);
+    private static final double BOUNDARY_CONTAINS_TOLERANCE = 0.05;
     
     // 缁勪欢寮曠敤
     private JPanel visualizationPanel;
     private List<Point2D.Double> currentBoundary;
     private Rectangle2D.Double boundaryBounds;
+    private Path2D.Double currentBoundaryPath;
     private List<Point2D.Double> currentPlannedPath;
     private Rectangle2D.Double plannedPathBounds;
     private List<Obstacledge.Obstacle> currentObstacles;
@@ -94,6 +96,7 @@
     private long lastTrackPersistTimeMillis;
     private boolean trackDirty;
     private boolean handheldBoundaryPreviewActive;
+    private boolean pendingTrackBreak = true;
 
     private static final double TRACK_SAMPLE_MIN_DISTANCE_METERS = 0.1d;
     private static final long TRACK_PERSIST_INTERVAL_MS = 5_000L;
@@ -432,15 +435,39 @@
     }
 
     private void captureRealtimeTrackPoint() {
+        if (!realtimeTrackRecording) {
+            return;
+        }
         if (realtimeTrackLandNumber == null || visualizationPanel == null) {
+            pendingTrackBreak = true;
+            return;
+        }
+        Device device = Device.getGecaoji();
+        if (device == null) {
+            pendingTrackBreak = true;
+            return;
+        }
+
+        String fixQuality = device.getPositioningStatus();
+        if (!isHighPrecisionFix(fixQuality)) {
+            pendingTrackBreak = true;
             return;
         }
         Point2D.Double position = mower.getPosition();
         if (position == null || !Double.isFinite(position.x) || !Double.isFinite(position.y)) {
+            pendingTrackBreak = true;
+            return;
+        }
+
+        if (!isPointInsideActiveBoundary(position)) {
+            pendingTrackBreak = true;
             return;
         }
 
         Point2D.Double lastPoint = realtimeMowingTrack.isEmpty() ? null : realtimeMowingTrack.get(realtimeMowingTrack.size() - 1);
+        if (pendingTrackBreak) {
+            lastPoint = null;
+        }
         double distance = 0.0;
         if (lastPoint != null) {
             double dx = position.x - lastPoint.x;
@@ -459,6 +486,7 @@
         updateCompletionMetrics();
         trackDirty = true;
         maybePersistRealtimeTrack(false);
+        pendingTrackBreak = false;
     }
 
     private void updateCompletionMetrics() {
@@ -575,16 +603,19 @@
 
         realtimeTrackLandNumber = normalizedLand;
         realtimeTrackRecording = true;
+        pendingTrackBreak = true;
         captureRealtimeTrackPoint();
     }
 
     public void pauseRealtimeTrackRecording() {
         realtimeTrackRecording = false;
+        pendingTrackBreak = true;
         maybePersistRealtimeTrack(true);
     }
 
     public void stopRealtimeTrackRecording() {
         realtimeTrackRecording = false;
+        pendingTrackBreak = true;
         maybePersistRealtimeTrack(true);
     }
 
@@ -602,15 +633,22 @@
         completedMowingAreaSqMeters = 0.0;
         mowingCompletionRatio = 0.0;
         trackDirty = true;
+        pendingTrackBreak = true;
         maybePersistRealtimeTrack(true);
         visualizationPanel.repaint();
     }
 
     public double getMowingCompletionRatio() {
+        if (!isMowerInsideSelectedBoundary()) {
+            return 0.0;
+        }
         return mowingCompletionRatio;
     }
 
     public double getCompletedMowingAreaSqMeters() {
+        if (!isMowerInsideSelectedBoundary()) {
+            return 0.0;
+        }
         return completedMowingAreaSqMeters;
     }
 
@@ -622,6 +660,14 @@
         return trackLengthMeters;
     }
 
+    private boolean isMowerInsideSelectedBoundary() {
+        Point2D.Double position = mower.getPosition();
+        if (position == null) {
+            return false;
+        }
+        return isPointInsideActiveBoundary(position);
+    }
+
     public void flushRealtimeTrack() {
         maybePersistRealtimeTrack(true);
     }
@@ -635,6 +681,7 @@
         mowingCompletionRatio = 0.0;
         trackDirty = false;
         lastTrackPersistTimeMillis = 0L;
+        pendingTrackBreak = true;
 
         String trimmed = normalizeValue(trackData);
         if (trimmed == null || trimmed.isEmpty()) {
@@ -1071,7 +1118,7 @@
         mowerNumberValueLabel.setText(formatDeviceValue(device.getMowerNumber()));
         realtimeXValueLabel.setText(formatDeviceValue(device.getRealtimeX()));
         realtimeYValueLabel.setText(formatDeviceValue(device.getRealtimeY()));
-        positioningStatusValueLabel.setText(formatDeviceValue(device.getPositioningStatus()));
+    positioningStatusValueLabel.setText(formatFixQualityValue(device.getPositioningStatus()));
         satelliteCountValueLabel.setText(formatDeviceValue(device.getSatelliteCount()));
         realtimeSpeedValueLabel.setText(formatDeviceValue(device.getRealtimeSpeed()));
         headingValueLabel.setText(formatDeviceValue(device.getHeading()));
@@ -1082,7 +1129,7 @@
         if (mowerNumberValueLabel != null) mowerNumberValueLabel.setText(value);
         if (realtimeXValueLabel != null) realtimeXValueLabel.setText(value);
         if (realtimeYValueLabel != null) realtimeYValueLabel.setText(value);
-        if (positioningStatusValueLabel != null) positioningStatusValueLabel.setText(value);
+    if (positioningStatusValueLabel != null) positioningStatusValueLabel.setText(value);
         if (satelliteCountValueLabel != null) satelliteCountValueLabel.setText(value);
         if (realtimeSpeedValueLabel != null) realtimeSpeedValueLabel.setText(value);
         if (headingValueLabel != null) headingValueLabel.setText(value);
@@ -1105,6 +1152,37 @@
         return sanitized == null ? "--" : sanitized;
     }
 
+    private String formatFixQualityValue(String value) {
+        String sanitized = sanitizeDeviceValue(value);
+        if (sanitized == null) {
+            return "--";
+        }
+        switch (sanitized) {
+            case "0":
+                return "鏈畾浣�";
+            case "1":
+                return "鍗曠偣瀹氫綅";
+            case "2":
+                return "鐮佸樊鍒�";
+            case "3":
+                return "鏃犳晥PPS";
+            case "4":
+                return "鍥哄畾瑙�";
+            case "5":
+                return "娴偣瑙�";
+            case "6":
+                return "姝e湪浼扮畻";
+            case "7":
+                return "浜哄伐杈撳叆鍥哄畾鍊�";
+            case "8":
+                return "妯℃嫙妯″紡";
+            case "9":
+                return "WAAS宸垎";
+            default:
+                return sanitized;
+        }
+    }
+
     private String formatTimestamp(String value) {
         String sanitized = sanitizeDeviceValue(value);
         if (sanitized == null) {
@@ -1207,6 +1285,7 @@
 
         if (updated.size() < 2) {
             currentBoundary = null;
+            currentBoundaryPath = null;
             boundaryBounds = null;
             boundaryPointsVisible = false;
             Dikuaiguanli.updateBoundaryPointVisibility(currentBoundaryLandNumber, false);
@@ -1214,10 +1293,12 @@
             adjustViewAfterBoundaryReset();
         } else {
             currentBoundary = updated;
+            rebuildBoundaryPath();
             boundaryBounds = computeBounds(updated);
             Dikuaiguanli.updateBoundaryPointVisibility(currentBoundaryLandNumber, boundaryPointsVisible);
             visualizationPanel.repaint();
         }
+        pendingTrackBreak = true;
     }
 
     private boolean persistBoundaryChanges(List<Point2D.Double> updatedBoundary) {
@@ -1279,6 +1360,79 @@
         return Math.hypot(dx, dy) <= BOUNDARY_POINT_MERGE_THRESHOLD;
     }
 
+    private boolean isHighPrecisionFix(String fixQuality) {
+        if (fixQuality == null) {
+            return false;
+        }
+        String trimmed = fixQuality.trim();
+        if (trimmed.isEmpty()) {
+            return false;
+        }
+        if ("4".equals(trimmed)) {
+            return true;
+        }
+        try {
+            double value = Double.parseDouble(trimmed);
+            return Math.abs(value - 4.0d) < 1e-6;
+        } catch (NumberFormatException ex) {
+            return false;
+        }
+    }
+
+    private boolean isPointInsideActiveBoundary(Point2D.Double point) {
+        if (point == null || !Double.isFinite(point.x) || !Double.isFinite(point.y)) {
+            return false;
+        }
+        if (realtimeTrackLandNumber == null) {
+            return false;
+        }
+        if (currentBoundaryLandNumber != null && !currentBoundaryLandNumber.equals(realtimeTrackLandNumber)) {
+            return false;
+        }
+
+        Path2D.Double path = currentBoundaryPath;
+        if (path == null) {
+            path = buildBoundaryPath(currentBoundary);
+            currentBoundaryPath = path;
+        }
+        if (path == null) {
+            return false;
+        }
+        if (path.contains(point.x, point.y)) {
+            return true;
+        }
+        double size = BOUNDARY_CONTAINS_TOLERANCE * 2.0;
+        return path.intersects(point.x - BOUNDARY_CONTAINS_TOLERANCE, point.y - BOUNDARY_CONTAINS_TOLERANCE, size, size);
+    }
+
+    private void rebuildBoundaryPath() {
+        currentBoundaryPath = buildBoundaryPath(currentBoundary);
+    }
+
+    private Path2D.Double buildBoundaryPath(List<Point2D.Double> boundary) {
+        if (boundary == null || boundary.size() < 3) {
+            return null;
+        }
+        Path2D.Double path = new Path2D.Double();
+        boolean started = false;
+        for (Point2D.Double point : boundary) {
+            if (point == null || !Double.isFinite(point.x) || !Double.isFinite(point.y)) {
+                continue;
+            }
+            if (!started) {
+                path.moveTo(point.x, point.y);
+                started = true;
+            } else {
+                path.lineTo(point.x, point.y);
+            }
+        }
+        if (!started) {
+            return null;
+        }
+        path.closePath();
+        return path;
+    }
+
     
     /**
      * 缁樺埗瑙嗗浘淇℃伅
@@ -1360,8 +1514,10 @@
             return;
         }
 
-        currentBoundary = parsed;
-        boundaryBounds = computeBounds(parsed);
+    currentBoundary = parsed;
+    rebuildBoundaryPath();
+    pendingTrackBreak = true;
+    boundaryBounds = computeBounds(parsed);
 
         Rectangle2D.Double bounds = boundaryBounds;
         SwingUtilities.invokeLater(() -> {
@@ -1372,10 +1528,12 @@
 
     private void clearBoundaryData() {
         currentBoundary = null;
+        currentBoundaryPath = null;
         boundaryBounds = null;
         boundaryName = null;
         boundaryPointsVisible = false;
         currentBoundaryLandNumber = null;
+        pendingTrackBreak = true;
     }
 
     public void setCurrentObstacles(String obstaclesData, String landNumber) {

--
Gitblit v1.10.0