From 48ee74129bb09a817a0bbbabe860c4007b74c66b Mon Sep 17 00:00:00 2001
From: 826220679@qq.com <826220679@qq.com>
Date: 星期日, 21 十二月 2025 12:37:44 +0800
Subject: [PATCH] 新增了往返路径

---
 src/zhuye/Shouye.java |  225 ++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 193 insertions(+), 32 deletions(-)

diff --git a/src/zhuye/Shouye.java b/src/zhuye/Shouye.java
index 1dbc337..22f8fc6 100644
--- a/src/zhuye/Shouye.java
+++ b/src/zhuye/Shouye.java
@@ -19,23 +19,23 @@
 import gecaoji.Device;
 import gecaoji.Gecaoji;
 import gecaoji.MowerBoundaryChecker;
+import publicway.buttonset;
 import set.Sets;
 import set.debug;
 import udpdell.UDPServer;
 import zhangaiwu.AddDikuai;
-import yaokong.Control04;
 import yaokong.RemoteControlDialog;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Locale;
-import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 import java.awt.geom.Point2D;
 
+import publicway.Gpstoxuzuobiao;
+
 /**
  * 棣栭〉鐣岄潰 - 閫傞厤6.5瀵哥珫灞忥紝浣跨敤鐙珛鐨凪apRenderer杩涜缁樺埗
  */
@@ -107,12 +107,14 @@
 	// 鍦板浘娓叉煋鍣�
 	private MapRenderer mapRenderer;
 	
+	private boolean pathPreviewActive;
+	
 	private final Consumer<String> serialLineListener = line -> {
 		SwingUtilities.invokeLater(() -> {
 			updateDataPacketCountLabel();
 			// 濡傛灉鏀跺埌$GNGGA鏁版嵁锛岀珛鍗虫洿鏂版嫋灏�
 			if (line != null && line.trim().startsWith("$GNGGA")) {
-				if (mapRenderer != null) {
+				if (mapRenderer != null && !pathPreviewActive) {
 					mapRenderer.forceUpdateIdleMowerTrail();
 				}
 			}
@@ -125,7 +127,6 @@
 	private JPanel floatingButtonColumn;
 	private Runnable endDrawingCallback;
 	private JButton pathPreviewReturnButton;
-	private boolean pathPreviewActive;
 	private Runnable pathPreviewReturnAction;
 	private JButton settingsReturnButton;  // 杩斿洖绯荤粺璁剧疆椤甸潰鐨勬偓娴寜閽�
 	private JButton saveManualBoundaryButton;  // 淇濆瓨鎵嬪姩缁樺埗杈圭晫鐨勬寜閽�
@@ -170,6 +171,7 @@
 	private boolean storedStopButtonActive;
 	private String storedStatusBeforeDrawing;
 	private boolean handheldCaptureInlineUiActive;
+	private WangfanDraw returnPathDrawer;  // 寰�杩旇矾寰勭粯鍒剁鐞嗗櫒
 	private Timer handheldCaptureStatusTimer;
 	private String handheldCaptureStoredStatusText;
 	private Color handheldStartButtonOriginalBackground;
@@ -209,6 +211,54 @@
 		// 鍒濆鍖栧湴鍥炬覆鏌撳櫒
 		mapRenderer = new MapRenderer(visualizationPanel);
 		applyIdleTrailDurationFromSettings();
+		
+		// 鍒濆鍖栧線杩旇矾寰勭粯鍒剁鐞嗗櫒
+		returnPathDrawer = new WangfanDraw(this, mapRenderer, new WangfanDraw.DrawingHelper() {
+			@Override
+			public double[] resolveBaseLatLon() {
+				return resolveCircleBaseLatLon();
+			}
+			
+			@Override
+			public Coordinate getLatestCoordinate() {
+				return Shouye.this.getLatestCoordinate();
+			}
+			
+			@Override
+			public double parseDMToDecimal(String dmm, String direction) {
+				return Shouye.this.parseDMToDecimal(dmm, direction);
+			}
+			
+			@Override
+			public double[] convertLatLonToLocal(double lat, double lon, double baseLat, double baseLon) {
+				return Shouye.this.convertLatLonToLocal(lat, lon, baseLat, baseLon);
+			}
+			
+			@Override
+			public boolean arePointsClose(Point2D.Double a, Point2D.Double b) {
+				return Shouye.this.arePointsClose(a, b);
+			}
+			
+			@Override
+			public void enterDrawingControlMode() {
+				Shouye.this.enterDrawingControlMode();
+			}
+			
+			@Override
+			public void exitDrawingControlMode() {
+				Shouye.this.exitDrawingControlMode();
+			}
+			
+			@Override
+			public boolean isDrawingPaused() {
+				return drawingPaused;
+			}
+		});
+		
+		// 璁剧疆 MapRenderer 鐨勫線杩旇矾寰勭粯鍒剁鐞嗗櫒
+		if (mapRenderer != null) {
+			mapRenderer.setReturnPathDrawer(returnPathDrawer);
+		}
 
 		// 鍒濆鍖栧璇濇寮曠敤涓簄ull锛屽欢杩熷垱寤�
 		legendDialog = null;
@@ -1839,7 +1889,11 @@
 	}
 
 	private void handleDrawingStopFromControlPanel() {
-		if (endDrawingCallback != null) {
+		// 濡傛灉鏄線杩旇矾寰勭粯鍒舵ā寮忥紝璋冪敤瀹屾垚缁樺埗鍥炶皟
+		if (returnPathDrawer != null && returnPathDrawer.isActive()) {
+			returnPathDrawer.stop();
+			returnPathDrawer.executeFinishCallback();
+		} else if (endDrawingCallback != null) {
 			endDrawingCallback.run();
 		} else {
 			addzhangaiwu.finishDrawingSession();
@@ -2086,7 +2140,8 @@
 			startBtn.setText(drawingPaused ? "寮�濮嬬粯鍒�" : "鏆傚仠缁樺埗");
 		}
 		if (stopBtn != null) {
-			stopBtn.setText("缁撴潫缁樺埗");
+			// 濡傛灉鏄線杩旇矾寰勭粯鍒舵ā寮忥紝鏄剧ず"瀹屾垚缁樺埗"锛屽惁鍒欐樉绀�"缁撴潫缁樺埗"
+			stopBtn.setText((returnPathDrawer != null && returnPathDrawer.isActive()) ? "瀹屾垚缁樺埗" : "缁撴潫缁樺埗");
 		}
 	}
 
@@ -3645,34 +3700,11 @@
 	}
 
 	private double parseDMToDecimal(String dmm, String direction) {
-		if (dmm == null || dmm.trim().isEmpty()) {
-			return Double.NaN;
-		}
-		try {
-			String trimmed = dmm.trim();
-			int dotIndex = trimmed.indexOf('.');
-			if (dotIndex < 2) {
-				return Double.NaN;
-			}
-			int degrees = Integer.parseInt(trimmed.substring(0, dotIndex - 2));
-			double minutes = Double.parseDouble(trimmed.substring(dotIndex - 2));
-			double decimal = degrees + minutes / 60.0;
-			if ("S".equalsIgnoreCase(direction) || "W".equalsIgnoreCase(direction)) {
-				decimal = -decimal;
-			}
-			return decimal;
-		} catch (NumberFormatException ex) {
-			return Double.NaN;
-		}
+		return Gpstoxuzuobiao.parseDMToDecimal(dmm, direction);
 	}
 
 	private double[] convertLatLonToLocal(double lat, double lon, double baseLat, double baseLon) {
-		double deltaLat = lat - baseLat;
-		double deltaLon = lon - baseLon;
-		double meanLatRad = Math.toRadians((baseLat + lat) / 2.0);
-		double eastMeters = deltaLon * METERS_PER_DEGREE_LAT * Math.cos(meanLatRad);
-		double northMeters = deltaLat * METERS_PER_DEGREE_LAT;
-		return new double[]{eastMeters, northMeters};
+		return Gpstoxuzuobiao.convertLatLonToLocal(lat, lon, baseLat, baseLon);
 	}
 
 	private CircleSolution fitCircleFromPoints(List<double[]> points) {
@@ -4114,6 +4146,135 @@
 	}
 
     
+    /**
+     * 鍚姩寰�杩旇矾寰勭粯鍒�
+     * @param finishCallback 瀹屾垚缁樺埗鏃剁殑鍥炶皟
+     * @return 鏄惁鎴愬姛鍚姩
+     */
+    public boolean startReturnPathDrawing(Runnable finishCallback) {
+        if (returnPathDrawer == null) {
+            return false;
+        }
+        return returnPathDrawer.start(finishCallback);
+    }
+    
+    /**
+     * 鍋滄寰�杩旇矾寰勭粯鍒�
+     */
+    public void stopReturnPathDrawing() {
+        if (returnPathDrawer != null) {
+            returnPathDrawer.stop();
+        }
+    }
+    
+    /**
+     * 鑾峰彇寰�杩旇矾寰勭粯鍒剁鐞嗗櫒
+     */
+    public WangfanDraw getReturnPathDrawer() {
+        return returnPathDrawer;
+    }
+    
+    /**
+     * 鍚姩寰�杩旇矾寰勯瑙�
+     * @param coordinatesStr 璺緞鍧愭爣瀛楃涓� (x,y;x,y)
+     * @param returnCallback 杩斿洖鍥炶皟
+     */
+    public void startReturnPathPreview(String coordinatesStr, Runnable returnCallback) {
+        if (returnPathDrawer == null || coordinatesStr == null || coordinatesStr.isEmpty()) {
+            return;
+        }
+        
+        // 瑙f瀽鍧愭爣
+        List<Point2D.Double> points = new ArrayList<>();
+        String[] pairs = coordinatesStr.split(";");
+        for (String pair : pairs) {
+            String[] xy = pair.split(",");
+            if (xy.length == 2) {
+                try {
+                    double x = Double.parseDouble(xy[0]);
+                    double y = Double.parseDouble(xy[1]);
+                    points.add(new Point2D.Double(x, y));
+                } catch (NumberFormatException e) {
+                    // 蹇界暐鏃犳晥鍧愭爣
+                }
+            }
+        }
+        
+        if (points.isEmpty()) {
+            JOptionPane.showMessageDialog(this, "娌℃湁鏈夋晥鐨勮矾寰勭偣鍙瑙�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+        
+        // 璁剧疆棰勮鐐�
+        returnPathDrawer.setPoints(points);
+        if (mapRenderer != null) {
+            mapRenderer.setPreviewReturnPath(points);
+        }
+        
+        // 寮�鍚瑙堟ā寮�
+        pathPreviewActive = true;
+        if (mapRenderer != null) {
+            mapRenderer.clearIdleTrail();
+        }
+        pathPreviewReturnAction = returnCallback;
+        
+        // 纭繚鎮诞鎸夐挳鍩虹璁炬柦宸插垱寤�
+        ensureFloatingButtonInfrastructure();
+        
+        // 鍒涘缓鎴栨樉绀鸿繑鍥炴寜閽�
+        if (pathPreviewReturnButton == null) {
+            pathPreviewReturnButton = publicway.buttonset.createStyledButton("杩斿洖", null);
+            pathPreviewReturnButton.setToolTipText("杩斿洖缁樺埗椤甸潰");
+            pathPreviewReturnButton.addActionListener(e -> {
+                // 鍋滄棰勮
+                stopReturnPathPreview();
+            });
+        }
+        
+        // 闅愯棌鍏朵粬鎮诞鎸夐挳
+        hideFloatingDrawingControls();
+        
+        // 鏄剧ず杩斿洖鎸夐挳
+        pathPreviewReturnButton.setVisible(true);
+        floatingButtonPanel.setVisible(true);
+        if (floatingButtonPanel.getParent() != visualizationPanel) {
+            visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH);
+        }
+        rebuildFloatingButtonColumn();
+        
+        visualizationPanel.revalidate();
+        visualizationPanel.repaint();
+    }
+    
+    /**
+     * 鍋滄寰�杩旇矾寰勯瑙�
+     */
+    private void stopReturnPathPreview() {
+        pathPreviewActive = false;
+        
+        // 娓呯┖棰勮鐐�
+        if (returnPathDrawer != null) {
+            returnPathDrawer.clearPoints();
+        }
+        if (mapRenderer != null) {
+            mapRenderer.setPreviewReturnPath(null);
+        }
+        
+        // 闅愯棌杩斿洖鎸夐挳
+        if (pathPreviewReturnButton != null) {
+            pathPreviewReturnButton.setVisible(false);
+        }
+        
+        // 闅愯棌鎮诞闈㈡澘
+        if (floatingButtonPanel != null) {
+            floatingButtonPanel.setVisible(false);
+        }
+        
+        // 鎵ц杩斿洖鍥炶皟
+        if (pathPreviewReturnAction != null) {
+            pathPreviewReturnAction.run();
+        }
+    }
 
 	// 娴嬭瘯鏂规硶
     public static void main(String[] args) {

--
Gitblit v1.10.0