From c498385fb7e372d13e2ee76d7b54ae2381728082 Mon Sep 17 00:00:00 2001
From: 张世豪 <979909237@qq.com>
Date: 星期三, 17 十二月 2025 19:35:57 +0800
Subject: [PATCH] 新增了测量模式

---
 src/zhuye/Shouye.java | 4799 ++++++++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 3,719 insertions(+), 1,080 deletions(-)

diff --git a/src/zhuye/Shouye.java b/src/zhuye/Shouye.java
index 14565cb..30c1444 100644
--- a/src/zhuye/Shouye.java
+++ b/src/zhuye/Shouye.java
@@ -1,1107 +1,3746 @@
 package zhuye;
 
 import javax.swing.*;
+import javax.swing.Timer;
 
 import baseStation.BaseStation;
+import set.Setsys;
 import baseStation.BaseStationDialog;
 
 import java.awt.*;
 import java.awt.event.*;
 
+import chuankou.dellmessage;
+import chuankou.sendmessage;
+import chuankou.SerialPortService;
 import dikuai.Dikuai;
 import dikuai.Dikuaiguanli;
 import dikuai.addzhangaiwu;
 import gecaoji.Device;
+import gecaoji.Gecaoji;
+import gecaoji.MowerBoundaryChecker;
 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;
 
 /**
  * 棣栭〉鐣岄潰 - 閫傞厤6.5瀵哥珫灞忥紝浣跨敤鐙珛鐨凪apRenderer杩涜缁樺埗
  */
 public class Shouye extends JPanel {
-    private static final long serialVersionUID = 1L;
-    private static Shouye instance;
+	private static final long serialVersionUID = 1L;
+	private static Shouye instance;
+
+	// 涓婚棰滆壊
+	private final Color THEME_COLOR = new Color(46, 139, 87);
+	private final Color THEME_HOVER_COLOR = new Color(30, 107, 69);
+	private final Color BACKGROUND_COLOR = new Color(250, 250, 250);
+	private final Color PANEL_BACKGROUND = new Color(255, 255, 255);
+	private final Color STATUS_PAUSE_COLOR = new Color(255, 165, 0);
+
+	// 缁勪欢
+	private JPanel headerPanel;
+	private JPanel mainContentPanel;
+	private JPanel controlPanel;
+	private JPanel navigationPanel;
+	private JPanel visualizationPanel;
+
+	// 鎸夐挳
+	private JButton startBtn;
+	private JButton stopBtn;
+	private JButton legendBtn;
+	private JButton remoteBtn;
+	private JButton areaSelectBtn;
+	private JButton baseStationBtn;
+	private JButton bluetoothBtn;
+	private JLabel dataPacketCountLabel;
+	private JLabel mowerSpeedValueLabel;
+	private JLabel mowerSpeedUnitLabel;
+	private JLabel mowingProgressLabel;
+	private gpszhuangtai fixQualityIndicator;
+
+	// 瀵艰埅鎸夐挳
+	private JButton homeNavBtn;
+	private JButton areasNavBtn;
+	private JButton settingsNavBtn;
+
+	// 鐘舵�佹樉绀�
+	private JLabel statusLabel;
+	private JLabel speedLabel;  // 閫熷害鏄剧ず鏍囩
+	private JLabel areaNameLabel;
+	
+	// 杈圭晫璀﹀憡鐩稿叧
+	private Timer boundaryWarningTimer;  // 杈圭晫璀﹀憡妫�鏌ュ畾鏃跺櫒
+	private Timer warningBlinkTimer;  // 璀﹀憡鍥炬爣闂儊瀹氭椂鍣�
+	private boolean isMowerOutsideBoundary = false;  // 鍓茶崏鏈烘槸鍚﹀湪杈圭晫澶�
+	private boolean warningIconVisible = true;  // 璀﹀憡鍥炬爣鏄剧ず鐘舵�侊紙鐢ㄤ簬闂儊锛�
+	
+	// 浠ュ壊鑽夋満涓轰腑蹇冭鍥炬ā寮�
+	private boolean centerOnMowerMode = false;  // 鏄惁澶勪簬浠ュ壊鑽夋満涓轰腑蹇冪殑妯″紡
+
+	// 褰撳墠閫変腑鐨勫鑸寜閽�
+	private JButton currentNavButton;
+
+	// 瀵硅瘽妗嗗紩鐢�
+	private LegendDialog legendDialog;
+	// 浣跨敤瀹屽叏闄愬畾鍚嶄互閬垮厤涓庡悓鍖呯殑 RemoteControlDialog 鍐茬獊锛岀‘淇濅娇鐢� yaokong 鍖呬笅鐨勫疄鐜�
+	private yaokong.RemoteControlDialog remoteDialog;
+	private AreaSelectionDialog areaDialog;
+	private BaseStationDialog baseStationDialog;
+	private Sets settingsDialog;
+	private BaseStation baseStation;
+
+	// 鍦板浘娓叉煋鍣�
+	private MapRenderer mapRenderer;
+	
+	private final Consumer<String> serialLineListener = line -> {
+		SwingUtilities.invokeLater(() -> {
+			updateDataPacketCountLabel();
+			// 濡傛灉鏀跺埌$GNGGA鏁版嵁锛岀珛鍗虫洿鏂版嫋灏�
+			if (line != null && line.trim().startsWith("$GNGGA")) {
+				if (mapRenderer != null) {
+					mapRenderer.forceUpdateIdleMowerTrail();
+				}
+			}
+		});
+	};
+	private static final int FLOAT_ICON_SIZE = 32;
+	private JButton endDrawingButton;
+	private JButton drawingPauseButton;
+	private JPanel floatingButtonPanel;
+	private JPanel floatingButtonColumn;
+	private Runnable endDrawingCallback;
+	private JButton pathPreviewReturnButton;
+	private boolean pathPreviewActive;
+	private Runnable pathPreviewReturnAction;
+	private String previewRestoreLandNumber;
+	private String previewRestoreLandName;
+	private boolean drawingPaused;
+	private ImageIcon pauseIcon;
+	private ImageIcon pauseActiveIcon;
+	private ImageIcon endIcon;
+	private ImageIcon bluetoothIcon;
+	private ImageIcon bluetoothLinkedIcon;
+	private JPanel circleGuidancePanel;
+	private JLabel circleGuidanceLabel;
+	private JButton circleGuidancePrimaryButton;
+	private JButton circleGuidanceSecondaryButton;
+	private int circleGuidanceStep;
+	private JDialog circleGuidanceDialog;
+	private boolean circleDialogMode;
+	private ComponentAdapter circleDialogOwnerAdapter;
+	private static final double METERS_PER_DEGREE_LAT = 111320.0d;
+	private static final double HANDHELD_DUPLICATE_THRESHOLD_METERS = 0.01d;
+	private final List<double[]> circleCapturedPoints = new ArrayList<>();
+	private double[] circleBaseLatLon;
+	private Timer circleDataMonitor;
+	private Coordinate lastCapturedCoordinate;
+	private boolean handheldCaptureActive;
+	private int handheldCapturedPoints;
+	private final List<Point2D.Double> handheldTemporaryPoints = new ArrayList<>();
+	private final List<Point2D.Double> mowerTemporaryPoints = new ArrayList<>();
+	private enum BoundaryCaptureMode { NONE, HANDHELD, MOWER }
+	private BoundaryCaptureMode activeBoundaryMode = BoundaryCaptureMode.NONE;
+	private boolean mowerBoundaryCaptureActive;
+	private Timer mowerBoundaryMonitor;
+	private Coordinate lastMowerCoordinate;
+	private double[] mowerBaseLatLon;
+	private boolean startButtonShowingPause = true;
+	private boolean stopButtonActive = false;
+	private boolean bluetoothConnected = false;
+	private Timer mowerSpeedRefreshTimer;
+	private boolean drawingControlModeActive;
+	private boolean storedStartButtonShowingPause;
+	private boolean storedStopButtonActive;
+	private String storedStatusBeforeDrawing;
+	private boolean handheldCaptureInlineUiActive;
+	private Timer handheldCaptureStatusTimer;
+	private String handheldCaptureStoredStatusText;
+	private Color handheldStartButtonOriginalBackground;
+	private Color handheldStartButtonOriginalForeground;
+	private Color handheldStopButtonOriginalBackground;
+	private Color handheldStopButtonOriginalForeground;
+
+	public Shouye() {
+		instance = this;
+		baseStation = new BaseStation();
+		baseStation.load();
+		dellmessage.registerLineListener(serialLineListener);
+		initializeUI();
+		setupEventHandlers();
+		scheduleIdentifierCheck();
+	}
+
+	public static Shouye getInstance() {
+		return instance;
+	}
+
+	private void initializeUI() {
+		setLayout(new BorderLayout());
+		setBackground(BACKGROUND_COLOR);
+
+		// 鍒涘缓鍚勪釜闈㈡澘
+		createHeaderPanel();
+		createMainContentPanel();
+		createControlPanel();
+		createNavigationPanel();
+
+		// 娣诲姞鍒颁富闈㈡澘
+		add(headerPanel, BorderLayout.NORTH);
+		add(mainContentPanel, BorderLayout.CENTER);
+		add(controlPanel, BorderLayout.SOUTH);
+
+		// 鍒濆鍖栧湴鍥炬覆鏌撳櫒
+		mapRenderer = new MapRenderer(visualizationPanel);
+		applyIdleTrailDurationFromSettings();
+
+		// 鍒濆鍖栧璇濇寮曠敤涓簄ull锛屽欢杩熷垱寤�
+		legendDialog = null;
+		remoteDialog = null;
+		areaDialog = null;
+		baseStationDialog = null;
+		settingsDialog = null;
+
+		// 璁剧疆榛樿鐘舵��
+		setNavigationActive(homeNavBtn);
+
+		initializeDefaultAreaSelection();
+		refreshMapForSelectedArea();
+		
+		// 鍚姩杈圭晫璀﹀憡妫�鏌ュ畾鏃跺櫒
+		startBoundaryWarningTimer();
+	}
+
+	private void scheduleIdentifierCheck() {
+		HierarchyListener listener = new HierarchyListener() {
+			@Override
+			public void hierarchyChanged(HierarchyEvent e) {
+				if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 && Shouye.this.isShowing()) {
+					Shouye.this.removeHierarchyListener(this);
+					SwingUtilities.invokeLater(() -> {
+						Shouye.this.checkIdentifiersAndPromptIfNeeded();
+						Shouye.this.showInitialMowerSelfCheckDialogIfNeeded();
+						// 璁剧疆绐楀彛鍏抽棴鐩戝惉鍣紝鍦ㄥ叧闂椂淇濆瓨缂╂斁姣斾緥
+						setupWindowCloseListener();
+					});
+				}
+			}
+		};
+		addHierarchyListener(listener);
+	}
+	
+	/**
+	 * 璁剧疆绐楀彛鍏抽棴鐩戝惉鍣紝鍦ㄧ獥鍙e叧闂椂淇濆瓨褰撳墠缂╂斁姣斾緥
+	 */
+	private void setupWindowCloseListener() {
+		Window window = SwingUtilities.getWindowAncestor(this);
+		if (window != null && window instanceof JFrame) {
+			JFrame frame = (JFrame) window;
+			frame.addWindowListener(new WindowAdapter() {
+				@Override
+				public void windowClosing(WindowEvent e) {
+					// 淇濆瓨褰撳墠缂╂斁姣斾緥
+					saveCurrentScale();
+					// 鍋滄杈圭晫璀﹀憡瀹氭椂鍣�
+					if (boundaryWarningTimer != null && boundaryWarningTimer.isRunning()) {
+						boundaryWarningTimer.stop();
+					}
+					// 鍋滄闂儊瀹氭椂鍣�
+					if (warningBlinkTimer != null && warningBlinkTimer.isRunning()) {
+						warningBlinkTimer.stop();
+					}
+				}
+			});
+		}
+	}
+	
+	/**
+	 * 淇濆瓨褰撳墠鍦板浘缂╂斁姣斾緥鍜岃鍥句腑蹇冨潗鏍囧埌閰嶇疆鏂囦欢
+	 */
+	public void saveCurrentScale() {
+		if (mapRenderer != null) {
+			double currentScale = mapRenderer.getScale();
+			double translateX = mapRenderer.getTranslateX();
+			double translateY = mapRenderer.getTranslateY();
+			Setsys setsys = new Setsys();
+			// 淇濈暀2浣嶅皬鏁�
+			setsys.updateProperty("mapScale", String.format("%.2f", currentScale));
+			setsys.updateProperty("viewCenterX", String.format("%.2f", translateX));
+			setsys.updateProperty("viewCenterY", String.format("%.2f", translateY));
+		}
+	}
+	
+	/**
+	 * 鍚姩杈圭晫璀﹀憡妫�鏌ュ畾鏃跺櫒
+	 */
+	private void startBoundaryWarningTimer() {
+		// 杈圭晫妫�鏌ュ畾鏃跺櫒锛氭瘡500ms妫�鏌ヤ竴娆″壊鑽夋満鏄惁鍦ㄨ竟鐣屽唴
+		boundaryWarningTimer = new Timer(500, e -> {
+			checkMowerBoundaryStatus();
+			// 鍚屾椂鏇存柊钃濈墮鍥炬爣鐘舵��
+			updateBluetoothButtonIcon();
+		});
+		boundaryWarningTimer.setInitialDelay(0);
+		boundaryWarningTimer.start();
+		
+		// 闂儊瀹氭椂鍣細姣�1绉掑垏鎹竴娆¤鍛婂浘鏍囨樉绀虹姸鎬�
+		warningBlinkTimer = new Timer(1000, e -> {
+			if (isMowerOutsideBoundary) {
+				warningIconVisible = !warningIconVisible;
+				if (visualizationPanel != null) {
+					visualizationPanel.repaint();
+				}
+			}
+		});
+		warningBlinkTimer.setInitialDelay(0);
+		warningBlinkTimer.start();
+	}
+	
+	/**
+	 * 鍒囨崲浠ュ壊鑽夋満涓轰腑蹇冪殑妯″紡
+	 */
+	private void toggleCenterOnMowerMode() {
+		centerOnMowerMode = !centerOnMowerMode;
+		
+		if (centerOnMowerMode) {
+			// 寮�鍚ā寮忥細绔嬪嵆灏嗚鍥句腑蹇冪Щ鍔ㄥ埌鍓茶崏鏈轰綅缃�
+			updateViewToCenterOnMower();
+		}
+		// 鍏抽棴妯″紡鏃朵笉闇�瑕佸仛浠讳綍鎿嶄綔锛岀敤鎴峰凡缁忓彲浠ヨ嚜鐢辩Щ鍔ㄥ湴鍥�
+		
+		// 鏇存柊鍥炬爣鏄剧ず锛堥噸缁樹互鍒囨崲鍥炬爣锛�
+		if (visualizationPanel != null) {
+			visualizationPanel.repaint();
+		}
+	}
+	
+	/**
+	 * 鏇存柊瑙嗗浘涓績鍒板壊鑽夋満浣嶇疆
+	 */
+	private void updateViewToCenterOnMower() {
+		if (mapRenderer == null) {
+			return;
+		}
+		
+		Gecaoji mower = mapRenderer.getMower();
+		if (mower != null && mower.hasValidPosition()) {
+			Point2D.Double mowerPosition = mower.getPosition();
+			if (mowerPosition != null) {
+				// 鑾峰彇褰撳墠缂╂斁姣斾緥
+				double currentScale = mapRenderer.getScale();
+				// 璁剧疆瑙嗗浘鍙樻崲锛屼娇鍓茶崏鏈轰綅缃搴斿埌灞忓箷涓績
+				// translateX = -mowerX, translateY = -mowerY 鍙互璁╁壊鑽夋満鍦ㄥ睆骞曚腑蹇�
+				mapRenderer.setViewTransform(currentScale, -mowerPosition.x, -mowerPosition.y);
+			}
+		}
+	}
+	
+	/**
+	 * 妫�鏌ュ壊鑽夋満杈圭晫鐘舵��
+	 */
+	private void checkMowerBoundaryStatus() {
+		// 濡傛灉澶勪簬浠ュ壊鑽夋満涓轰腑蹇冪殑妯″紡锛屽疄鏃舵洿鏂拌鍥句腑蹇�
+		if (centerOnMowerMode) {
+			updateViewToCenterOnMower();
+		}
+		
+		// 妫�鏌ユ槸鍚﹀湪浣滀笟涓�
+		if (statusLabel == null || !"浣滀笟涓�".equals(statusLabel.getText())) {
+			// 涓嶅湪浣滀笟涓紝閲嶇疆鐘舵��
+			if (isMowerOutsideBoundary) {
+				isMowerOutsideBoundary = false;
+				warningIconVisible = true;
+				if (visualizationPanel != null) {
+					visualizationPanel.repaint();
+				}
+			}
+			return;
+		}
+		
+		// 鍦ㄤ綔涓氫腑锛屾鏌ユ槸鍚﹀湪杈圭晫鍐�
+		if (mapRenderer == null) {
+			return;
+		}
+		
+		// 鑾峰彇褰撳墠杈圭晫
+		List<Point2D.Double> boundary = mapRenderer.getCurrentBoundary();
+		if (boundary == null || boundary.size() < 3) {
+			// 娌℃湁杈圭晫锛岄噸缃姸鎬�
+			if (isMowerOutsideBoundary) {
+				isMowerOutsideBoundary = false;
+				warningIconVisible = true;
+				if (visualizationPanel != null) {
+					visualizationPanel.repaint();
+				}
+			}
+			return;
+		}
+		
+		// 鑾峰彇鍓茶崏鏈轰綅缃�
+		Gecaoji mower = mapRenderer.getMower();
+		if (mower == null || !mower.hasValidPosition()) {
+			// 鏃犳硶鑾峰彇浣嶇疆锛岄噸缃姸鎬�
+			if (isMowerOutsideBoundary) {
+				isMowerOutsideBoundary = false;
+				warningIconVisible = true;
+				if (visualizationPanel != null) {
+					visualizationPanel.repaint();
+				}
+			}
+			return;
+		}
+		
+		Point2D.Double mowerPosition = mower.getPosition();
+		if (mowerPosition == null) {
+			return;
+		}
+		
+		// 浣跨敤 MowerBoundaryChecker 妫�鏌ユ槸鍚﹀湪杈圭晫鍐�
+		boolean isInside = MowerBoundaryChecker.isInsideBoundaryPoints(
+			boundary, 
+			mowerPosition.x, 
+			mowerPosition.y
+		);
+		
+		// 鏇存柊鐘舵��
+		boolean wasOutside = isMowerOutsideBoundary;
+		isMowerOutsideBoundary = !isInside;
+		
+		// 濡傛灉鐘舵�佹敼鍙橈紝绔嬪嵆閲嶇粯
+		if (wasOutside != isMowerOutsideBoundary) {
+			warningIconVisible = true;
+			if (visualizationPanel != null) {
+				visualizationPanel.repaint();
+			}
+		}
+	}
+
+	private void showInitialMowerSelfCheckDialogIfNeeded() {
+		// 宸茬Щ闄よ繘鍏ヤ富椤垫椂鐨勮嚜妫�鎻愮ず锛堟寜鐢ㄦ埛瑕佹眰鍒犻櫎锛�
+		// 浠ュ墠杩欓噷浼氳皟鐢� zijian.showInitialPromptIfNeeded(...) 灞曠ず鑷瀵硅瘽妗嗭紝鐜板凡绂佺敤銆�
+	}
+
+	private void applyIdleTrailDurationFromSettings() {
+		if (mapRenderer == null) {
+			return;
+		}
+		int durationSeconds = MapRenderer.DEFAULT_IDLE_TRAIL_DURATION_SECONDS;
+		String configuredValue = Setsys.getPropertyValue("idleTrailDurationSeconds");
+		if (configuredValue != null) {
+			String trimmed = configuredValue.trim();
+			if (!trimmed.isEmpty()) {
+				try {
+					int parsed = Integer.parseInt(trimmed);
+					if (parsed >= 5 && parsed <= 600) {
+						durationSeconds = parsed;
+					}
+				} catch (NumberFormatException ignored) {
+					durationSeconds = MapRenderer.DEFAULT_IDLE_TRAIL_DURATION_SECONDS;
+				}
+			}
+		}
+		mapRenderer.setIdleTrailDurationSeconds(durationSeconds);
+		
+		// 搴旂敤杈圭晫璺濈鏄剧ず璁剧疆鍜屾祴閲忔ā寮忚缃�
+		Setsys setsys = new Setsys();
+		setsys.initializeFromProperties();
+		mapRenderer.setBoundaryLengthVisible(setsys.isBoundaryLengthVisible());
+		// 鍒濆鍖栨祴閲忔ā寮�
+		boolean measurementEnabled = setsys.isMeasurementModeEnabled();
+		mapRenderer.setMeasurementMode(measurementEnabled);
+		if (measurementEnabled) {
+			celiangmoshi.start();
+		} else {
+			celiangmoshi.stop();
+		}
+	}
+
+	private void createHeaderPanel() {
+		headerPanel = new JPanel(new BorderLayout());
+		headerPanel.setBackground(PANEL_BACKGROUND);
+		headerPanel.setBorder(BorderFactory.createEmptyBorder(15, 20, 15, 20));
+		headerPanel.setPreferredSize(new Dimension(0, 80));
+
+	// 宸︿晶淇℃伅鍖哄煙锛堝瀭鐩存帓鍒楋細鍦板潡鍚嶇О鍦ㄤ笂锛岀姸鎬佽鍦ㄤ笅锛�
+	JPanel leftInfoPanel = new JPanel();
+	leftInfoPanel.setBackground(PANEL_BACKGROUND);
+	leftInfoPanel.setLayout(new BoxLayout(leftInfoPanel, BoxLayout.Y_AXIS));
+	// 淇濊瘉瀛愮粍浠跺乏瀵归綈
+	leftInfoPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+	areaNameLabel = new JLabel("鏈�夋嫨鍦板潡");
+		areaNameLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 18));
+		areaNameLabel.setForeground(Color.BLACK);
+		areaNameLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+		areaNameLabel.addMouseListener(new MouseAdapter() {
+			@Override
+			public void mouseClicked(MouseEvent e) {
+				Dikuaiguanli.showDikuaiManagement(Shouye.this, null);
+			}
+
+			@Override
+			public void mouseEntered(MouseEvent e) {
+				areaNameLabel.setForeground(THEME_COLOR);
+			}
+
+			@Override
+			public void mouseExited(MouseEvent e) {
+				areaNameLabel.setForeground(Color.BLACK);
+			}
+		});
+
+		statusLabel = new JLabel("寰呮満");
+		statusLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+		statusLabel.setForeground(Color.GRAY);
+		statusLabel.addPropertyChangeListener("text", evt -> {
+			Object newValue = evt.getNewValue();
+			applyStatusLabelColor(newValue instanceof String ? (String) newValue : null);
+		});
+		applyStatusLabelColor(statusLabel.getText());
+
+		// 娣诲姞閫熷害鏄剧ず鏍囩
+		speedLabel = new JLabel("");
+		speedLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+		speedLabel.setForeground(Color.GRAY);
+		speedLabel.setVisible(false);  // 榛樿闅愯棌
+
+	// 灏嗙姸鎬佷笌閫熷害鏀惧湪鍚屼竴琛岋紝鏄剧ず鍦ㄥ湴鍧楀悕绉颁笅闈竴琛�
+	JPanel statusRow = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 0));
+	statusRow.setOpaque(false);
+	statusRow.add(statusLabel);
+	statusRow.add(speedLabel);
+
+	// 宸﹀榻愭爣绛句笌鐘舵�佽锛岀‘淇濆畠浠湪 BoxLayout 涓潬宸︽樉绀�
+	areaNameLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+	statusRow.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+	leftInfoPanel.add(areaNameLabel);
+	leftInfoPanel.add(statusRow);
+
+		// 鍙充晶鎿嶄綔鍖哄煙
+		JPanel rightActionPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+		rightActionPanel.setBackground(PANEL_BACKGROUND);
+
+		bluetoothBtn = createBluetoothButton();
+
+		// 淇敼璁剧疆鎸夐挳锛氫娇鐢ㄥ浘鐗囨浛浠nicode鍥炬爣
+		JButton settingsBtn = new JButton();
+		try {
+			ImageIcon settingsIcon = new ImageIcon("image/sets.png");
+			// 璋冩暣鍥剧墖澶у皬浠ラ�傚簲鎸夐挳
+			Image scaledImage = settingsIcon.getImage().getScaledInstance(30, 30, Image.SCALE_SMOOTH);
+			settingsBtn.setIcon(new ImageIcon(scaledImage));
+		} catch (Exception e) {
+			// 濡傛灉鍥剧墖鍔犺浇澶辫触锛屼娇鐢ㄩ粯璁ゆ枃鏈�
+			settingsBtn.setText("璁剧疆");
+			System.err.println("鏃犳硶鍔犺浇璁剧疆鍥炬爣: " + e.getMessage());
+		}
+		settingsBtn.setPreferredSize(new Dimension(40, 40));
+		settingsBtn.setBackground(PANEL_BACKGROUND);
+		settingsBtn.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+		settingsBtn.setFocusPainted(false);
+
+		// 娣诲姞鎮仠鏁堟灉
+		settingsBtn.addMouseListener(new MouseAdapter() {
+			public void mouseEntered(MouseEvent e) {
+				settingsBtn.setBackground(new Color(240, 240, 240));
+			}
+			public void mouseExited(MouseEvent e) {
+				settingsBtn.setBackground(PANEL_BACKGROUND);
+			}
+		});
+
+		// 娣诲姞璁剧疆鎸夐挳浜嬩欢
+		settingsBtn.addActionListener(e -> showSettingsDialog());
+
+		rightActionPanel.add(bluetoothBtn);
+		rightActionPanel.add(settingsBtn);
+
+		headerPanel.add(leftInfoPanel, BorderLayout.WEST);
+		headerPanel.add(rightActionPanel, BorderLayout.EAST);
+	}
+
+	private void createMainContentPanel() {
+		mainContentPanel = new JPanel(new BorderLayout());
+		mainContentPanel.setBackground(BACKGROUND_COLOR);
+
+		// 鍙鍖栧尯鍩� - 浣跨敤MapRenderer杩涜缁樺埗
+		visualizationPanel = new JPanel() {
+			private ImageIcon gecaojiIcon1 = null;  // 榛樿鍥炬爣
+			private ImageIcon gecaojiIcon2 = null;  // 浠ュ壊鑽夋満涓轰腑蹇冩ā寮忓浘鏍�
+			private static final int GECAOJI_ICON_X = 37;
+			private static final int GECAOJI_ICON_Y = 10;
+			private static final int GECAOJI_ICON_SIZE = 20;
+			
+			{
+				// 鍔犺浇鍓茶崏鏈哄浘鏍囷紝澶у皬20x20鍍忕礌
+				gecaojiIcon1 = loadScaledIcon("image/gecaojishijiao1.png", GECAOJI_ICON_SIZE, GECAOJI_ICON_SIZE);
+				gecaojiIcon2 = loadScaledIcon("image/gecaojishijiao2.png", GECAOJI_ICON_SIZE, GECAOJI_ICON_SIZE);
+			}
+			
+			/**
+			 * 妫�鏌ラ紶鏍囦綅缃槸鍚﹀湪鍓茶崏鏈哄浘鏍囧尯鍩熷唴
+			 */
+			private boolean isMouseOnGecaojiIcon(Point mousePoint) {
+				return mousePoint.x >= GECAOJI_ICON_X && 
+				       mousePoint.x <= GECAOJI_ICON_X + GECAOJI_ICON_SIZE &&
+				       mousePoint.y >= GECAOJI_ICON_Y && 
+				       mousePoint.y <= GECAOJI_ICON_Y + GECAOJI_ICON_SIZE;
+			}
+			
+			@Override
+			public String getToolTipText(MouseEvent event) {
+				// 濡傛灉榧犳爣鍦ㄥ壊鑽夋満鍥炬爣鍖哄煙鍐咃紝鏄剧ず鎻愮ず鏂囧瓧
+				if (isMouseOnGecaojiIcon(event.getPoint())) {
+					// 鏍规嵁褰撳墠妯″紡鏄剧ず涓嶅悓鐨勬彁绀烘枃瀛�
+					return centerOnMowerMode ? "鍙栨秷浠ュ壊鑽夋満涓轰腑蹇�" : "浠ュ壊鑽夋満涓轰腑蹇�";
+				}
+				// 涓嶅湪鍥炬爣涓婃椂杩斿洖null锛屼笉鏄剧ず宸ュ叿鎻愮ず妗�
+				return null;
+			}
+			
+			@Override
+			protected void paintComponent(Graphics g) {
+				super.paintComponent(g);
+				// 濮旀墭缁橫apRenderer杩涜缁樺埗
+				if (mapRenderer != null) {
+					mapRenderer.renderMap(g);
+				}
+				
+				// 妫�鏌ユ槸鍚﹂渶瑕佹樉绀鸿鍛婂浘鏍�
+				if (isMowerOutsideBoundary && warningIconVisible) {
+					// 缁樺埗绾㈣壊涓夎褰㈣鍛婂浘鏍囷紙甯﹀徆鍙凤級
+					drawWarningIcon(g, GECAOJI_ICON_X, GECAOJI_ICON_Y, GECAOJI_ICON_SIZE);
+				} else {
+					// 鏍规嵁妯″紡閫夋嫨涓嶅悓鐨勫浘鏍�
+					ImageIcon iconToDraw = centerOnMowerMode ? gecaojiIcon2 : gecaojiIcon1;
+					if (iconToDraw != null) {
+						// 缁樺埗鍓茶崏鏈哄浘鏍�
+						// 姘村钩鏂瑰悜涓庨�熷害鎸囩ず鍣ㄥ榻愶紙x=37锛�
+						// 鍨傜洿鏂瑰悜涓庡崼鏄熺姸鎬佸浘鏍囧榻愶紙y=10锛岄�熷害鎸囩ず鍣ㄩ潰鏉块《閮ㄨ竟璺�10鍍忕礌锛屼娇鍥炬爣涓績瀵归綈锛�
+						g.drawImage(iconToDraw.getImage(), GECAOJI_ICON_X, GECAOJI_ICON_Y, null);
+					}
+				}
+			}
+			
+			/**
+			 * 缁樺埗绾㈣壊涓夎褰㈣鍛婂浘鏍囷紙甯﹀徆鍙凤級
+			 */
+			private void drawWarningIcon(Graphics g, int x, int y, int size) {
+				Graphics2D g2d = (Graphics2D) g.create();
+				g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+				
+				// 缁樺埗绾㈣壊涓夎褰�
+				int[] xPoints = {x + size / 2, x, x + size};
+				int[] yPoints = {y, y + size, y + size};
+				g2d.setColor(Color.RED);
+				g2d.fillPolygon(xPoints, yPoints, 3);
+				
+				// 缁樺埗鐧借壊杈规
+				g2d.setColor(Color.WHITE);
+				g2d.setStroke(new BasicStroke(1.5f));
+				g2d.drawPolygon(xPoints, yPoints, 3);
+				
+				// 缁樺埗鐧借壊鍙瑰彿
+				g2d.setColor(Color.WHITE);
+				g2d.setFont(new Font("Arial", Font.BOLD, size * 3 / 4));
+				FontMetrics fm = g2d.getFontMetrics();
+				String exclamation = "!";
+				int textWidth = fm.stringWidth(exclamation);
+				int textHeight = fm.getAscent();
+				g2d.drawString(exclamation, x + (size - textWidth) / 2, y + (size + textHeight) / 2 - 2);
+				
+				g2d.dispose();
+			}
+		};
+		visualizationPanel.setLayout(new BorderLayout());
+		
+		// 娣诲姞榧犳爣鐐瑰嚮鐩戝惉鍣紝妫�娴嬫槸鍚︾偣鍑讳簡鍓茶崏鏈哄浘鏍�
+		visualizationPanel.addMouseListener(new MouseAdapter() {
+			@Override
+			public void mouseClicked(MouseEvent e) {
+				if (SwingUtilities.isLeftMouseButton(e)) {
+					Point clickPoint = e.getPoint();
+					// 妫�鏌ユ槸鍚︾偣鍑讳簡鍓茶崏鏈哄浘鏍囧尯鍩燂紙37, 10, 20, 20锛�
+					if (clickPoint.x >= 37 && clickPoint.x <= 57 && 
+					    clickPoint.y >= 10 && clickPoint.y <= 30) {
+						// 鍒囨崲浠ュ壊鑽夋満涓轰腑蹇冪殑妯″紡
+						toggleCenterOnMowerMode();
+					}
+				}
+			}
+		});
+
+		JPanel speedIndicatorPanel = createSpeedIndicatorPanel();
+		visualizationPanel.add(speedIndicatorPanel, BorderLayout.NORTH);
+
+		// 鍒涘缓鍔熻兘鎸夐挳闈㈡澘
+		JPanel functionButtonsPanel = new JPanel();
+		functionButtonsPanel.setLayout(new BoxLayout(functionButtonsPanel, BoxLayout.Y_AXIS));
+		functionButtonsPanel.setOpaque(false);
+		functionButtonsPanel.setBorder(BorderFactory.createEmptyBorder(20, 5, 0, 0)); // 宸﹁竟璺濇敼涓�5鍍忕礌
+
+		legendBtn = createFunctionButton("鍥句緥", "馃摉");
+		baseStationBtn = createFunctionButton("鍩哄噯绔�", "馃摗"); // 璋冩暣鍒扮浜屼綅
+		areaSelectBtn = createFunctionButton("鍦板潡", "馃尶");    // 璋冩暣鍒扮涓変綅
+		remoteBtn = createFunctionButton("閬ユ帶", "馃幃");        // 璋冩暣鍒版渶鍚�
+
+		functionButtonsPanel.add(legendBtn);
+		functionButtonsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+		functionButtonsPanel.add(baseStationBtn);
+		functionButtonsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+		functionButtonsPanel.add(areaSelectBtn);
+		functionButtonsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+		functionButtonsPanel.add(remoteBtn);
+
+		visualizationPanel.add(functionButtonsPanel, BorderLayout.WEST);
+
+		JPanel zoomControlPanel = createZoomControlPanel();
+		visualizationPanel.add(zoomControlPanel, BorderLayout.EAST);
+
+		mainContentPanel.add(visualizationPanel, BorderLayout.CENTER);
+
+		startMowerSpeedUpdates();
+	}
+
+	private JPanel createZoomControlPanel() {
+		JPanel container = new JPanel(new BorderLayout());
+		container.setOpaque(false);
+		container.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 20));
+
+		JPanel alignmentPanel = new JPanel();
+		alignmentPanel.setOpaque(false);
+		alignmentPanel.setLayout(new BoxLayout(alignmentPanel, BoxLayout.Y_AXIS));
+		alignmentPanel.add(Box.createVerticalGlue());
+
+		JPanel buttonStack = new JPanel();
+		buttonStack.setOpaque(false);
+		buttonStack.setLayout(new BoxLayout(buttonStack, BoxLayout.Y_AXIS));
+
+		JButton zoomInButton = createZoomButton("+");
+		JButton zoomOutButton = createZoomButton("-");
+
+		AtomicBoolean skipZoomInClick = new AtomicBoolean(false);
+		AtomicBoolean skipZoomOutClick = new AtomicBoolean(false);
+
+		Timer zoomInHoldTimer = new Timer(120, event -> {
+			if (mapRenderer == null || !zoomInButton.getModel().isPressed()) {
+				((Timer) event.getSource()).stop();
+				return;
+			}
+			if (!mapRenderer.canZoomIn()) {
+				((Timer) event.getSource()).stop();
+				return;
+			}
+			skipZoomInClick.set(true);
+			mapRenderer.zoomInFromCenter();
+			if (!mapRenderer.canZoomIn()) {
+				((Timer) event.getSource()).stop();
+			}
+		});
+		zoomInHoldTimer.setInitialDelay(180);
+		zoomInHoldTimer.setDelay(120);
+
+		Timer zoomOutHoldTimer = new Timer(120, event -> {
+			if (mapRenderer == null || !zoomOutButton.getModel().isPressed()) {
+				((Timer) event.getSource()).stop();
+				return;
+			}
+			if (!mapRenderer.canZoomOut()) {
+				((Timer) event.getSource()).stop();
+				return;
+			}
+			skipZoomOutClick.set(true);
+			mapRenderer.zoomOutFromCenter();
+			if (!mapRenderer.canZoomOut()) {
+				((Timer) event.getSource()).stop();
+			}
+		});
+		zoomOutHoldTimer.setInitialDelay(180);
+		zoomOutHoldTimer.setDelay(120);
+
+		zoomInButton.addMouseListener(new MouseAdapter() {
+			@Override
+			public void mousePressed(MouseEvent e) {
+				if (!SwingUtilities.isLeftMouseButton(e) || mapRenderer == null || !zoomInButton.isEnabled()) {
+					return;
+				}
+				if (mapRenderer.canZoomIn()) {
+					skipZoomInClick.set(true);
+					mapRenderer.zoomInFromCenter();
+					if (mapRenderer.canZoomIn()) {
+						zoomInHoldTimer.restart();
+					} else {
+						zoomInHoldTimer.stop();
+					}
+				} else {
+					skipZoomInClick.set(false);
+					zoomInHoldTimer.stop();
+				}
+			}
+
+			@Override
+			public void mouseReleased(MouseEvent e) {
+				zoomInHoldTimer.stop();
+				if (!zoomInButton.contains(e.getPoint())) {
+					skipZoomInClick.set(false);
+				}
+			}
+
+			@Override
+			public void mouseExited(MouseEvent e) {
+				if (!zoomInButton.getModel().isPressed()) {
+					zoomInHoldTimer.stop();
+				}
+			}
+		});
+
+		zoomOutButton.addMouseListener(new MouseAdapter() {
+			@Override
+			public void mousePressed(MouseEvent e) {
+				if (!SwingUtilities.isLeftMouseButton(e) || mapRenderer == null || !zoomOutButton.isEnabled()) {
+					return;
+				}
+				if (mapRenderer.canZoomOut()) {
+					skipZoomOutClick.set(true);
+					mapRenderer.zoomOutFromCenter();
+					if (mapRenderer.canZoomOut()) {
+						zoomOutHoldTimer.restart();
+					} else {
+						zoomOutHoldTimer.stop();
+					}
+				} else {
+					skipZoomOutClick.set(false);
+					zoomOutHoldTimer.stop();
+				}
+			}
+
+			@Override
+			public void mouseReleased(MouseEvent e) {
+				zoomOutHoldTimer.stop();
+				if (!zoomOutButton.contains(e.getPoint())) {
+					skipZoomOutClick.set(false);
+				}
+			}
+
+			@Override
+			public void mouseExited(MouseEvent e) {
+				if (!zoomOutButton.getModel().isPressed()) {
+					zoomOutHoldTimer.stop();
+				}
+			}
+		});
+
+		zoomInButton.addActionListener(e -> {
+			if (skipZoomInClick.getAndSet(false)) {
+				return;
+			}
+			if (mapRenderer != null) {
+				mapRenderer.zoomInFromCenter();
+			}
+		});
+
+		zoomOutButton.addActionListener(e -> {
+			if (skipZoomOutClick.getAndSet(false)) {
+				return;
+			}
+			if (mapRenderer != null) {
+				mapRenderer.zoomOutFromCenter();
+			}
+		});
+
+		buttonStack.add(zoomInButton);
+		buttonStack.add(Box.createRigidArea(new Dimension(0, 8)));
+		buttonStack.add(zoomOutButton);
+
+		buttonStack.setAlignmentX(Component.CENTER_ALIGNMENT);
+		alignmentPanel.add(buttonStack);
+
+		container.add(alignmentPanel, BorderLayout.CENTER);
+		return container;
+	}
+
+	private JButton createZoomButton(String symbol) {
+		JButton button = new JButton(symbol);
+		Dimension size = new Dimension(44, 44);
+		button.setPreferredSize(size);
+		button.setMinimumSize(size);
+		button.setMaximumSize(size);
+		button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 22));
+		button.setMargin(new Insets(0, 0, 0, 0));
+		button.setFocusPainted(false);
+		button.setBackground(Color.WHITE);
+		button.setForeground(Color.DARK_GRAY);
+		button.setBorder(BorderFactory.createLineBorder(new Color(210, 210, 210), 1, true));
+		button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+		button.setOpaque(true);
+
+		button.addMouseListener(new MouseAdapter() {
+			@Override
+			public void mouseEntered(MouseEvent e) {
+				button.setBackground(new Color(245, 245, 245));
+			}
+
+			@Override
+			public void mouseExited(MouseEvent e) {
+				button.setBackground(Color.WHITE);
+			}
+		});
+
+		return button;
+	}
+
+	private void createControlPanel() {
+		controlPanel = new JPanel(new BorderLayout());
+		controlPanel.setBackground(PANEL_BACKGROUND);
+		controlPanel.setBorder(BorderFactory.createEmptyBorder(15, 20, 15, 20));
+		controlPanel.setPreferredSize(new Dimension(0, 100));
+
+		JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 20, 0));
+		buttonPanel.setBackground(PANEL_BACKGROUND);
+
+		startBtn = createControlButton("鏆傚仠", THEME_COLOR);
+		updateStartButtonAppearance();
+
+		stopBtn = createControlButton("缁撴潫", Color.ORANGE);
+		updateStopButtonIcon();
+
+		buttonPanel.add(startBtn);
+		buttonPanel.add(stopBtn);
+
+		controlPanel.add(buttonPanel, BorderLayout.CENTER);
+	}
+
+	private void createNavigationPanel() {
+		navigationPanel = new JPanel(new GridLayout(1, 3));
+		navigationPanel.setBackground(PANEL_BACKGROUND);
+		navigationPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+		navigationPanel.setPreferredSize(new Dimension(0, 70));
+
+		homeNavBtn = createNavButton("棣栭〉", "馃彔");
+		areasNavBtn = createNavButton("鍦板潡", "馃尶");
+		settingsNavBtn = createNavButton("璁剧疆", "鈿欙笍");
+
+		navigationPanel.add(homeNavBtn);
+		navigationPanel.add(areasNavBtn);
+		navigationPanel.add(settingsNavBtn);
+
+		// 娣诲姞鍒颁富鐣岄潰搴曢儴
+		add(navigationPanel, BorderLayout.SOUTH);
+	}
+
+	private JButton createIconButton(String icon, int size) {
+		JButton button = new JButton(icon);
+		button.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 20));
+		button.setPreferredSize(new Dimension(size, size));
+		button.setBackground(PANEL_BACKGROUND);
+		button.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+		button.setFocusPainted(false);
+
+		// 鎮仠鏁堟灉
+		button.addMouseListener(new MouseAdapter() {
+			public void mouseEntered(MouseEvent e) {
+				button.setBackground(new Color(240, 240, 240));
+			}
+			public void mouseExited(MouseEvent e) {
+				button.setBackground(PANEL_BACKGROUND);
+			}
+		});
+
+		return button;
+	}
+
+	private JButton createBluetoothButton() {
+		JButton button = new JButton();
+		button.setPreferredSize(new Dimension(40, 40));
+		button.setBackground(PANEL_BACKGROUND);
+		button.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+		button.setFocusPainted(false);
+		button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+		button.addMouseListener(new MouseAdapter() {
+			@Override
+			public void mouseEntered(MouseEvent e) {
+				button.setBackground(new Color(240, 240, 240));
+			}
+
+			@Override
+			public void mouseExited(MouseEvent e) {
+				button.setBackground(PANEL_BACKGROUND);
+			}
+		});
+		ensureBluetoothIconsLoaded();
+		// 鏍规嵁涓插彛杩炴帴鐘舵�佹樉绀哄浘鏍�
+		SerialPortService service = sendmessage.getActiveService();
+		boolean serialConnected = (service != null && service.isOpen());
+		ImageIcon initialIcon = serialConnected ? bluetoothLinkedIcon : bluetoothIcon;
+		if (initialIcon != null) {
+			button.setIcon(initialIcon);
+		} else {
+			button.setText(serialConnected ? "宸茶繛" : "钃濈墮");
+		}
+		return button;
+	}
+
+	private JButton createFunctionButton(String text, String icon) {
+		JButton button = new JButton("<html><center>" + icon + "<br>" + text + "</center></html>");
+		button.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+		button.setPreferredSize(new Dimension(80, 70));
+		button.setBackground(new Color(0, 0, 0, 0)); // 瀹屽叏閫忔槑鑳屾櫙
+		button.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); // 鍙繚鐣欏唴杈硅窛
+		button.setFocusPainted(false);
+		button.setOpaque(false); // 璁剧疆涓轰笉閫忔槑锛岀‘淇濊儗鏅�忔槑
+
+		// 鍘绘帀鎮仠鏁堟灉
+		button.addMouseListener(new MouseAdapter() {
+			public void mouseEntered(MouseEvent e) {
+				// 鎮仠鏃朵笉鏀瑰彉浠讳綍鏍峰紡
+			}
+			public void mouseExited(MouseEvent e) {
+				// 鎮仠鏃朵笉鏀瑰彉浠讳綍鏍峰紡
+			}
+		});
+
+		return button;
+	}
+
+	private JButton createControlButton(String text, Color color) {
+		JButton button = new JButton(text);
+		button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
+		button.setBackground(color);
+		button.setForeground(Color.WHITE);
+		button.setBorder(BorderFactory.createEmptyBorder(15, 0, 15, 0));
+		button.setFocusPainted(false);
+
+		// 鎮仠鏁堟灉
+		button.addMouseListener(new MouseAdapter() {
+			public void mouseEntered(MouseEvent e) {
+				if (button.isEnabled()) {
+					if (color == THEME_COLOR) {
+						button.setBackground(THEME_HOVER_COLOR);
+					} else {
+						button.setBackground(new Color(255, 165, 0));
+					}
+				}
+			}
+			public void mouseExited(MouseEvent e) {
+				if (button.isEnabled()) {
+					button.setBackground(color);
+				}
+			}
+		});
+
+		return button;
+	}
+
+	private void applyButtonIcon(JButton button, String imagePath) {
+		try {
+			ImageIcon icon = new ImageIcon(imagePath);
+			Image scaledImage = icon.getImage().getScaledInstance(28, 28, Image.SCALE_SMOOTH);
+			button.setIcon(new ImageIcon(scaledImage));
+			button.setHorizontalAlignment(SwingConstants.CENTER);
+			button.setIconTextGap(10);
+			button.setHorizontalTextPosition(SwingConstants.RIGHT);
+			button.setVerticalTextPosition(SwingConstants.CENTER);
+		} catch (Exception e) {
+			System.err.println("鏃犳硶鍔犺浇鎸夐挳鍥炬爣: " + imagePath + " -> " + e.getMessage());
+		}
+	}
+
+	private JButton createNavButton(String text, String icon) {
+		JButton button = new JButton("<html><center>" + icon + "<br>" + text + "</center></html>");
+		button.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+		button.setBackground(PANEL_BACKGROUND);
+		button.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 5));
+		button.setFocusPainted(false);
+
+		// 鎮仠鏁堟灉
+		button.addMouseListener(new MouseAdapter() {
+			public void mouseEntered(MouseEvent e) {
+				if (button != currentNavButton) {
+					button.setBackground(new Color(240, 240, 240));
+				}
+			}
+			public void mouseExited(MouseEvent e) {
+				if (button != currentNavButton) {
+					button.setBackground(PANEL_BACKGROUND);
+				}
+			}
+		});
+
+		return button;
+	}
+
+	private void setNavigationActive(JButton navButton) {
+		// 閲嶇疆鎵�鏈夊鑸寜閽牱寮�
+		for (Component comp : navigationPanel.getComponents()) {
+			if (comp instanceof JButton) {
+				JButton btn = (JButton) comp;
+				btn.setBackground(PANEL_BACKGROUND);
+				btn.setForeground(Color.BLACK);
+			}
+		}
+
+		// 璁剧疆褰撳墠閫変腑鎸夐挳鏍峰紡
+		navButton.setBackground(THEME_COLOR);
+		navButton.setForeground(Color.WHITE);
+		currentNavButton = navButton;
+	}
+
+	private void setupEventHandlers() {
+		// 瀵艰埅鎸夐挳浜嬩欢
+		homeNavBtn.addActionListener(e -> setNavigationActive(homeNavBtn));
+		areasNavBtn.addActionListener(e -> setNavigationActive(areasNavBtn));
+		settingsNavBtn.addActionListener(e -> setNavigationActive(settingsNavBtn));
+
+		// 鍔熻兘鎸夐挳浜嬩欢
+		legendBtn.addActionListener(e -> showLegendDialog());
+		if (bluetoothBtn != null) {
+			bluetoothBtn.addActionListener(e -> toggleBluetoothConnection());
+		}
+		remoteBtn.addActionListener(e -> showRemoteControlDialog());
+		areaSelectBtn.addActionListener(e -> {
+			// 鐐瑰嚮鈥滃湴鍧椻�濈洿鎺ユ墦寮�鍦板潡绠$悊瀵硅瘽妗嗭紙鑻ラ渶瑕佸彲浼犲叆鐗瑰畾鍦板潡缂栧彿锛�
+			Dikuaiguanli.showDikuaiManagement(this, null);
+		});
+		baseStationBtn.addActionListener(e -> showBaseStationDialog());
+
+		// 鎺у埗鎸夐挳浜嬩欢
+		startBtn.addActionListener(e -> toggleStartPause());
+		stopBtn.addActionListener(e -> handleStopAction());
+	}
+
+	private void showSettingsDialog() {
+		if (settingsDialog == null) {
+			Window parentWindow = SwingUtilities.getWindowAncestor(this);
+			if (parentWindow instanceof JFrame) {
+				settingsDialog = new Sets((JFrame) parentWindow, THEME_COLOR);
+			} else if (parentWindow instanceof JDialog) {
+				settingsDialog = new Sets((JDialog) parentWindow, THEME_COLOR);
+			} else {
+				// Fallback to a frameless dialog when no parent is available
+				settingsDialog = new Sets((JFrame) null, THEME_COLOR);
+			}
+		}
+		settingsDialog.setVisible(true);
+	}
+
+	private void showLegendDialog() {
+		if (legendDialog == null) {
+			Window parentWindow = SwingUtilities.getWindowAncestor(this);
+			if (parentWindow != null) {
+				legendDialog = new LegendDialog(this, THEME_COLOR);
+			} else {
+				// 濡傛灉娌℃湁鐖剁獥鍙o紝鍒涘缓鏃犵埗绐楀彛鐨勫璇濇
+				legendDialog = new LegendDialog((JFrame) null, THEME_COLOR);
+			}
+		}
+		legendDialog.setVisible(true);
+	}
+
+	private void showRemoteControlDialog() {
+		if (remoteDialog == null) {
+			Window parentWindow = SwingUtilities.getWindowAncestor(this);
+			if (parentWindow != null) {
+				// 浣跨敤 yaokong 鍖呬腑鐨� RemoteControlDialog 瀹炵幇
+				remoteDialog = new yaokong.RemoteControlDialog(this, THEME_COLOR, speedLabel);
+			} else {
+				remoteDialog = new yaokong.RemoteControlDialog((JFrame) null, THEME_COLOR, speedLabel);
+			}
+		}
+		if (remoteDialog != null) {
+			positionRemoteDialogBottomCenter(remoteDialog);
+			zijian.markSelfCheckCompleted();
+			remoteDialog.setVisible(true);
+		}
+	}
+
+	private void positionRemoteDialogBottomCenter(RemoteControlDialog dialog) {
+		if (dialog == null) {
+			return;
+		}
+		// 灏嗗璇濇搴曢儴涓庢暣涓椤碉紙Shouye 闈㈡澘锛夌殑涓嬭竟妗嗗榻愶紝
+		// 鑰屼笉鏄笌 visualizationPanel 瀵归綈锛屼互婊¤冻璁捐瑕佹眰銆�
+		Dimension dialogSize = dialog.getSize();
+
+		// 鑾峰彇褰撳墠 Shouye 闈㈡澘鍦ㄥ睆骞曚笂鐨勪綅缃拰灏哄
+		Point parentOnScreen = null;
+		try {
+			parentOnScreen = this.getLocationOnScreen();
+		} catch (IllegalComponentStateException ex) {
+			// 濡傛灉缁勪欢灏氭湭鏄剧ず鎴栨棤娉曡幏鍙栧睆骞曚綅缃紝鍥為��鍒扮獥鍙e眰绾т綅缃幏鍙�
+			Window owner = SwingUtilities.getWindowAncestor(this);
+			if (owner != null) {
+				parentOnScreen = owner.getLocationOnScreen();
+			}
+		}
+
+		int x = 0, y = 0;
+		if (parentOnScreen != null) {
+			int parentWidth = this.getWidth();
+			int parentHeight = this.getHeight();
+			x = parentOnScreen.x + (parentWidth - dialogSize.width) / 2;
+			y = parentOnScreen.y + parentHeight - dialogSize.height;
+		} else {
+			// 浣滀负鏈�鍚庣殑鍥為��锛屼娇鐢ㄥ睆骞曚腑蹇冨榻愬簳閮�
+			Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
+			x = (screen.width - dialogSize.width) / 2;
+			y = screen.height - dialogSize.height;
+		}
+
+		dialog.setLocation(Math.max(x, 0), Math.max(y, 0));
+	}
+
+	private Rectangle computeVisualizationBoundsOnScreen() {
+		if (visualizationPanel == null || !visualizationPanel.isShowing()) {
+			return null;
+		}
+		Point location = visualizationPanel.getLocationOnScreen();
+		Dimension size = visualizationPanel.getSize();
+		return new Rectangle(location.x, location.y, size.width, size.height);
+	}
+
+	private void showAreaSelectionDialog() {
+		if (areaDialog == null) {
+			Window parentWindow = SwingUtilities.getWindowAncestor(this);
+			if (parentWindow != null) {
+				areaDialog = new AreaSelectionDialog(this, THEME_COLOR, areaNameLabel, statusLabel);
+			} else {
+				areaDialog = new AreaSelectionDialog((JFrame) null, THEME_COLOR, areaNameLabel, statusLabel);
+			}
+		}
+		areaDialog.setVisible(true);
+	}
+
+	private void showBaseStationDialog() {
+		if (baseStation == null) {
+			baseStation = new BaseStation();
+		}
+		baseStation.load();
+
+		Component dialogParent = this;
+
+		if (!hasValidBaseStationId()) {
+			boolean recorded = promptForBaseStationId(dialogParent);
+			if (!recorded) {
+				return;
+			}
+		}
+
+		Device device = Device.getGecaoji();
+		if (device == null) {
+			device = new Device();
+			device.initFromProperties();
+			Device.setGecaoji(device);
+		}
+
+		if (baseStationDialog == null) {
+			baseStationDialog = new BaseStationDialog(dialogParent, THEME_COLOR, device, baseStation);
+		} else {
+			baseStationDialog.refreshData();
+		}
+		baseStationDialog.setVisible(true);
+	}
+
+	private void checkIdentifiersAndPromptIfNeeded() {
+		if (baseStation == null) {
+			baseStation = new BaseStation();
+		}
+		baseStation.load();
+
+		String currentMowerId = Setsys.getPropertyValue("mowerId");
+		String currentBaseStationId = baseStation.getDeviceId();
+
+		if (!isIdentifierMissing(currentMowerId) && !isIdentifierMissing(currentBaseStationId)) {
+			return;
+		}
+
+		Window owner = SwingUtilities.getWindowAncestor(this);
+		promptForMissingIdentifiers(owner, currentMowerId, currentBaseStationId);
+	}
+
+	private void promptForMissingIdentifiers(Window owner, String currentMowerId, String currentBaseStationId) {
+		while (true) {
+			JTextField mowerField = new JTextField(10);
+			JTextField baseField = new JTextField(10);
+
+			if (!isIdentifierMissing(currentMowerId)) {
+				mowerField.setText(currentMowerId.trim());
+			}
+			if (!isIdentifierMissing(currentBaseStationId)) {
+				baseField.setText(currentBaseStationId.trim());
+			}
+
+			JPanel panel = new JPanel();
+			panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+			panel.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
+
+			JLabel mowerLabel = new JLabel("鍓茶崏鏈虹紪鍙�");
+			JLabel baseLabel = new JLabel("宸垎鍩哄噯绔欑紪鍙�");
+
+			mowerField.setMaximumSize(new Dimension(Integer.MAX_VALUE, mowerField.getPreferredSize().height));
+			baseField.setMaximumSize(new Dimension(Integer.MAX_VALUE, baseField.getPreferredSize().height));
+
+			panel.add(mowerLabel);
+			panel.add(Box.createVerticalStrut(4));
+			panel.add(mowerField);
+			panel.add(Box.createVerticalStrut(10));
+			panel.add(baseLabel);
+			panel.add(Box.createVerticalStrut(4));
+			panel.add(baseField);
+
+			Object[] options = {"淇濆瓨", "鍙栨秷"};
+			int result = JOptionPane.showOptionDialog(owner, panel, "瀹屽杽璁惧淇℃伅",
+					JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
+
+			if (result != 0) {
+				break;
+			}
+
+			String mowerInput = mowerField.getText().trim();
+			String baseInput = baseField.getText().trim();
+
+			if (mowerInput.isEmpty()) {
+				JOptionPane.showMessageDialog(owner, "鍓茶崏鏈虹紪鍙蜂笉鑳戒负绌恒��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+				continue;
+			}
+
+			if (baseInput.isEmpty()) {
+				JOptionPane.showMessageDialog(owner, "宸垎鍩哄噯绔欑紪鍙蜂笉鑳戒负绌恒��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+				continue;
+			}
+
+			boolean mowerSaved = persistMowerIdentifier(mowerInput);
+			boolean baseSaved = persistBaseStationIdentifier(baseInput);
+
+			if (mowerSaved && baseSaved) {
+				JOptionPane.showMessageDialog(owner, "缂栧彿宸蹭繚瀛樸��", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+				break;
+			}
+
+			StringBuilder errorBuilder = new StringBuilder();
+			if (!mowerSaved) {
+				errorBuilder.append("鍓茶崏鏈虹紪鍙蜂繚瀛樺け璐ャ��");
+			}
+			if (!baseSaved) {
+				if (errorBuilder.length() > 0) {
+					errorBuilder.append('\n');
+				}
+				errorBuilder.append("宸垎鍩哄噯绔欑紪鍙蜂繚瀛樺け璐ャ��");
+			}
+
+			JOptionPane.showMessageDialog(owner, errorBuilder.toString(), "淇濆瓨澶辫触", JOptionPane.ERROR_MESSAGE);
+
+			currentMowerId = Setsys.getPropertyValue("mowerId");
+			baseStation.load();
+			currentBaseStationId = baseStation.getDeviceId();
+		}
+	}
+
+	private boolean isIdentifierMissing(String value) {
+		if (value == null) {
+			return true;
+		}
+		String trimmed = value.trim();
+		return trimmed.isEmpty() || "-1".equals(trimmed);
+	}
+
+	private boolean persistMowerIdentifier(String mowerId) {
+		try {
+			Setsys setsys = new Setsys();
+			setsys.initializeFromProperties();
+			boolean updated = setsys.updateProperty("mowerId", mowerId);
+			if (updated) {
+				Device.initializeActiveDevice(mowerId);
+			}
+			return updated;
+		} catch (Exception ex) {
+			ex.printStackTrace();
+			return false;
+		}
+	}
+
+	private boolean persistBaseStationIdentifier(String baseStationId) {
+		if (baseStation == null) {
+			baseStation = new BaseStation();
+		}
+		try {
+			baseStation.updateByDeviceId(baseStationId,
+					baseStation.getInstallationCoordinates(),
+					baseStation.getIotSimCardNumber(),
+					baseStation.getDeviceActivationTime(),
+					baseStation.getDataUpdateTime());
+			baseStation.load();
+			return true;
+		} catch (Exception ex) {
+			ex.printStackTrace();
+			return false;
+		}
+	}
+
+	private boolean hasValidBaseStationId() {
+		if (baseStation == null) {
+			return false;
+		}
+		String deviceId = baseStation.getDeviceId();
+		if (deviceId == null) {
+			return false;
+		}
+		String trimmed = deviceId.trim();
+		return !trimmed.isEmpty() && !"-1".equals(trimmed);
+	}
+
+	private boolean promptForBaseStationId(Component parentComponent) {
+		if (baseStation == null) {
+			baseStation = new BaseStation();
+		}
+
+		while (true) {
+			String input = JOptionPane.showInputDialog(parentComponent,
+					"璇疯緭鍏ュ熀鍑嗙珯缂栧彿", "褰曞叆鍩哄噯绔欑紪鍙�", JOptionPane.PLAIN_MESSAGE);
+
+			if (input == null) {
+				return false;
+			}
+
+			String trimmed = input.trim();
+			if (trimmed.isEmpty()) {
+				JOptionPane.showMessageDialog(parentComponent,
+						"鍩哄噯绔欑紪鍙蜂笉鑳戒负绌猴紝璇烽噸鏂拌緭鍏ャ��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+				continue;
+			}
+
+			try {
+				baseStation.updateByDeviceId(trimmed,
+						baseStation.getInstallationCoordinates(),
+						baseStation.getIotSimCardNumber(),
+						baseStation.getDeviceActivationTime(),
+						baseStation.getDataUpdateTime());
+				baseStation.load();
+				JOptionPane.showMessageDialog(parentComponent,
+						"鍩哄噯绔欑紪鍙峰凡淇濆瓨銆�", "鎿嶄綔鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+				return true;
+			} catch (IllegalArgumentException ex) {
+				JOptionPane.showMessageDialog(parentComponent,
+						ex.getMessage(), "杈撳叆閿欒", JOptionPane.ERROR_MESSAGE);
+			}
+		}
+	}
+
+	private void toggleStartPause() {
+		if (handheldCaptureInlineUiActive) {
+			handleHandheldConfirmAction();
+			return;
+		}
+		if (drawingControlModeActive) {
+			toggleDrawingPause();
+			return;
+		}
+		if (startBtn == null) {
+			return;
+		}
+		if (startButtonShowingPause) {
+			// 鐐瑰嚮寮�濮嬫寜閽椂涓嶅啀寮瑰嚭鑷鎻愮ず锛堟寜鐢ㄦ埛瑕佹眰鍒犻櫎锛�
+			// 鏃ч�昏緫锛氳皟鐢� zijian.ensureBeforeMowing(...) 骞跺湪鏈‘璁よ嚜妫�鏃堕樆姝㈠紑濮�
+			// 鏂伴�昏緫锛氱洿鎺ュ厑璁稿紑濮嬩綔涓�
+		}
+		startButtonShowingPause = !startButtonShowingPause;
+		if (!startButtonShowingPause) {
+			// 妫�鏌ュ壊鑽夋満鏄惁鍦ㄤ綔涓氬湴鍧楄竟鐣岃寖鍥村唴
+			if (!checkMowerInBoundary()) {
+				startButtonShowingPause = true;
+				statusLabel.setText("寰呮満");
+				updateStartButtonAppearance();
+				return;
+			}
+			
+			statusLabel.setText("浣滀笟涓�");
+			if (stopButtonActive) {
+				stopButtonActive = false;
+				updateStopButtonIcon();
+			}
+		if (!beginMowingSession()) {
+			startButtonShowingPause = true;
+			statusLabel.setText("寰呮満");
+			updateStartButtonAppearance();
+			return;
+		}
+		} else {
+			statusLabel.setText("鏆傚仠涓�");
+			pauseMowingSession();
+		}
+		updateStartButtonAppearance();
+	}
+
+	/**
+	 * 妫�鏌ュ壊鑽夋満鏄惁鍦ㄥ綋鍓嶉�変腑鐨勪綔涓氬湴鍧楄竟鐣岃寖鍥村唴
+	 * @return 濡傛灉鍓茶崏鏈哄湪杈圭晫鍐呰繑鍥瀟rue锛屽惁鍒欒繑鍥瀎alse骞舵樉绀烘彁绀�
+	 */
+	private boolean checkMowerInBoundary() {
+		if (mapRenderer == null) {
+			return true; // 濡傛灉娌℃湁鍦板浘娓叉煋鍣紝璺宠繃妫�鏌�
+		}
+
+		// 鑾峰彇褰撳墠杈圭晫
+		List<Point2D.Double> boundary = mapRenderer.getCurrentBoundary();
+		if (boundary == null || boundary.size() < 3) {
+			return true; // 濡傛灉娌℃湁杈圭晫鎴栬竟鐣岀偣涓嶈冻锛岃烦杩囨鏌�
+		}
+
+		// 鑾峰彇鍓茶崏鏈轰綅缃�
+		Gecaoji mower = mapRenderer.getMower();
+		if (mower == null || !mower.hasValidPosition()) {
+			showCustomMessageDialog("鏃犳硶鑾峰彇鍓茶崏鏈轰綅缃紝璇锋鏌ヨ澶囪繛鎺�", "鎻愮ず");
+			return false;
+		}
+
+		Point2D.Double mowerPosition = mower.getPosition();
+		if (mowerPosition == null) {
+			showCustomMessageDialog("鏃犳硶鑾峰彇鍓茶崏鏈轰綅缃紝璇锋鏌ヨ澶囪繛鎺�", "鎻愮ず");
+			return false;
+		}
+
+		// 浣跨敤 MowerBoundaryChecker 妫�鏌ユ槸鍚﹀湪杈圭晫鍐�
+		boolean isInside = MowerBoundaryChecker.isInsideBoundaryPoints(
+			boundary, 
+			mowerPosition.x, 
+			mowerPosition.y
+		);
+
+		if (!isInside) {
+			showCustomMessageDialog("璇峰皢鍓茶崏鏈哄紑鍒颁綔涓氬湴鍧楀唴鐒跺悗鐐瑰嚮寮�濮嬩綔涓�", "鎻愮ず");
+			return false;
+		}
+
+		return true;
+	}
+
+	/**
+	 * 鏄剧ず鑷畾涔夋秷鎭璇濇锛屼娇鐢� buttonset 鍒涘缓纭畾鎸夐挳
+	 * @param message 娑堟伅鍐呭
+	 * @param title 瀵硅瘽妗嗘爣棰�
+	 */
+	private void showCustomMessageDialog(String message, String title) {
+		Window parentWindow = SwingUtilities.getWindowAncestor(this);
+		JDialog dialog = new JDialog(parentWindow, title, Dialog.ModalityType.APPLICATION_MODAL);
+		dialog.setLayout(new BorderLayout(20, 20));
+		dialog.setResizable(false);
+		
+		// 鍐呭闈㈡澘
+		JPanel contentPanel = new JPanel(new BorderLayout(0, 15));
+		contentPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 10, 20));
+		contentPanel.setBackground(Color.WHITE);
+		
+		// 娑堟伅鏍囩
+		JLabel messageLabel = new JLabel("<html><div style='text-align: center;'>" + message + "</div></html>");
+		messageLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+		messageLabel.setHorizontalAlignment(SwingConstants.CENTER);
+		contentPanel.add(messageLabel, BorderLayout.CENTER);
+		
+		// 鎸夐挳闈㈡澘
+		JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0));
+		buttonPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
+		buttonPanel.setOpaque(false);
+		
+		// 浣跨敤 buttonset 鍒涘缓纭畾鎸夐挳
+		JButton okButton = buttonset.createStyledButton("纭畾", THEME_COLOR);
+		okButton.addActionListener(e -> dialog.dispose());
+		buttonPanel.add(okButton);
+		
+		contentPanel.add(buttonPanel, BorderLayout.SOUTH);
+		dialog.add(contentPanel, BorderLayout.CENTER);
+		
+		dialog.pack();
+		dialog.setLocationRelativeTo(this);
+		dialog.setVisible(true);
+	}
+
+	private void handleStopAction() {
+		if (handheldCaptureInlineUiActive) {
+			handleHandheldFinishAction();
+			return;
+		}
+		if (drawingControlModeActive) {
+			handleDrawingStopFromControlPanel();
+			return;
+		}
+		stopButtonActive = !stopButtonActive;
+		updateStopButtonIcon();
+		if (stopButtonActive) {
+			statusLabel.setText("宸茬粨鏉�");
+			startButtonShowingPause = false;
+			stopMowingSession();
+		} else {
+			statusLabel.setText("寰呮満");
+			startButtonShowingPause = true;
+			pauseMowingSession();
+		}
+		updateStartButtonAppearance();
+	}
+
+	private void handleDrawingStopFromControlPanel() {
+		if (endDrawingCallback != null) {
+			endDrawingCallback.run();
+		} else {
+			addzhangaiwu.finishDrawingSession();
+		}
+	}
+
+	private void handleHandheldConfirmAction() {
+		if (!handheldCaptureInlineUiActive) {
+			return;
+		}
+		if (!canConfirmHandheldPoint()) {
+			refreshHandheldCaptureUiState();
+			return;
+		}
+		int count = captureHandheldBoundaryPoint();
+		if (count <= 0) {
+			refreshHandheldCaptureUiState();
+			return;
+		}
+		refreshHandheldCaptureUiState();
+	}
+
+	private void handleHandheldFinishAction() {
+		if (!handheldCaptureInlineUiActive) {
+			return;
+		}
+		if (stopBtn != null && !stopBtn.isEnabled()) {
+			refreshHandheldCaptureUiState();
+			return;
+		}
+		if (!finishHandheldBoundaryCapture()) {
+			refreshHandheldCaptureUiState();
+		}
+	}
+
+	private void enterHandheldCaptureInlineUi() {
+		if (handheldCaptureInlineUiActive) {
+			refreshHandheldCaptureUiState();
+			return;
+		}
+		handheldCaptureInlineUiActive = true;
+		handheldCaptureStoredStatusText = statusLabel != null ? statusLabel.getText() : null;
+		if (statusLabel != null) {
+			statusLabel.setText("鎵嬫寔閲囬泦涓�");
+		}
+		if (startBtn != null) {
+			handheldStartButtonOriginalBackground = startBtn.getBackground();
+			handheldStartButtonOriginalForeground = startBtn.getForeground();
+			startBtn.setIcon(null);
+			startBtn.setIconTextGap(0);
+			startBtn.setHorizontalAlignment(SwingConstants.CENTER);
+			startBtn.setHorizontalTextPosition(SwingConstants.CENTER);
+			startBtn.setVerticalTextPosition(SwingConstants.CENTER);
+		}
+		if (stopBtn != null) {
+			handheldStopButtonOriginalBackground = stopBtn.getBackground();
+			handheldStopButtonOriginalForeground = stopBtn.getForeground();
+			stopBtn.setIcon(null);
+			stopBtn.setIconTextGap(0);
+			stopBtn.setHorizontalAlignment(SwingConstants.CENTER);
+			stopBtn.setHorizontalTextPosition(SwingConstants.CENTER);
+			stopBtn.setVerticalTextPosition(SwingConstants.CENTER);
+			stopBtn.setText("缁撴潫");
+		}
+		startHandheldCaptureStatusTimer();
+		refreshHandheldCaptureUiState();
+	}
+
+	private void exitHandheldCaptureInlineUi() {
+		if (!handheldCaptureInlineUiActive) {
+			return;
+		}
+		handheldCaptureInlineUiActive = false;
+		stopHandheldCaptureStatusTimer();
+		if (statusLabel != null) {
+			statusLabel.setText(handheldCaptureStoredStatusText != null ? handheldCaptureStoredStatusText : "寰呮満");
+		}
+		if (startBtn != null) {
+			startBtn.setToolTipText(null);
+			if (handheldStartButtonOriginalBackground != null) {
+				startBtn.setBackground(handheldStartButtonOriginalBackground);
+			}
+			if (handheldStartButtonOriginalForeground != null) {
+				startBtn.setForeground(handheldStartButtonOriginalForeground);
+			}
+			startBtn.setEnabled(true);
+			updateStartButtonAppearance();
+		}
+		if (stopBtn != null) {
+			stopBtn.setToolTipText(null);
+			if (handheldStopButtonOriginalBackground != null) {
+				stopBtn.setBackground(handheldStopButtonOriginalBackground);
+			}
+			if (handheldStopButtonOriginalForeground != null) {
+				stopBtn.setForeground(handheldStopButtonOriginalForeground);
+			}
+			stopBtn.setEnabled(true);
+			stopBtn.setText("缁撴潫");
+			updateStopButtonIcon();
+		}
+		handheldCaptureStoredStatusText = null;
+		handheldStartButtonOriginalBackground = null;
+		handheldStartButtonOriginalForeground = null;
+		handheldStopButtonOriginalBackground = null;
+		handheldStopButtonOriginalForeground = null;
+	}
+
+	private void startHandheldCaptureStatusTimer() {
+		if (handheldCaptureStatusTimer == null) {
+			handheldCaptureStatusTimer = new Timer(400, e -> refreshHandheldCaptureUiState());
+			handheldCaptureStatusTimer.setRepeats(true);
+		}
+		if (!handheldCaptureStatusTimer.isRunning()) {
+			handheldCaptureStatusTimer.start();
+		}
+	}
+
+	private void stopHandheldCaptureStatusTimer() {
+		if (handheldCaptureStatusTimer != null && handheldCaptureStatusTimer.isRunning()) {
+			handheldCaptureStatusTimer.stop();
+		}
+	}
+
+	// Update inline handheld capture buttons based on the current device reading.
+	private void refreshHandheldCaptureUiState() {
+		if (!handheldCaptureInlineUiActive) {
+			return;
+		}
+		int nextIndex = handheldCapturedPoints + 1;
+		boolean hasFix = hasHighPrecisionFix();
+		boolean hasValid = hasValidRealtimeHandheldPosition();
+		boolean duplicate = hasValid && isCurrentHandheldPointDuplicate();
+		boolean canConfirm = handheldCaptureActive && hasFix && hasValid && !duplicate;
+
+		if (startBtn != null) {
+			String prompt = "<html><center>閲囬泦鐐�" + nextIndex + "<br>纭畾</center></html>";
+			startBtn.setText(prompt);
+			startBtn.setEnabled(canConfirm);
+			if (canConfirm) {
+				if (handheldStartButtonOriginalBackground != null) {
+					startBtn.setBackground(handheldStartButtonOriginalBackground);
+				}
+				if (handheldStartButtonOriginalForeground != null) {
+					startBtn.setForeground(handheldStartButtonOriginalForeground);
+				}
+				startBtn.setToolTipText(null);
+			} else {
+				startBtn.setBackground(new Color(200, 200, 200));
+				startBtn.setForeground(new Color(130, 130, 130));
+				startBtn.setToolTipText(resolveHandheldConfirmTooltip(hasFix, hasValid, duplicate));
+			}
+		}
+
+		if (stopBtn != null) {
+			boolean canFinish = handheldCapturedPoints >= 3;
+			stopBtn.setText("缁撴潫");
+			stopBtn.setEnabled(canFinish);
+			if (canFinish) {
+				if (handheldStopButtonOriginalBackground != null) {
+					stopBtn.setBackground(handheldStopButtonOriginalBackground);
+				}
+				if (handheldStopButtonOriginalForeground != null) {
+					stopBtn.setForeground(handheldStopButtonOriginalForeground);
+				}
+				stopBtn.setToolTipText("缁撴潫閲囬泦骞惰繑鍥炴柊澧炲湴鍧�");
+			} else {
+				stopBtn.setBackground(new Color(220, 220, 220));
+				stopBtn.setForeground(new Color(130, 130, 130));
+				stopBtn.setToolTipText("鑷冲皯閲囬泦涓変釜鐐规墠鑳界粨鏉�");
+			}
+		}
+	}
+
+	private String resolveHandheldConfirmTooltip(boolean hasFix, boolean hasValidPosition, boolean duplicate) {
+		if (!hasFix) {
+			return "褰撳墠瀹氫綅璐ㄩ噺涓嶈冻锛屾棤娉曢噰闆�";
+		}
+		if (!hasValidPosition) {
+			return "褰撳墠瀹氫綅鏁版嵁鏃犳晥锛岃绋嶅悗鍐嶈瘯";
+		}
+		if (duplicate) {
+			return "褰撳墠鍧愭爣宸查噰闆嗭紝璇风Щ鍔ㄥ埌鏂扮殑浣嶇疆";
+		}
+		return null;
+	}
+
+	private boolean hasHighPrecisionFix() {
+		Device device = Device.getGecaoji();
+		if (device == null) {
+			return false;
+		}
+		String status = device.getPositioningStatus();
+		return status != null && "4".equals(status.trim());
+	}
+
+	private boolean canConfirmHandheldPoint() {
+		return handheldCaptureActive
+				&& hasHighPrecisionFix()
+				&& hasValidRealtimeHandheldPosition()
+				&& !isCurrentHandheldPointDuplicate();
+	}
+
+	private void enterDrawingControlMode() {
+		if (drawingControlModeActive) {
+			return;
+		}
+		storedStartButtonShowingPause = startButtonShowingPause;
+		storedStopButtonActive = stopButtonActive;
+		storedStatusBeforeDrawing = statusLabel != null ? statusLabel.getText() : null;
+		drawingControlModeActive = true;
+		applyDrawingPauseState(false, false);
+		updateDrawingControlButtonLabels();
+	}
+
+	private void exitDrawingControlMode() {
+		if (!drawingControlModeActive) {
+			return;
+		}
+		drawingControlModeActive = false;
+		applyDrawingPauseState(false, false);
+		drawingPaused = false;
+		stopButtonActive = storedStopButtonActive;
+		startButtonShowingPause = storedStartButtonShowingPause;
+		if (startBtn != null) {
+			updateStartButtonAppearance();
+		}
+		if (stopBtn != null) {
+			stopBtn.setText("缁撴潫");
+			updateStopButtonIcon();
+		}
+		if (statusLabel != null) {
+			statusLabel.setText(storedStatusBeforeDrawing != null ? storedStatusBeforeDrawing : "寰呮満");
+		}
+		storedStatusBeforeDrawing = null;
+	}
+
+	private void updateDrawingControlButtonLabels() {
+		if (!drawingControlModeActive) {
+			return;
+		}
+		configureButtonForDrawingMode(startBtn);
+		configureButtonForDrawingMode(stopBtn);
+		if (startBtn != null) {
+			startBtn.setText(drawingPaused ? "寮�濮嬬粯鍒�" : "鏆傚仠缁樺埗");
+		}
+		if (stopBtn != null) {
+			stopBtn.setText("缁撴潫缁樺埗");
+		}
+	}
+
+	private void configureButtonForDrawingMode(JButton button) {
+		if (button == null) {
+			return;
+		}
+		button.setIcon(null);
+		button.setIconTextGap(0);
+		button.setHorizontalAlignment(SwingConstants.CENTER);
+		button.setHorizontalTextPosition(SwingConstants.CENTER);
+	}
+
+	private void updateStartButtonAppearance() {
+		if (startBtn == null) {
+			return;
+		}
+		String iconPath = startButtonShowingPause ? "image/start0.png" : "image/start1.png";
+		startBtn.setText(startButtonShowingPause ? "鏆傚仠" : "寮�濮�");
+		applyButtonIcon(startBtn, iconPath);
+	}
+
+	private void updateStopButtonIcon() {
+		if (stopBtn == null) {
+			return;
+		}
+		String iconPath = stopButtonActive ? "image/stop1.png" : "image/stop0.png";
+		applyButtonIcon(stopBtn, iconPath);
+	}
+
+	private void toggleBluetoothConnection() {
+		if (bluetoothBtn == null) {
+			return;
+		}
+		// 寮瑰嚭绯荤粺璋冭瘯椤甸潰
+		showDebugDialog();
+	}
+	
+	private void showDebugDialog() {
+		Window parentWindow = SwingUtilities.getWindowAncestor(this);
+		debug debugDialog = new debug(parentWindow, THEME_COLOR);
+		debugDialog.setLocationRelativeTo(this); // 灞呬腑鏄剧ず鍦ㄩ椤�
+		debugDialog.setVisible(true);
+	}
+
+	private void updateBluetoothButtonIcon() {
+		if (bluetoothBtn == null) {
+			return;
+		}
+		ensureBluetoothIconsLoaded();
+		// 鏍规嵁涓插彛杩炴帴鐘舵�佹樉绀哄浘鏍�
+		SerialPortService service = sendmessage.getActiveService();
+		boolean serialConnected = (service != null && service.isOpen());
+		ImageIcon icon = serialConnected ? bluetoothLinkedIcon : bluetoothIcon;
+		if (icon != null) {
+			bluetoothBtn.setIcon(icon);
+			bluetoothBtn.setText(null);
+		} else {
+			bluetoothBtn.setText(serialConnected ? "宸茶繛" : "钃濈墮");
+		}
+	}
+
+	private JPanel createSpeedIndicatorPanel() {
+		JPanel panel = new JPanel(new BorderLayout());
+		panel.setOpaque(false);
+		panel.setBorder(BorderFactory.createEmptyBorder(10, 20, 5, 20));
+
+		JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0));
+		rightPanel.setOpaque(false);
+
+	fixQualityIndicator = new gpszhuangtai(THEME_COLOR);
+		fixQualityIndicator.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+		fixQualityIndicator.addMouseListener(new MouseAdapter() {
+			@Override
+			public void mouseClicked(MouseEvent e) {
+				if (SwingUtilities.isLeftMouseButton(e) && mapRenderer != null) {
+					mapRenderer.showMowerInfo();
+				}
+			}
+		});
+		mowingProgressLabel = new JLabel("--%");
+		mowingProgressLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 12));
+		mowingProgressLabel.setForeground(THEME_COLOR);
+
+		mowerSpeedValueLabel = new JLabel("--");
+		mowerSpeedValueLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
+		mowerSpeedValueLabel.setForeground(THEME_COLOR);
+
+		mowerSpeedUnitLabel = new JLabel("km/h");
+		mowerSpeedUnitLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 9));
+		mowerSpeedUnitLabel.setForeground(THEME_COLOR);
+
+		dataPacketCountLabel = new JLabel("--");
+		dataPacketCountLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 12));
+		dataPacketCountLabel.setForeground(THEME_COLOR);
+
+		rightPanel.add(fixQualityIndicator);
+
+		JSeparator areaSeparator = new JSeparator(SwingConstants.VERTICAL);
+		areaSeparator.setPreferredSize(new Dimension(1, 16));
+		rightPanel.add(areaSeparator);
+
+		rightPanel.add(mowingProgressLabel);
+		JSeparator speedSeparator = new JSeparator(SwingConstants.VERTICAL);
+		speedSeparator.setPreferredSize(new Dimension(1, 16));
+		rightPanel.add(speedSeparator);
+		rightPanel.add(mowerSpeedValueLabel);
+		rightPanel.add(mowerSpeedUnitLabel);
+
+		JSeparator separator = new JSeparator(SwingConstants.VERTICAL);
+		separator.setPreferredSize(new Dimension(1, 16));
+		rightPanel.add(separator);
+
+		rightPanel.add(dataPacketCountLabel);
+
+		panel.add(rightPanel, BorderLayout.EAST);
+		updateFixQualityIndicator();
+		updateDataPacketCountLabel();
+		return panel;
+	}
+
+	private void startMowerSpeedUpdates() {
+		if (mowerSpeedRefreshTimer == null) {
+			mowerSpeedRefreshTimer = new Timer(1000, e -> refreshMowerSpeedLabel());
+			mowerSpeedRefreshTimer.setRepeats(true);
+		}
+		if (!mowerSpeedRefreshTimer.isRunning()) {
+			mowerSpeedRefreshTimer.start();
+		}
+		refreshMowerSpeedLabel();
+	}
+
+	private void refreshMowerSpeedLabel() {
+		if (mowerSpeedValueLabel == null) {
+			return;
+		}
+		String display = "--";
+		Device device = Device.getGecaoji();
+		if (device != null) {
+			String sanitized = sanitizeSpeedValue(device.getRealtimeSpeed());
+			if (sanitized != null) {
+				display = sanitized;
+			}
+		}
+		mowerSpeedValueLabel.setText(display);
+		if (mowerSpeedUnitLabel != null) {
+			mowerSpeedUnitLabel.setText("km/h");
+		}
+		updateMowingProgressLabel();
+		updateFixQualityIndicator();
+		updateDataPacketCountLabel();
+	}
+
+	private void updateDataPacketCountLabel() {
+		if (dataPacketCountLabel == null) {
+			return;
+		}
+		int udpCount = UDPServer.getReceivedPacketCount();
+		int serialCount = dellmessage.getProcessedLineCount();
+		int displayCount = Math.max(udpCount, serialCount);
+
+		if (displayCount <= 0) {
+			dataPacketCountLabel.setText("--");
+			dataPacketCountLabel.setToolTipText(null);
+		} else {
+			dataPacketCountLabel.setText(String.valueOf(displayCount));
+			dataPacketCountLabel.setToolTipText(String.format("涓插彛: %d  UDP: %d", serialCount, udpCount));
+		}
+	}
+
+	private void updateFixQualityIndicator() {
+		if (fixQualityIndicator == null) {
+			return;
+		}
+		Device device = Device.getGecaoji();
+		String code = null;
+		if (device != null) {
+			code = sanitizeDeviceValue(device.getPositioningStatus());
+		}
+		fixQualityIndicator.setQuality(code);
+	}
+
+	private Color resolveFixQualityColor(String code) {
+		if (code == null) {
+			return new Color(160, 160, 160);
+		}
+		switch (code) {
+		case "0":
+			return new Color(160, 160, 160);
+		case "1":
+			return new Color(52, 152, 219);
+		case "2":
+			return new Color(26, 188, 156);
+		case "3":
+			return new Color(155, 89, 182);
+		case "4":
+			return THEME_COLOR;
+		case "5":
+			return new Color(241, 196, 15);
+		case "6":
+			return new Color(231, 76, 60);
+		case "7":
+			return new Color(230, 126, 34);
+		default:
+			return new Color(95, 95, 95);
+		}
+	}
+
+	private String resolveFixQualityDescription(String code) {
+		if (code == null) {
+			return "鏈煡";
+		}
+		switch (code) {
+		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 "浜哄伐杈撳叆鍥哄畾鍊�";
+		default:
+			return "鍏朵粬";
+		}
+	}
+
+	private String sanitizeSpeedValue(String raw) {
+		if (raw == null) {
+			return null;
+		}
+		String trimmed = raw.trim();
+		if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+			return null;
+		}
+		if (trimmed.toLowerCase().endsWith("km/h")) {
+			trimmed = trimmed.substring(0, trimmed.length() - 4).trim();
+		}
+		return trimmed;
+	}
+
+	private String sanitizeDeviceValue(String raw) {
+		if (raw == null) {
+			return null;
+		}
+		String trimmed = raw.trim();
+		if (trimmed.isEmpty() || "-1".equals(trimmed) || "null".equalsIgnoreCase(trimmed)) {
+			return null;
+		}
+		return trimmed;
+	}
+
+	private void updateMowingProgressLabel() {
+		if (mowingProgressLabel == null) {
+			return;
+		}
+		if (mapRenderer == null) {
+			mowingProgressLabel.setText("--%");
+			mowingProgressLabel.setToolTipText(null);
+			return;
+		}
+
+		double totalArea = mapRenderer.getTotalLandAreaSqMeters();
+		double completedArea = mapRenderer.getCompletedMowingAreaSqMeters();
+		double ratio = mapRenderer.getMowingCompletionRatio();
+
+		if (totalArea <= 0) {
+			mowingProgressLabel.setText("--%");
+			mowingProgressLabel.setToolTipText("鏆傛棤鍦板潡闈㈢Н鏁版嵁");
+			return;
+		}
+
+		double percent = Math.max(0.0, Math.min(1.0, ratio)) * 100.0;
+		mowingProgressLabel.setText(String.format(Locale.US, "%.1f%%", percent));
+		mowingProgressLabel.setToolTipText(String.format(Locale.US, "%.1f銕� / %.1f銕�", completedArea, totalArea));
+	}
+
+	public void refreshMowingIndicators() {
+		refreshMowerSpeedLabel();
+	}
+
+	public void setHandheldMowerIconActive(boolean active) {
+		if (mapRenderer == null) {
+			return;
+		}
+		mapRenderer.setHandheldMowerIconActive(active);
+	}
+
+	public boolean startMowerBoundaryCapture() {
+		if (mapRenderer == null) {
+			return false;
+		}
+		double[] baseLatLonCandidate = resolveCircleBaseLatLon();
+		if (baseLatLonCandidate == null) {
+			return false;
+		}
+
+		mapRenderer.clearIdleTrail();
+
+		activeBoundaryMode = BoundaryCaptureMode.MOWER;
+		mowerBoundaryCaptureActive = true;
+		mowerBaseLatLon = baseLatLonCandidate;
+		lastMowerCoordinate = null;
+
+		synchronized (Coordinate.coordinates) {
+			Coordinate.coordinates.clear();
+		}
+		synchronized (mowerTemporaryPoints) {
+			mowerTemporaryPoints.clear();
+		}
+
+		AddDikuai.recordTemporaryBoundaryPoints(Collections.emptyList());
+		Coordinate.setStartSaveGngga(true);
+
+		if (mapRenderer != null) {
+			mapRenderer.setBoundaryPreviewMarkerScale(2.0d);
+			mapRenderer.beginHandheldBoundaryPreview();
+		}
+
+		setHandheldMowerIconActive(false);
+
+		startMowerBoundaryMonitor();
+		return true;
+	}
+
+	public boolean startHandheldBoundaryCapture() {
+		if (mapRenderer == null) {
+			return false;
+		}
+		if (activeBoundaryMode == BoundaryCaptureMode.MOWER) {
+			stopMowerBoundaryCapture();
+		}
+
+		mapRenderer.clearIdleTrail();
+
+		activeBoundaryMode = BoundaryCaptureMode.HANDHELD;
+		handheldCaptureActive = true;
+		handheldCapturedPoints = 0;
+		Coordinate.setStartSaveGngga(false);
+		synchronized (Coordinate.coordinates) {
+			Coordinate.coordinates.clear();
+		}
+		synchronized (handheldTemporaryPoints) {
+			handheldTemporaryPoints.clear();
+		}
+		AddDikuai.recordTemporaryBoundaryPoints(Collections.emptyList());
+		mapRenderer.setBoundaryPreviewMarkerScale(1.0d);
+		mapRenderer.beginHandheldBoundaryPreview();
+		setHandheldMowerIconActive(true);
+		enterHandheldCaptureInlineUi();
+		return true;
+	}
+
+	private void startMowerBoundaryMonitor() {
+		if (mowerBoundaryMonitor == null) {
+			mowerBoundaryMonitor = new Timer(600, e -> pollMowerBoundaryCoordinate());
+			mowerBoundaryMonitor.setRepeats(true);
+		}
+		if (!mowerBoundaryMonitor.isRunning()) {
+			mowerBoundaryMonitor.start();
+		}
+		pollMowerBoundaryCoordinate();
+	}
+
+	private void stopMowerBoundaryMonitor() {
+		if (mowerBoundaryMonitor != null && mowerBoundaryMonitor.isRunning()) {
+			mowerBoundaryMonitor.stop();
+		}
+	}
+
+	private void pollMowerBoundaryCoordinate() {
+		if (!mowerBoundaryCaptureActive) {
+			return;
+		}
+
+		Coordinate latest = getLatestCoordinate();
+		if (latest == null || latest == lastMowerCoordinate) {
+			return;
+		}
+
+		double[] base = mowerBaseLatLon;
+		if (base == null || base.length < 2) {
+			discardLatestCoordinate(latest);
+			lastMowerCoordinate = latest;
+			return;
+		}
+
+		double lat = parseDMToDecimal(latest.getLatitude(), latest.getLatDirection());
+		double lon = parseDMToDecimal(latest.getLongitude(), latest.getLonDirection());
+		if (!Double.isFinite(lat) || !Double.isFinite(lon)) {
+			discardLatestCoordinate(latest);
+			lastMowerCoordinate = latest;
+			return;
+		}
+
+		double[] local = convertLatLonToLocal(lat, lon, base[0], base[1]);
+		Point2D.Double candidate = new Point2D.Double(local[0], local[1]);
+		if (!Double.isFinite(candidate.x) || !Double.isFinite(candidate.y)) {
+			discardLatestCoordinate(latest);
+			lastMowerCoordinate = latest;
+			return;
+		}
+
+		List<Point2D.Double> snapshot;
+		synchronized (mowerTemporaryPoints) {
+			for (Point2D.Double existing : mowerTemporaryPoints) {
+				if (existing != null && arePointsClose(existing, candidate)) {
+					discardLatestCoordinate(latest);
+					lastMowerCoordinate = latest;
+					return;
+				}
+			}
+			mowerTemporaryPoints.add(candidate);
+			snapshot = new ArrayList<>(mowerTemporaryPoints.size() + 1);
+			for (Point2D.Double point : mowerTemporaryPoints) {
+				if (point != null) {
+					snapshot.add(new Point2D.Double(point.x, point.y));
+				}
+			}
+		}
+
+		ensureClosed(snapshot);
+		AddDikuai.recordTemporaryBoundaryPoints(snapshot);
+		if (mapRenderer != null) {
+			mapRenderer.addHandheldBoundaryPoint(candidate.x, candidate.y);
+		}
+		lastMowerCoordinate = latest;
+	}
+
+	private void stopMowerBoundaryCapture() {
+		stopMowerBoundaryMonitor();
+		mowerBoundaryCaptureActive = false;
+		lastMowerCoordinate = null;
+		mowerBaseLatLon = null;
+		if (mapRenderer != null) {
+			mapRenderer.clearHandheldBoundaryPreview();
+		}
+		Coordinate.setStartSaveGngga(false);
+		if (activeBoundaryMode == BoundaryCaptureMode.MOWER) {
+			activeBoundaryMode = BoundaryCaptureMode.NONE;
+		}
+		setHandheldMowerIconActive(false);
+	}
+
+	private void discardLatestCoordinate(Coordinate coordinate) {
+		if (coordinate == null) {
+			return;
+		}
+		synchronized (Coordinate.coordinates) {
+			int size = Coordinate.coordinates.size();
+			if (size == 0) {
+				return;
+			}
+			int lastIndex = size - 1;
+			if (Coordinate.coordinates.get(lastIndex) == coordinate) {
+				Coordinate.coordinates.remove(lastIndex);
+			} else {
+				Coordinate.coordinates.remove(coordinate);
+			}
+		}
+	}
+
+	private void ensureClosed(List<Point2D.Double> points) {
+		if (points == null || points.size() < 3) {
+			return;
+		}
+		Point2D.Double first = points.get(0);
+		Point2D.Double last = points.get(points.size() - 1);
+		if (first == null || last == null) {
+			return;
+		}
+		if (!arePointsClose(first, last)) {
+			points.add(new Point2D.Double(first.x, first.y));
+		}
+	}
+
+	int captureHandheldBoundaryPoint() {
+		if (!handheldCaptureActive) {
+			return -1;
+		}
+		Device device = Device.getGecaoji();
+		if (device == null) {
+			JOptionPane.showMessageDialog(this, "鏈娴嬪埌閲囬泦璁惧锛岃妫�鏌ヨ繛鎺ャ��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+			return -1;
+		}
+
+		String[] latParts = splitCoordinateComponents(device.getRealtimeLatitude(), true);
+		String[] lonParts = splitCoordinateComponents(device.getRealtimeLongitude(), false);
+		if (latParts == null || lonParts == null) {
+			JOptionPane.showMessageDialog(this, "褰撳墠瀹氫綅鏃犳晥锛岃鍦ㄥ畾浣嶇ǔ瀹氬悗鍐嶈瘯銆�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+			return -1;
+		}
+
+		double x = parseMetersValue(device.getRealtimeX());
+		double y = parseMetersValue(device.getRealtimeY());
+		if (!Double.isFinite(x) || !Double.isFinite(y)) {
+			JOptionPane.showMessageDialog(this, "褰撳墠瀹氫綅鏁版嵁鏃犳晥锛岃绋嶅悗鍐嶈瘯銆�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+			return -1;
+		}
+		if (isDuplicateHandheldPoint(x, y)) {
+			JOptionPane.showMessageDialog(this, "褰撳墠鍧愭爣宸查噰闆嗭紝璇风Щ鍔ㄥ埌鏂扮殑浣嶇疆鍚庡啀璇曘��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+			return -1;
+		}
+
+		double altitude = parseAltitudeValue(device.getRealtimeAltitude());
+		Coordinate coordinate = new Coordinate(latParts[0], latParts[1], lonParts[0], lonParts[1], altitude);
+		synchronized (Coordinate.coordinates) {
+			Coordinate.coordinates.add(coordinate);
+		}
+
+		if (mapRenderer != null) {
+			mapRenderer.addHandheldBoundaryPoint(x, y);
+		}
+
+		List<Point2D.Double> snapshot;
+		synchronized (handheldTemporaryPoints) {
+			handheldTemporaryPoints.add(new Point2D.Double(x, y));
+			snapshot = new ArrayList<>(handheldTemporaryPoints);
+		}
+		AddDikuai.recordTemporaryBoundaryPoints(snapshot);
+
+		handheldCapturedPoints++;
+		return handheldCapturedPoints;
+	}
+
+	boolean finishHandheldBoundaryCapture() {
+		if (!handheldCaptureActive) {
+			return false;
+		}
+		if (handheldCapturedPoints < 3) {
+			JOptionPane.showMessageDialog(this, "鑷冲皯閲囬泦涓変釜鐐规墠鑳界敓鎴愯竟鐣屻��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+			return false;
+		}
+
+		List<Point2D.Double> closedSnapshot = createClosedHandheldPointSnapshot();
+		handheldCaptureActive = false;
+		activeBoundaryMode = BoundaryCaptureMode.NONE;
+		Coordinate.setStartSaveGngga(false);
+		if (mapRenderer != null) {
+			mapRenderer.clearHandheldBoundaryPreview();
+		}
+
+		AddDikuai.recordTemporaryBoundaryPoints(closedSnapshot);
+
+		exitHandheldCaptureInlineUi();
+
+		SwingUtilities.invokeLater(AddDikuai::finishDrawingSession);
+		return true;
+	}
+
+	int getHandheldCapturedPointCount() {
+		return handheldCapturedPoints;
+	}
+
+	public List<Point2D.Double> getHandheldTemporaryPointsSnapshot() {
+		if (activeBoundaryMode == BoundaryCaptureMode.MOWER) {
+			return createClosedMowerPointSnapshot();
+		}
+		if (!handheldCaptureActive) {
+			return createClosedHandheldPointSnapshot();
+		}
+		synchronized (handheldTemporaryPoints) {
+			return new ArrayList<>(handheldTemporaryPoints);
+		}
+	}
+
+	public boolean isCurrentHandheldPointDuplicate() {
+		Device device = Device.getGecaoji();
+		if (device == null) {
+			return false;
+		}
+		double x = parseMetersValue(device.getRealtimeX());
+		double y = parseMetersValue(device.getRealtimeY());
+		if (!Double.isFinite(x) || !Double.isFinite(y)) {
+			return false;
+		}
+		return isDuplicateHandheldPoint(x, y);
+	}
+
+	public boolean hasValidRealtimeHandheldPosition() {
+		Device device = Device.getGecaoji();
+		if (device == null) {
+			return false;
+		}
+		double x = parseMetersValue(device.getRealtimeX());
+		double y = parseMetersValue(device.getRealtimeY());
+		return Double.isFinite(x) && Double.isFinite(y);
+	}
+
+	private boolean isDuplicateHandheldPoint(double x, double y) {
+		Point2D.Double candidate = new Point2D.Double(x, y);
+		synchronized (handheldTemporaryPoints) {
+			for (Point2D.Double existing : handheldTemporaryPoints) {
+				if (existing == null) {
+					continue;
+				}
+				if (arePointsClose(existing, candidate)) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	private boolean arePointsClose(Point2D.Double a, Point2D.Double b) {
+		if (a == null || b == null) {
+			return false;
+		}
+		double dx = a.x - b.x;
+		double dy = a.y - b.y;
+		return Math.hypot(dx, dy) < HANDHELD_DUPLICATE_THRESHOLD_METERS;
+	}
+
+	private List<Point2D.Double> createClosedHandheldPointSnapshot() {
+		List<Point2D.Double> copy = new ArrayList<>();
+		synchronized (handheldTemporaryPoints) {
+			for (Point2D.Double point : handheldTemporaryPoints) {
+				if (point != null) {
+					copy.add(new Point2D.Double(point.x, point.y));
+				}
+			}
+		}
+		ensureClosed(copy);
+		return copy;
+	}
+
+	private List<Point2D.Double> createClosedMowerPointSnapshot() {
+		List<Point2D.Double> copy = new ArrayList<>();
+		synchronized (mowerTemporaryPoints) {
+			for (Point2D.Double point : mowerTemporaryPoints) {
+				if (point != null) {
+					copy.add(new Point2D.Double(point.x, point.y));
+				}
+			}
+		}
+		ensureClosed(copy);
+		return copy;
+	}
+
+	private String[] splitCoordinateComponents(String combined, boolean latitude) {
+		if (combined == null) {
+			return null;
+		}
+		String trimmed = combined.trim();
+		if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+			return null;
+		}
+
+		String valuePart;
+		String directionPart = null;
+		String[] parts = trimmed.split(",");
+		if (parts.length >= 2) {
+			valuePart = parts[0].trim();
+			directionPart = parts[1].trim();
+		} else {
+			valuePart = trimmed;
+		}
+
+		if (valuePart.isEmpty()) {
+			return null;
+		}
+
+		if (directionPart == null || directionPart.isEmpty()) {
+			char lastChar = valuePart.charAt(valuePart.length() - 1);
+			if (Character.isLetter(lastChar)) {
+				directionPart = String.valueOf(lastChar);
+				valuePart = valuePart.substring(0, valuePart.length() - 1).trim();
+			}
+		}
+
+		if (valuePart.isEmpty()) {
+			return null;
+		}
+
+		directionPart = normalizeHemisphere(directionPart, latitude);
+		return new String[]{valuePart, directionPart};
+	}
+
+	private String normalizeHemisphere(String direction, boolean latitude) {
+		if (direction == null || direction.trim().isEmpty()) {
+			return latitude ? "N" : "E";
+		}
+		String normalized = direction.trim().toUpperCase(Locale.ROOT);
+		if (latitude) {
+			if (!"N".equals(normalized) && !"S".equals(normalized)) {
+				return "N";
+			}
+		} else {
+			if (!"E".equals(normalized) && !"W".equals(normalized)) {
+				return "E";
+			}
+		}
+		return normalized;
+	}
+
+	private double parseMetersValue(String raw) {
+		if (raw == null) {
+			return Double.NaN;
+		}
+		String trimmed = raw.trim();
+		if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+			return Double.NaN;
+		}
+		try {
+			return Double.parseDouble(trimmed);
+		} catch (NumberFormatException ex) {
+			return Double.NaN;
+		}
+	}
+
+	private double parseAltitudeValue(String raw) {
+		if (raw == null) {
+			return 0.0;
+		}
+		String trimmed = raw.trim();
+		if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+			return 0.0;
+		}
+		try {
+			return Double.parseDouble(trimmed);
+		} catch (NumberFormatException ex) {
+			return 0.0;
+		}
+	}
+
+	private boolean beginMowingSession() {
+		if (mapRenderer == null) {
+			return false;
+		}
+		String landNumber = Dikuaiguanli.getCurrentWorkLandNumber();
+		if (!isMeaningfulValue(landNumber)) {
+			JOptionPane.showMessageDialog(this, "璇峰厛閫夋嫨鍦板潡鍚庡啀寮�濮嬩綔涓�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+			return false;
+		}
+
+		double widthMeters = resolveMowerWidthMeters(landNumber);
+		if (widthMeters <= 0) {
+			JOptionPane.showMessageDialog(this, "鏈厤缃壊鑽夊搴︼紝灏嗘棤娉曡绠椾綔涓氶潰绉�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+		}
+
+		mapRenderer.startRealtimeTrackRecording(landNumber, widthMeters);
+		refreshMowerSpeedLabel();
+		return true;
+	}
+
+	private void pauseMowingSession() {
+		if (mapRenderer == null) {
+			return;
+		}
+		mapRenderer.pauseRealtimeTrackRecording();
+		refreshMowerSpeedLabel();
+	}
+
+	private void stopMowingSession() {
+		if (mapRenderer == null) {
+			return;
+		}
+		mapRenderer.stopRealtimeTrackRecording();
+		refreshMowerSpeedLabel();
+	}
+
+	private double resolveMowerWidthMeters(String landNumber) {
+		double width = 0.0;
+		if (isMeaningfulValue(landNumber)) {
+			Dikuai current = Dikuai.getDikuai(landNumber);
+			if (current != null) {
+				width = parseMowerWidthMeters(current.getMowingWidth());
+			}
+		}
+		if (width > 0) {
+			return width;
+		}
+		return parseMowerWidthFromDevice();
+	}
+
+	private double parseMowerWidthFromDevice() {
+		Device device = Device.getGecaoji();
+		if (device == null) {
+			return 0.0;
+		}
+		return parseMowerWidthMeters(device.getMowingWidth());
+	}
+
+	private double parseMowerWidthMeters(String raw) {
+		if (raw == null) {
+			return 0.0;
+		}
+		String sanitized = raw.trim().toLowerCase(Locale.ROOT);
+		if (sanitized.isEmpty() || "-1".equals(sanitized)) {
+			return 0.0;
+		}
+		sanitized = sanitized.replace("鍘樼背", "cm");
+		sanitized = sanitized.replace("鍏垎", "cm");
+		sanitized = sanitized.replace("绫�", "m");
+		sanitized = sanitized.replace("cm", "");
+		sanitized = sanitized.replace("m", "");
+		sanitized = sanitized.trim();
+		if (sanitized.isEmpty()) {
+			return 0.0;
+		}
+		try {
+			double value = Double.parseDouble(sanitized);
+			if (value <= 0) {
+				return 0.0;
+			}
+			if (value > 10) {
+				return value / 100.0;
+			}
+			return value;
+		} catch (NumberFormatException ex) {
+			return 0.0;
+		}
+	}
+
+	private void applyStatusLabelColor(String statusText) {
+		if (statusLabel == null) {
+			return;
+		}
+		if ("浣滀笟涓�".equals(statusText) || "缁樺埗涓�".equals(statusText)) {
+			statusLabel.setForeground(THEME_COLOR);
+		} else if ("鏆傚仠涓�".equals(statusText) || "缁樺埗鏆傚仠".equals(statusText)) {
+			statusLabel.setForeground(STATUS_PAUSE_COLOR);
+		} else {
+			statusLabel.setForeground(Color.GRAY);
+		}
+	}
+
+	private void ensureBluetoothIconsLoaded() {
+		if (bluetoothIcon == null) {
+			bluetoothIcon = loadScaledIcon("image/blue.png", 28, 28);
+		}
+		if (bluetoothLinkedIcon == null) {
+			bluetoothLinkedIcon = loadScaledIcon("image/bluelink.png", 28, 28);
+		}
+	}
+
+	private JButton createFloatingIconButton() {
+		JButton button = new JButton();
+		button.setContentAreaFilled(false);
+		button.setBorder(BorderFactory.createEmptyBorder());
+		button.setFocusPainted(false);
+		button.setOpaque(false);
+		button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+		int size = FLOAT_ICON_SIZE + 8;
+		button.setPreferredSize(new Dimension(size, size));
+		return button;
+	}
+
+	private JButton createFloatingTextButton(String text) {
+		JButton button = new JButton(text);
+		button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 15));
+		button.setForeground(Color.WHITE);
+		button.setBackground(THEME_COLOR);
+		button.setBorder(BorderFactory.createEmptyBorder(10, 18, 10, 18));
+		button.setFocusPainted(false);
+		button.setOpaque(true);
+		button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+		button.addMouseListener(new MouseAdapter() {
+			@Override
+			public void mouseEntered(MouseEvent e) {
+				button.setBackground(THEME_HOVER_COLOR);
+			}
+
+			@Override
+			public void mouseExited(MouseEvent e) {
+				button.setBackground(THEME_COLOR);
+			}
+		});
+		return button;
+	}
+
+	private ImageIcon loadScaledIcon(String path, int width, int height) {
+		try {
+			ImageIcon icon = new ImageIcon(path);
+			if (icon.getIconWidth() <= 0 || icon.getIconHeight() <= 0) {
+				return null;
+			}
+			Image scaled = icon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH);
+			return new ImageIcon(scaled);
+		} catch (Exception ex) {
+			System.err.println("鍔犺浇鍥炬爣澶辫触: " + path + " - " + ex.getMessage());
+			return null;
+		}
+	}
+
+	private void ensureFloatingIconsLoaded() {
+		if (pauseIcon == null) {
+			pauseIcon = loadScaledIcon("image/zanting.png", FLOAT_ICON_SIZE, FLOAT_ICON_SIZE);
+		}
+		if (pauseActiveIcon == null) {
+			pauseActiveIcon = loadScaledIcon("image/zantingzhong.png", FLOAT_ICON_SIZE, FLOAT_ICON_SIZE);
+		}
+		if (endIcon == null) {
+			endIcon = loadScaledIcon("image/end.png", FLOAT_ICON_SIZE, FLOAT_ICON_SIZE);
+		}
+	}
+
+	private void updatePauseButtonVisual() {
+		if (drawingPauseButton == null) {
+			return;
+		}
+		if (drawingPaused) {
+			if (pauseActiveIcon != null) {
+				drawingPauseButton.setIcon(pauseActiveIcon);
+				drawingPauseButton.setText(null);
+			} else {
+				drawingPauseButton.setText("鏆傚仠涓�");
+				drawingPauseButton.setIcon(null);
+			}
+			drawingPauseButton.setToolTipText("鐐瑰嚮鎭㈠缁樺埗");
+		} else {
+			if (pauseIcon != null) {
+				drawingPauseButton.setIcon(pauseIcon);
+				drawingPauseButton.setText(null);
+			} else {
+				drawingPauseButton.setText("鏆傚仠");
+				drawingPauseButton.setIcon(null);
+			}
+			drawingPauseButton.setToolTipText("鐐瑰嚮鏆傚仠缁樺埗");
+		}
+	}
+
+	private void applyDrawingPauseState(boolean paused, boolean notifyCoordinate) {
+		drawingPaused = paused;
+		updatePauseButtonVisual();
+		if (notifyCoordinate) {
+			Coordinate.setStartSaveGngga(!paused);
+		}
+		if (drawingControlModeActive) {
+			updateDrawingControlButtonLabels();
+			if (statusLabel != null) {
+				statusLabel.setText(paused ? "缁樺埗鏆傚仠" : "缁樺埗涓�");
+			}
+		}
+	}
+
+	private void toggleDrawingPause() {
+		applyDrawingPauseState(!drawingPaused, true);
+	}
+
+	public void showEndDrawingButton(Runnable callback) {
+		showEndDrawingButton(callback, null);
+	}
+
+	public void showEndDrawingButton(Runnable callback, String drawingShape) {
+		endDrawingCallback = callback;
+		circleDialogMode = false;
+		hideCircleGuidancePanel();
+		enterDrawingControlMode();
+
+		boolean enableCircleGuidance = drawingShape != null
+				&& "circle".equalsIgnoreCase(drawingShape.trim());
+		if (enableCircleGuidance) {
+			ensureFloatingIconsLoaded();
+			ensureFloatingButtonInfrastructure();
+			if (drawingPauseButton != null) {
+				drawingPauseButton.setVisible(false);
+			}
+			if (endDrawingButton != null) {
+				endDrawingButton.setVisible(false);
+			}
+			prepareCircleGuidanceState();
+			showCircleGuidanceStep(1);
+			floatingButtonPanel.setVisible(true);
+			if (floatingButtonPanel.getParent() != visualizationPanel) {
+				visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH);
+			}
+			rebuildFloatingButtonColumn();
+		} else {
+			clearCircleGuidanceArtifacts();
+			hideFloatingDrawingControls();
+		}
+
+		visualizationPanel.revalidate();
+		visualizationPanel.repaint();
+	}
+
+	private void ensureFloatingButtonInfrastructure() {
+		if (endDrawingButton == null) {
+			endDrawingButton = createFloatingIconButton();
+			endDrawingButton.addActionListener(e -> {
+				if (endDrawingCallback != null) {
+					endDrawingCallback.run();
+				}
+			});
+		}
+		if (endIcon != null) {
+			endDrawingButton.setIcon(endIcon);
+			endDrawingButton.setText(null);
+		} else {
+			endDrawingButton.setText("缁撴潫缁樺埗");
+		}
+		endDrawingButton.setToolTipText("缁撴潫缁樺埗");
+
+		if (drawingPauseButton == null) {
+			drawingPauseButton = createFloatingIconButton();
+			drawingPauseButton.addActionListener(e -> toggleDrawingPause());
+		}
+		updatePauseButtonVisual();
+
+		if (floatingButtonPanel == null) {
+			floatingButtonPanel = new JPanel(new BorderLayout());
+			floatingButtonPanel.setOpaque(false);
+			floatingButtonPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 20));
+
+			floatingButtonColumn = new JPanel();
+			floatingButtonColumn.setOpaque(false);
+			floatingButtonColumn.setLayout(new BoxLayout(floatingButtonColumn, BoxLayout.Y_AXIS));
+			floatingButtonPanel.add(floatingButtonColumn, BorderLayout.EAST);
+		}
+	}
+
+	private void hideFloatingDrawingControls() {
+		if (drawingPauseButton != null) {
+			drawingPauseButton.setVisible(false);
+		}
+		if (endDrawingButton != null) {
+			endDrawingButton.setVisible(false);
+		}
+		if (floatingButtonPanel != null) {
+			floatingButtonPanel.setVisible(false);
+		}
+		if (!circleDialogMode) {
+			rebuildFloatingButtonColumn();
+		}
+	}
+
+	private void rebuildFloatingButtonColumn() {
+		if (floatingButtonColumn == null) {
+			return;
+		}
+		floatingButtonColumn.removeAll();
+		boolean added = false;
+		if (!circleDialogMode && circleGuidancePanel != null && circleGuidancePanel.isVisible()) {
+			floatingButtonColumn.add(circleGuidancePanel);
+			added = true;
+		}
+		if (!circleDialogMode && drawingPauseButton != null && drawingPauseButton.isVisible()) {
+			if (added) {
+				floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
+			}
+			floatingButtonColumn.add(drawingPauseButton);
+			added = true;
+		}
+		if (!circleDialogMode && endDrawingButton != null && endDrawingButton.isVisible()) {
+			if (added) {
+				floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
+			}
+			floatingButtonColumn.add(endDrawingButton);
+			added = true;
+		}
+		if (pathPreviewReturnButton != null && pathPreviewReturnButton.isVisible()) {
+			if (added) {
+				floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
+			}
+			floatingButtonColumn.add(pathPreviewReturnButton);
+			added = true;
+		}
+		floatingButtonColumn.revalidate();
+		floatingButtonColumn.repaint();
+	}
+
+	private void showCircleGuidanceStep(int step) {
+		ensureCircleGuidancePanel();
+		if (circleGuidancePanel == null) {
+			return;
+		}
+		circleGuidanceStep = step;
+
+		if (step == 1) {
+			circleGuidanceLabel.setText("閲囬泦绗�1涓偣");
+			circleGuidancePrimaryButton.setText("纭绗�1鐐�");
+			circleGuidanceSecondaryButton.setText("杩斿洖");
+			circleGuidanceSecondaryButton.setVisible(true);
+		} else if (step == 2) {
+			circleGuidanceLabel.setText("閲囬泦绗�2涓偣");
+			circleGuidancePrimaryButton.setText("纭绗�2鐐�");
+			circleGuidanceSecondaryButton.setText("杩斿洖");
+			circleGuidanceSecondaryButton.setVisible(true);
+		} else if (step == 3) {
+			circleGuidanceLabel.setText("閲囬泦绗�3涓偣");
+			circleGuidancePrimaryButton.setText("纭绗�3鐐�");
+			circleGuidanceSecondaryButton.setText("杩斿洖");
+			circleGuidanceSecondaryButton.setVisible(true);
+		} else if (step == 4) {
+			circleGuidanceLabel.setText("宸查噰闆嗕笁涓偣");
+			circleGuidancePrimaryButton.setText("缁撴潫缁樺埗");
+			circleGuidanceSecondaryButton.setText("閲嶆柊閲囬泦");
+			circleGuidanceSecondaryButton.setVisible(true);
+		} else {
+			hideCircleGuidancePanel();
+			return;
+		}
+		circleGuidancePanel.setVisible(true);
+
+		refreshCircleGuidanceButtonAvailability();
+
+		if (circleDialogMode) {
+			ensureCircleGuidanceDialog();
+			if (circleGuidanceDialog != null) {
+				circleGuidanceDialog.pack();
+				positionCircleGuidanceDialog();
+				circleGuidanceDialog.setVisible(true);
+				circleGuidanceDialog.toFront();
+			}
+		} else {
+			rebuildFloatingButtonColumn();
+		}
+	}
+
+	private void ensureCircleGuidancePanel() {
+		if (circleGuidancePanel != null) {
+			return;
+		}
+		circleGuidancePanel = new JPanel();
+		circleGuidancePanel.setLayout(new BoxLayout(circleGuidancePanel, BoxLayout.Y_AXIS));
+		circleGuidancePanel.setOpaque(true);
+		circleGuidancePanel.setBackground(new Color(255, 255, 255, 235));
+		circleGuidancePanel.setBorder(BorderFactory.createCompoundBorder(
+				BorderFactory.createLineBorder(THEME_COLOR, 1),
+				BorderFactory.createEmptyBorder(10, 12, 10, 12)));
+		circleGuidancePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+		circleGuidanceLabel = new JLabel();
+		circleGuidanceLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 13));
+		circleGuidanceLabel.setForeground(new Color(33, 37, 41));
+		circleGuidanceLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+		JPanel buttonRow = new JPanel();
+		buttonRow.setLayout(new BoxLayout(buttonRow, BoxLayout.X_AXIS));
+		buttonRow.setOpaque(false);
+		buttonRow.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+		circleGuidancePrimaryButton = createGuidanceButton("鏄�", THEME_COLOR, Color.WHITE, true);
+		circleGuidancePrimaryButton.addActionListener(e -> onCircleGuidancePrimary());
+
+		circleGuidanceSecondaryButton = createGuidanceButton("鍚�", Color.WHITE, THEME_COLOR, false);
+		circleGuidanceSecondaryButton.addActionListener(e -> onCircleGuidanceSecondary());
+
+		buttonRow.add(circleGuidancePrimaryButton);
+		buttonRow.add(Box.createRigidArea(new Dimension(8, 0)));
+		buttonRow.add(circleGuidanceSecondaryButton);
+
+		circleGuidancePanel.add(circleGuidanceLabel);
+		circleGuidancePanel.add(Box.createRigidArea(new Dimension(0, 8)));
+		circleGuidancePanel.add(buttonRow);
+		circleGuidancePanel.setVisible(false);
+	}
+
+	private void ensureCircleGuidanceDialog() {
+		if (circleGuidancePanel == null) {
+			return;
+		}
+		if (circleGuidanceDialog != null) {
+			if (circleGuidancePanel.getParent() != circleGuidanceDialog.getContentPane()) {
+				detachCircleGuidancePanel();
+				circleGuidanceDialog.getContentPane().removeAll();
+				circleGuidanceDialog.getContentPane().add(circleGuidancePanel, BorderLayout.CENTER);
+				circleGuidanceDialog.pack();
+			}
+			return;
+		}
+
+		Window owner = SwingUtilities.getWindowAncestor(this);
+		circleGuidanceDialog = new JDialog(owner, "缁樺埗鎻愮ず", Dialog.ModalityType.MODELESS);
+		circleGuidanceDialog.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
+		circleGuidanceDialog.setResizable(false);
+		circleGuidanceDialog.setAlwaysOnTop(true);
+
+		if (owner != null && circleDialogOwnerAdapter == null) {
+			circleDialogOwnerAdapter = new ComponentAdapter() {
+				@Override
+				public void componentMoved(ComponentEvent e) {
+					positionCircleGuidanceDialog();
+				}
+
+				@Override
+				public void componentResized(ComponentEvent e) {
+					positionCircleGuidanceDialog();
+				}
+			};
+			owner.addComponentListener(circleDialogOwnerAdapter);
+		}
+
+		detachCircleGuidancePanel();
+		circleGuidanceDialog.getContentPane().setLayout(new BorderLayout());
+		circleGuidanceDialog.getContentPane().add(circleGuidancePanel, BorderLayout.CENTER);
+		circleGuidanceDialog.pack();
+	}
+
+	private void detachCircleGuidancePanel() {
+		if (circleGuidancePanel == null) {
+			return;
+		}
+		Container parent = circleGuidancePanel.getParent();
+		if (parent != null) {
+			parent.remove(circleGuidancePanel);
+			parent.revalidate();
+			parent.repaint();
+		}
+	}
+
+	private void positionCircleGuidanceDialog() {
+		if (circleGuidanceDialog == null) {
+			return;
+		}
+		Window owner = SwingUtilities.getWindowAncestor(this);
+		if (owner == null || !owner.isShowing()) {
+			return;
+		}
+		try {
+			Point ownerLocation = owner.getLocationOnScreen();
+			int x = ownerLocation.x + owner.getWidth() - circleGuidanceDialog.getWidth() - 30;
+			int y = ownerLocation.y + owner.getHeight() - circleGuidanceDialog.getHeight() - 40;
+			x = Math.max(ownerLocation.x, x);
+			y = Math.max(ownerLocation.y, y);
+			circleGuidanceDialog.setLocation(x, y);
+		} catch (IllegalComponentStateException ex) {
+			// Owner not yet displayable; skip positioning
+		}
+	}
+
+	private JButton createGuidanceButton(String text, Color bg, Color fg, boolean filled) {
+		JButton button = new JButton(text);
+		button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 12));
+		button.setForeground(fg);
+		button.setBackground(bg);
+		button.setOpaque(true);
+		button.setFocusPainted(false);
+		button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+		button.setAlignmentX(Component.LEFT_ALIGNMENT);
+		if (filled) {
+			button.setBorder(BorderFactory.createEmptyBorder(6, 14, 6, 14));
+		} else {
+			button.setBackground(Color.WHITE);
+			button.setOpaque(true);
+			button.setBorder(BorderFactory.createCompoundBorder(
+					BorderFactory.createLineBorder(THEME_COLOR, 1),
+					BorderFactory.createEmptyBorder(5, 12, 5, 12)));
+		}
+		button.addMouseListener(new MouseAdapter() {
+			@Override
+			public void mouseEntered(MouseEvent e) {
+				if (!button.isEnabled()) {
+					return;
+				}
+				if (filled) {
+					button.setBackground(THEME_HOVER_COLOR);
+				} else {
+					button.setBackground(new Color(245, 245, 245));
+				}
+			}
+
+			@Override
+			public void mouseExited(MouseEvent e) {
+				if (!button.isEnabled()) {
+					return;
+				}
+				if (filled) {
+					button.setBackground(bg);
+				} else {
+					button.setBackground(Color.WHITE);
+				}
+			}
+		});
+		return button;
+	}
+
+	private void onCircleGuidancePrimary() {
+		if (circleGuidanceStep >= 1 && circleGuidanceStep <= 3) {
+			if (!recordCircleSamplePoint()) {
+				return;
+			}
+			if (circleGuidanceStep == 3) {
+				showCircleGuidanceStep(4);
+			} else {
+				showCircleGuidanceStep(circleGuidanceStep + 1);
+			}
+		} else if (circleGuidanceStep == 4) {
+			handleCircleCompletion();
+		}
+	}
+
+	private void onCircleGuidanceSecondary() {
+		if (circleGuidanceStep == 1 || circleGuidanceStep == 2 || circleGuidanceStep == 3) {
+			handleCircleAbort(null);
+		} else if (circleGuidanceStep == 4) {
+			restartCircleCapture();
+		}
+	}
+
+	private void hideCircleGuidancePanel() {
+		circleGuidanceStep = 0;
+		if (circleGuidancePanel != null) {
+			circleGuidancePanel.setVisible(false);
+		}
+		if (circleGuidanceDialog != null) {
+			circleGuidanceDialog.setVisible(false);
+		}
+		if (!circleDialogMode) {
+			rebuildFloatingButtonColumn();
+		}
+	}
+
+	private void handleCircleAbort(String message) {
+		// Return the wizard to step 2 without committing any captured points
+		hideEndDrawingButton();
+		addzhangaiwu.abortCircleDrawingAndReturn(message);
+	}
+
+	private void handleCircleCompletion() {
+		hideCircleGuidancePanel();
+		clearCircleGuidanceArtifacts();
+		if (endDrawingCallback != null) {
+			endDrawingCallback.run();
+		} else {
+			addzhangaiwu.finishDrawingSession();
+		}
+	}
+
+	private void prepareCircleGuidanceState() {
+		clearCircleGuidanceArtifacts();
+		circleBaseLatLon = resolveCircleBaseLatLon();
+		startCircleDataMonitor();
+	}
+
+	private void clearCircleGuidanceArtifacts() {
+		stopCircleDataMonitor();
+		circleCapturedPoints.clear();
+		circleBaseLatLon = null;
+		lastCapturedCoordinate = null;
+		if (mapRenderer != null) {
+			mapRenderer.clearCircleCaptureOverlay();
+			mapRenderer.clearCircleSampleMarkers();
+		}
+	}
+
+	private void restartCircleCapture() {
+		clearCircleGuidanceArtifacts();
+		synchronized (Coordinate.coordinates) {
+			Coordinate.coordinates.clear();
+		}
+		Coordinate.setStartSaveGngga(true);
+		circleBaseLatLon = resolveCircleBaseLatLon();
+		showCircleGuidanceStep(1);
+		startCircleDataMonitor();
+	}
+
+	private boolean recordCircleSamplePoint() {
+		Coordinate latest = getLatestCoordinate();
+		if (latest == null) {
+			JOptionPane.showMessageDialog(this, "鏈幏鍙栧埌褰撳墠浣嶇疆鍧愭爣锛岃绋嶅悗閲嶈瘯銆�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+			return false;
+		}
+
+		double[] base = ensureCircleBaseLatLon();
+		if (base == null) {
+			JOptionPane.showMessageDialog(this, "鍩哄噯绔欏潗鏍囨棤鏁堬紝璇峰厛鍦ㄥ熀鍑嗙珯绠$悊涓畬鎴愯缃��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+			return false;
+		}
+
+		double lat = parseDMToDecimal(latest.getLatitude(), latest.getLatDirection());
+		double lon = parseDMToDecimal(latest.getLongitude(), latest.getLonDirection());
+		if (!Double.isFinite(lat) || !Double.isFinite(lon)) {
+			JOptionPane.showMessageDialog(this, "閲囬泦鐐瑰潗鏍囨棤鏁堬紝璇烽噸鏂伴噰闆嗐��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+			return false;
+		}
+
+		double[] local = convertLatLonToLocal(lat, lon, base[0], base[1]);
+		circleCapturedPoints.add(local);
+		if (mapRenderer != null) {
+			mapRenderer.updateCircleSampleMarkers(circleCapturedPoints);
+		}
+		lastCapturedCoordinate = latest;
+		refreshCircleGuidanceButtonAvailability();
+
+		if (circleCapturedPoints.size() >= 3) {
+			CircleSolution solution = fitCircleFromPoints(circleCapturedPoints);
+			if (solution == null) {
+				circleCapturedPoints.remove(circleCapturedPoints.size() - 1);
+				JOptionPane.showMessageDialog(this, "鏃犳硶鏍规嵁褰撳墠涓変釜鐐圭敓鎴愬渾锛岃閲嶆柊閲囬泦鐐广��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+				restartCircleCapture();
+				return false;
+			}
+			if (mapRenderer != null) {
+				mapRenderer.showCircleCaptureOverlay(solution.centerX, solution.centerY, solution.radius, circleCapturedPoints);
+			}
+		}
+
+		return true;
+	}
+
+	private void startCircleDataMonitor() {
+		if (circleDataMonitor == null) {
+			circleDataMonitor = new Timer(600, e -> refreshCircleGuidanceButtonAvailability());
+			circleDataMonitor.setRepeats(true);
+		}
+		if (!circleDataMonitor.isRunning()) {
+			circleDataMonitor.start();
+		}
+		refreshCircleGuidanceButtonAvailability();
+	}
+
+	private void stopCircleDataMonitor() {
+		if (circleDataMonitor != null) {
+			circleDataMonitor.stop();
+		}
+	}
+
+	private void refreshCircleGuidanceButtonAvailability() {
+		if (circleGuidancePrimaryButton == null) {
+			return;
+		}
+		boolean shouldEnable = circleGuidanceStep >= 1 && circleGuidanceStep <= 3
+				? isCircleDataAvailable()
+						: true;
+		applyCirclePrimaryButtonState(shouldEnable);
+	}
+
+	private boolean isCircleDataAvailable() {
+		Coordinate latest = getLatestCoordinate();
+		if (latest == null) {
+			return false;
+		}
+		
+		// 妫�鏌ユ槸鍚︽湁鏂扮殑鍧愭爣锛堜笌涓婃閲囬泦鐨勪笉鍚岋級
+		if (lastCapturedCoordinate != null && latest == lastCapturedCoordinate) {
+			return false;
+		}
+		
+		// 妫�鏌ュ畾浣嶇姸鎬佹槸鍚︿负4锛堝浐瀹氳В锛�
+		// 褰撻�夋嫨鍓茶崏鏈虹粯鍒跺渾褰㈤殰纰嶇墿鏃讹紝闇�瑕佹鏌ヨ澶囩紪鍙峰拰瀹氫綅鐘舵��
+		Device device = Device.getGecaoji();
+		if (device == null) {
+			return false;
+		}
+		
+		String positioningStatus = device.getPositioningStatus();
+		if (positioningStatus == null || !"4".equals(positioningStatus.trim())) {
+			return false;
+		}
+		
+		// 妫�鏌ヨ澶囩紪鍙锋槸鍚﹀尮閰嶅壊鑽夋満缂栧彿
+		String mowerId = Setsys.getPropertyValue("mowerId");
+		String deviceId = device.getMowerNumber();
+		if (mowerId != null && !mowerId.trim().isEmpty()) {
+			if (deviceId == null || !mowerId.trim().equals(deviceId.trim())) {
+				return false;
+			}
+		}
+		
+		return true;
+	}
+
+	private void applyCirclePrimaryButtonState(boolean enabled) {
+		if (circleGuidancePrimaryButton == null) {
+			return;
+		}
+		if (circleGuidanceStep >= 4) {
+			enabled = true;
+		}
+		circleGuidancePrimaryButton.setEnabled(enabled);
+		if (enabled) {
+			circleGuidancePrimaryButton.setBackground(THEME_COLOR);
+			circleGuidancePrimaryButton.setForeground(Color.WHITE);
+			circleGuidancePrimaryButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+		} else {
+			circleGuidancePrimaryButton.setBackground(new Color(200, 200, 200));
+			circleGuidancePrimaryButton.setForeground(new Color(120, 120, 120));
+			circleGuidancePrimaryButton.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+		}
+	}
+
+	private Coordinate getLatestCoordinate() {
+		synchronized (Coordinate.coordinates) {
+			int size = Coordinate.coordinates.size();
+			if (size == 0) {
+				return null;
+			}
+			return Coordinate.coordinates.get(size - 1);
+		}
+	}
+
+	private double[] ensureCircleBaseLatLon() {
+		if (circleBaseLatLon == null) {
+			circleBaseLatLon = resolveCircleBaseLatLon();
+		}
+		return circleBaseLatLon;
+	}
+
+	private double[] resolveCircleBaseLatLon() {
+		String coords = null;
+
+		if (baseStation == null) {
+			baseStation = new BaseStation();
+		}
+		baseStation.load();
+		coords = baseStation.getInstallationCoordinates();
+
+		if (!isMeaningfulValue(coords)) {
+			String landNumber = Dikuaiguanli.getCurrentWorkLandNumber();
+			if (isMeaningfulValue(landNumber)) {
+				Dikuai current = Dikuai.getDikuai(landNumber);
+				if (current != null) {
+					coords = current.getBaseStationCoordinates();
+				}
+			}
+			if (!isMeaningfulValue(coords)) {
+				coords = addzhangaiwu.getActiveSessionBaseStation();
+			}
+			if (!isMeaningfulValue(coords)) {
+				return null;
+			}
+		}
+		String[] parts = coords.split(",");
+		if (parts.length < 4) {
+			return null;
+		}
+		double baseLat = parseDMToDecimal(parts[0], parts[1]);
+		double baseLon = parseDMToDecimal(parts[2], parts[3]);
+		if (!Double.isFinite(baseLat) || !Double.isFinite(baseLon)) {
+			return null;
+		}
+		return new double[]{baseLat, baseLon};
+	}
+
+	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;
+		}
+	}
+
+	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};
+	}
+
+	private CircleSolution fitCircleFromPoints(List<double[]> points) {
+		if (points == null || points.size() < 3) {
+			return null;
+		}
+		CircleSolution best = null;
+		double bestScore = 0.0;
+		int n = points.size();
+		for (int i = 0; i < n - 2; i++) {
+			double[] p1 = points.get(i);
+			for (int j = i + 1; j < n - 1; j++) {
+				double[] p2 = points.get(j);
+				for (int k = j + 1; k < n; k++) {
+					double[] p3 = points.get(k);
+					CircleSolution candidate = circleFromThreePoints(p1, p2, p3);
+					if (candidate == null || candidate.radius <= 0) {
+						continue;
+					}
+					double minEdge = Math.min(distance(p1, p2), Math.min(distance(p2, p3), distance(p1, p3)));
+					if (minEdge > bestScore) {
+						bestScore = minEdge;
+						best = candidate;
+					}
+				}
+			}
+		}
+		return best;
+	}
+
+	private CircleSolution circleFromThreePoints(double[] p1, double[] p2, double[] p3) {
+		if (p1 == null || p2 == null || p3 == null) {
+			return null;
+		}
+		double x1 = p1[0];
+		double y1 = p1[1];
+		double x2 = p2[0];
+		double y2 = p2[1];
+		double x3 = p3[0];
+		double y3 = p3[1];
+
+		double a = x1 * (y2 - y3) - y1 * (x2 - x3) + x2 * y3 - x3 * y2;
+		double d = 2.0 * a;
+		if (Math.abs(d) < 1e-6) {
+			return null;
+		}
+
+		double x1Sq = x1 * x1 + y1 * y1;
+		double x2Sq = x2 * x2 + y2 * y2;
+		double x3Sq = x3 * x3 + y3 * y3;
+
+		double centerX = (x1Sq * (y2 - y3) + x2Sq * (y3 - y1) + x3Sq * (y1 - y2)) / d;
+		double centerY = (x1Sq * (x3 - x2) + x2Sq * (x1 - x3) + x3Sq * (x2 - x1)) / d;
+		double radius = Math.hypot(centerX - x1, centerY - y1);
+		if (!Double.isFinite(centerX) || !Double.isFinite(centerY) || !Double.isFinite(radius)) {
+			return null;
+		}
+		if (radius < 0.05) {
+			return null;
+		}
+		return new CircleSolution(centerX, centerY, radius);
+	}
+
+	private double distance(double[] a, double[] b) {
+		if (a == null || b == null) {
+			return 0.0;
+		}
+		double dx = a[0] - b[0];
+		double dy = a[1] - b[1];
+		return Math.hypot(dx, dy);
+	}
+
+	private static final class CircleSolution {
+		final double centerX;
+		final double centerY;
+		final double radius;
+
+		CircleSolution(double centerX, double centerY, double radius) {
+			this.centerX = centerX;
+			this.centerY = centerY;
+			this.radius = radius;
+		}
+	}
+
+	public void hideEndDrawingButton() {
+		hideCircleGuidancePanel();
+		clearCircleGuidanceArtifacts();
+		hideFloatingDrawingControls();
+		circleDialogMode = false;
+		exitHandheldCaptureInlineUi();
+		handheldCaptureActive = false;
+		exitDrawingControlMode();
+		if (activeBoundaryMode == BoundaryCaptureMode.MOWER) {
+			stopMowerBoundaryCapture();
+		} else if (activeBoundaryMode == BoundaryCaptureMode.HANDHELD && !handheldCaptureActive) {
+			activeBoundaryMode = BoundaryCaptureMode.NONE;
+		}
+		endDrawingCallback = null;
+		visualizationPanel.revalidate();
+		visualizationPanel.repaint();
+		setHandheldMowerIconActive(false);
+	}
+
+	private void showPathPreviewReturnControls() {
+		ensureFloatingButtonInfrastructure();
+		if (drawingPauseButton != null) {
+			drawingPauseButton.setVisible(false);
+		}
+		if (endDrawingButton != null) {
+			endDrawingButton.setVisible(false);
+		}
+		if (pathPreviewReturnButton == null) {
+			pathPreviewReturnButton = createFloatingTextButton("杩斿洖");
+			pathPreviewReturnButton.setToolTipText("杩斿洖鏂板鍦板潡姝ラ");
+			pathPreviewReturnButton.addActionListener(e -> handlePathPreviewReturn());
+		}
+		pathPreviewReturnButton.setVisible(true);
+		if (floatingButtonPanel != null) {
+			floatingButtonPanel.setVisible(true);
+			if (floatingButtonPanel.getParent() != visualizationPanel) {
+				visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH);
+			}
+		}
+		rebuildFloatingButtonColumn();
+	}
+
+	private void hidePathPreviewReturnControls() {
+		if (pathPreviewReturnButton != null) {
+			pathPreviewReturnButton.setVisible(false);
+		}
+		rebuildFloatingButtonColumn();
+		if (floatingButtonPanel != null && floatingButtonColumn != null
+				&& floatingButtonColumn.getComponentCount() == 0) {
+			floatingButtonPanel.setVisible(false);
+		}
+	}
+
+	private void handlePathPreviewReturn() {
+		Runnable callback = pathPreviewReturnAction;
+		exitMowingPathPreview();
+		if (callback != null) {
+			callback.run();
+		}
+	}
+
+	public boolean startMowingPathPreview(String landNumber,
+			String landName,
+			String boundary,
+			String obstacles,
+			String plannedPath,
+			Runnable returnAction) {
+		if (mapRenderer == null) {
+			return false;
+		}
+		// 鍏佽娌℃湁璺緞鐨勯瑙堬紙渚嬪闅滅鐗╅瑙堬級锛屽彧瑕佹湁杩斿洖鍥炶皟鍗冲彲
+		if (!isMeaningfulValue(plannedPath) && returnAction == null) {
+			return false;
+		}
+
+		if (pathPreviewActive) {
+			exitMowingPathPreview();
+		}
+
+		exitDrawingControlMode();
+		hideCircleGuidancePanel();
+		clearCircleGuidanceArtifacts();
+
+		pathPreviewReturnAction = returnAction;
+		pathPreviewActive = true;
+		mapRenderer.setPathPreviewSizingEnabled(true);
+
+		previewRestoreLandNumber = Dikuaiguanli.getCurrentWorkLandNumber();
+		previewRestoreLandName = null;
+		if (isMeaningfulValue(previewRestoreLandNumber)) {
+			Dikuai existing = Dikuai.getDikuai(previewRestoreLandNumber);
+			if (existing != null) {
+				previewRestoreLandName = existing.getLandName();
+			}
+		}
+
+		mapRenderer.setCurrentBoundary(boundary, landNumber, landName);
+		mapRenderer.setCurrentObstacles(obstacles, landNumber);
+		// 鍙湁鍦ㄦ湁璺緞鏃舵墠璁剧疆璺緞
+		if (isMeaningfulValue(plannedPath)) {
+			mapRenderer.setCurrentPlannedPath(plannedPath);
+		} else {
+			mapRenderer.setCurrentPlannedPath(null);
+		}
+		mapRenderer.clearHandheldBoundaryPreview();
+		mapRenderer.setBoundaryPointSizeScale(1.0d);
+		mapRenderer.setBoundaryPointsVisible(isMeaningfulValue(boundary));
+		// 鍚敤闅滅鐗╄竟鐣岀偣鏄剧ず
+		mapRenderer.setObstaclePointsVisible(isMeaningfulValue(obstacles));
+
+		String displayName = isMeaningfulValue(landName) ? landName : landNumber;
+		updateCurrentAreaName(displayName);
+
+		showPathPreviewReturnControls();
+		visualizationPanel.revalidate();
+		visualizationPanel.repaint();
+		return true;
+	}
+
+	public void exitMowingPathPreview() {
+		if (!pathPreviewActive) {
+			return;
+		}
+		pathPreviewActive = false;
+		if (mapRenderer != null) {
+			mapRenderer.setPathPreviewSizingEnabled(false);
+		}
+		hidePathPreviewReturnControls();
+
+		String restoreNumber = previewRestoreLandNumber;
+		String restoreName = previewRestoreLandName;
+		previewRestoreLandNumber = null;
+		previewRestoreLandName = null;
+		pathPreviewReturnAction = null;
+
+		if (restoreNumber != null) {
+			Dikuaiguanli.setCurrentWorkLand(restoreNumber, restoreName);
+		} else if (mapRenderer != null) {
+			mapRenderer.setCurrentBoundary(null, null, null);
+			mapRenderer.setCurrentObstacles((String) null, null);
+			mapRenderer.setCurrentPlannedPath(null);
+			mapRenderer.setBoundaryPointsVisible(false);
+			mapRenderer.setBoundaryPointSizeScale(1.0d);
+			mapRenderer.clearHandheldBoundaryPreview();
+			mapRenderer.resetView();
+			updateCurrentAreaName(null);
+		}
+
+		visualizationPanel.revalidate();
+		visualizationPanel.repaint();
+	}
+
+	/**
+	 * 鑾峰彇鍦板浘娓叉煋鍣ㄥ疄渚�
+	 */
+	public MapRenderer getMapRenderer() {
+		return mapRenderer;
+	}
+
+	public void updateCurrentAreaName(String areaName) {
+		if (areaNameLabel == null) {
+			return;
+		}
+		if (areaName == null || areaName.trim().isEmpty()) {
+			areaNameLabel.setText("鏈�夋嫨鍦板潡");
+		} else {
+			areaNameLabel.setText(areaName);
+		}
+	}
+
+	/**
+	 * 閲嶇疆鍦板浘瑙嗗浘
+	 */
+	public void resetMapView() {
+		if (mapRenderer != null) {
+			mapRenderer.resetView();
+		}
+	}
+
+	private void initializeDefaultAreaSelection() {
+		Dikuai.initFromProperties();
+		String persistedLandNumber = Dikuaiguanli.getPersistedWorkLandNumber();
+		if (persistedLandNumber != null) {
+			Dikuai stored = Dikuai.getDikuai(persistedLandNumber);
+			if (stored != null) {
+				Dikuaiguanli.setCurrentWorkLand(persistedLandNumber, stored.getLandName());
+				return;
+			}
+			Dikuaiguanli.setCurrentWorkLand(null, null);
+		}
+
+		Map<String, Dikuai> all = Dikuai.getAllDikuai();
+		if (all.isEmpty()) {
+			Dikuaiguanli.setCurrentWorkLand(null, null);
+		} else if (all.size() == 1) {
+			Dikuai only = all.values().iterator().next();
+			if (only != null) {
+				Dikuaiguanli.setCurrentWorkLand(only.getLandNumber(), only.getLandName());
+			}
+		}
+	}
+
+	private void refreshMapForSelectedArea() {
+		if (mapRenderer == null) {
+			return;
+		}
+
+		String currentLandNumber = Dikuaiguanli.getCurrentWorkLandNumber();
+		if (isMeaningfulValue(currentLandNumber)) {
+			Dikuai current = Dikuai.getDikuai(currentLandNumber);
+			String landName = current != null ? current.getLandName() : null;
+			Dikuaiguanli.setCurrentWorkLand(currentLandNumber, landName);
+			return;
+		}
+
+		String labelName = areaNameLabel != null ? areaNameLabel.getText() : null;
+		if (!isMeaningfulAreaName(labelName)) {
+			Dikuaiguanli.setCurrentWorkLand(null, null);
+			return;
+		}
+
+		Map<String, Dikuai> all = Dikuai.getAllDikuai();
+		for (Dikuai dikuai : all.values()) {
+			if (dikuai == null) {
+				continue;
+			}
+			String candidateName = dikuai.getLandName();
+			if (candidateName != null && candidateName.trim().equals(labelName.trim())) {
+				Dikuaiguanli.setCurrentWorkLand(dikuai.getLandNumber(), candidateName);
+				return;
+			}
+		}
+
+		Dikuaiguanli.setCurrentWorkLand(null, null);
+	}
+
+	private boolean isMeaningfulValue(String value) {
+		if (value == null) {
+			return false;
+		}
+		String trimmed = value.trim();
+		return !trimmed.isEmpty() && !"-1".equals(trimmed);
+	}
+
+	private boolean isMeaningfulAreaName(String value) {
+		if (value == null) {
+			return false;
+		}
+		String trimmed = value.trim();
+		if (trimmed.isEmpty()) {
+			return false;
+		}
+		return !"鏈�夋嫨鍦板潡".equals(trimmed);
+	}
+
     
-    // 涓婚棰滆壊
-    private final Color THEME_COLOR = new Color(46, 139, 87);
-    private final Color THEME_HOVER_COLOR = new Color(30, 107, 69);
-    private final Color BACKGROUND_COLOR = new Color(250, 250, 250);
-    private final Color PANEL_BACKGROUND = new Color(255, 255, 255);
-    
-    // 缁勪欢
-    private JPanel headerPanel;
-    private JPanel mainContentPanel;
-    private JPanel controlPanel;
-    private JPanel navigationPanel;
-    private JPanel visualizationPanel;
-    
-    // 鎸夐挳
-    private JButton startBtn;
-    private JButton pauseBtn;
-    private JButton legendBtn;
-    private JButton remoteBtn;
-    private JButton areaSelectBtn;
-    private JButton baseStationBtn;
-    
-    // 瀵艰埅鎸夐挳
-    private JButton homeNavBtn;
-    private JButton areasNavBtn;
-    private JButton settingsNavBtn;
-    
-    // 鐘舵�佹樉绀�
-    private JLabel statusLabel;
-    private JLabel areaNameLabel;
-    
-    // 褰撳墠閫変腑鐨勫鑸寜閽�
-    private JButton currentNavButton;
-    
-    // 瀵硅瘽妗嗗紩鐢�
-    private LegendDialog legendDialog;
-    private RemoteControlDialog remoteDialog;
-    private AreaSelectionDialog areaDialog;
-    private BaseStationDialog baseStationDialog;
-    private Sets settingsDialog;
-    private BaseStation baseStation;
-    
-    // 鍦板浘娓叉煋鍣�
-    private MapRenderer mapRenderer;
-    private static final int FLOAT_ICON_SIZE = 32;
-    private JButton endDrawingButton;
-    private JButton drawingPauseButton;
-    private JPanel floatingButtonPanel;
-    private JPanel floatingButtonColumn;
-    private Runnable endDrawingCallback;
-    private boolean drawingPaused;
-    private ImageIcon pauseIcon;
-    private ImageIcon pauseActiveIcon;
-    private ImageIcon endIcon;
-    private JPanel circleGuidancePanel;
-    private JLabel circleGuidanceLabel;
-    private JButton circleGuidancePrimaryButton;
-    private JButton circleGuidanceSecondaryButton;
-    private int circleGuidanceStep;
-    private JDialog circleGuidanceDialog;
-    private boolean circleDialogMode;
-    private ComponentAdapter circleDialogOwnerAdapter;
-    
-    public Shouye() {
-        instance = this;
-        baseStation = new BaseStation();
-        baseStation.load();
-        initializeUI();
-        setupEventHandlers();
-    }
 
-    public static Shouye getInstance() {
-        return instance;
-    }
-    
-    private void initializeUI() {
-        setLayout(new BorderLayout());
-        setBackground(BACKGROUND_COLOR);
-        
-        // 鍒涘缓鍚勪釜闈㈡澘
-        createHeaderPanel();
-        createMainContentPanel();
-        createControlPanel();
-        createNavigationPanel();
-        
-        // 娣诲姞鍒颁富闈㈡澘
-        add(headerPanel, BorderLayout.NORTH);
-        add(mainContentPanel, BorderLayout.CENTER);
-        add(controlPanel, BorderLayout.SOUTH);
-        
-        // 鍒濆鍖栧湴鍥炬覆鏌撳櫒
-        mapRenderer = new MapRenderer(visualizationPanel);
-        
-        // 鍒濆鍖栧璇濇寮曠敤涓簄ull锛屽欢杩熷垱寤�
-        legendDialog = null;
-        remoteDialog = null;
-        areaDialog = null;
-        baseStationDialog = null;
-        settingsDialog = null;
-        
-        // 璁剧疆榛樿鐘舵��
-        setNavigationActive(homeNavBtn);
-
-        initializeDefaultAreaSelection();
-        refreshMapForSelectedArea();
-    }
-    
-    private void createHeaderPanel() {
-        headerPanel = new JPanel(new BorderLayout());
-        headerPanel.setBackground(PANEL_BACKGROUND);
-        headerPanel.setBorder(BorderFactory.createEmptyBorder(15, 20, 15, 20));
-        headerPanel.setPreferredSize(new Dimension(0, 80));
-        
-        // 宸︿晶淇℃伅鍖哄煙
-        JPanel leftInfoPanel = new JPanel(new GridLayout(2, 1));
-        leftInfoPanel.setBackground(PANEL_BACKGROUND);
-        
-        areaNameLabel = new JLabel("鏈�夋嫨鍦板潡");
-        areaNameLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 18));
-        areaNameLabel.setForeground(Color.BLACK);
-        areaNameLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
-        areaNameLabel.addMouseListener(new MouseAdapter() {
-            @Override
-            public void mouseClicked(MouseEvent e) {
-                Dikuaiguanli.showDikuaiManagement(Shouye.this, null);
-            }
-
-            @Override
-            public void mouseEntered(MouseEvent e) {
-                areaNameLabel.setForeground(THEME_COLOR);
-            }
-
-            @Override
-            public void mouseExited(MouseEvent e) {
-                areaNameLabel.setForeground(Color.BLACK);
-            }
-        });
-        
-        statusLabel = new JLabel("寰呮満");
-        statusLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
-        statusLabel.setForeground(Color.GRAY);
-        
-        leftInfoPanel.add(areaNameLabel);
-        leftInfoPanel.add(statusLabel);
-        
-        // 鍙充晶鎿嶄綔鍖哄煙
-        JPanel rightActionPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
-        rightActionPanel.setBackground(PANEL_BACKGROUND);
-        
-        JButton notificationBtn = createIconButton("馃敂", 40);
-        
-        // 淇敼璁剧疆鎸夐挳锛氫娇鐢ㄥ浘鐗囨浛浠nicode鍥炬爣
-        JButton settingsBtn = new JButton();
-        try {
-            ImageIcon settingsIcon = new ImageIcon("image/sets.png");
-            // 璋冩暣鍥剧墖澶у皬浠ラ�傚簲鎸夐挳
-            Image scaledImage = settingsIcon.getImage().getScaledInstance(30, 30, Image.SCALE_SMOOTH);
-            settingsBtn.setIcon(new ImageIcon(scaledImage));
-        } catch (Exception e) {
-            // 濡傛灉鍥剧墖鍔犺浇澶辫触锛屼娇鐢ㄩ粯璁ゆ枃鏈�
-            settingsBtn.setText("璁剧疆");
-            System.err.println("鏃犳硶鍔犺浇璁剧疆鍥炬爣: " + e.getMessage());
-        }
-        settingsBtn.setPreferredSize(new Dimension(40, 40));
-        settingsBtn.setBackground(PANEL_BACKGROUND);
-        settingsBtn.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
-        settingsBtn.setFocusPainted(false);
-        
-        // 娣诲姞鎮仠鏁堟灉
-        settingsBtn.addMouseListener(new MouseAdapter() {
-            public void mouseEntered(MouseEvent e) {
-                settingsBtn.setBackground(new Color(240, 240, 240));
-            }
-            public void mouseExited(MouseEvent e) {
-                settingsBtn.setBackground(PANEL_BACKGROUND);
-            }
-        });
-        
-        // 娣诲姞璁剧疆鎸夐挳浜嬩欢
-        settingsBtn.addActionListener(e -> showSettingsDialog());
-        
-        rightActionPanel.add(notificationBtn);
-        rightActionPanel.add(settingsBtn);
-        
-        headerPanel.add(leftInfoPanel, BorderLayout.WEST);
-        headerPanel.add(rightActionPanel, BorderLayout.EAST);
-    }
-    
-    private void createMainContentPanel() {
-        mainContentPanel = new JPanel(new BorderLayout());
-        mainContentPanel.setBackground(BACKGROUND_COLOR);
-        
-        // 鍙鍖栧尯鍩� - 浣跨敤MapRenderer杩涜缁樺埗
-        visualizationPanel = new JPanel() {
-            @Override
-            protected void paintComponent(Graphics g) {
-                super.paintComponent(g);
-                // 濮旀墭缁橫apRenderer杩涜缁樺埗
-                if (mapRenderer != null) {
-                    mapRenderer.renderMap(g);
-                }
-            }
-        };
-        visualizationPanel.setLayout(new BorderLayout());
-        
-        // 鍒涘缓鍔熻兘鎸夐挳闈㈡澘锛堟斁鍦ㄥ乏涓婅锛�
-        JPanel functionButtonsPanel = new JPanel();
-        functionButtonsPanel.setLayout(new BoxLayout(functionButtonsPanel, BoxLayout.Y_AXIS));
-        functionButtonsPanel.setOpaque(false);
-        functionButtonsPanel.setBorder(BorderFactory.createEmptyBorder(20, 5, 0, 0)); // 宸﹁竟璺濇敼涓�5鍍忕礌
-        
-        legendBtn = createFunctionButton("鍥句緥", "馃摉");
-        baseStationBtn = createFunctionButton("鍩哄噯绔�", "馃摗"); // 璋冩暣鍒扮浜屼綅
-        areaSelectBtn = createFunctionButton("鍦板潡", "馃尶");    // 璋冩暣鍒扮涓変綅
-        remoteBtn = createFunctionButton("閬ユ帶", "馃幃");        // 璋冩暣鍒版渶鍚�
-        
-        functionButtonsPanel.add(legendBtn);
-        functionButtonsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
-        functionButtonsPanel.add(baseStationBtn);
-        functionButtonsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
-        functionButtonsPanel.add(areaSelectBtn);
-        functionButtonsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
-        functionButtonsPanel.add(remoteBtn);
-        
-        visualizationPanel.add(functionButtonsPanel, BorderLayout.WEST);
-        
-        mainContentPanel.add(visualizationPanel, BorderLayout.CENTER);
-    }
-    
-    private void createControlPanel() {
-        controlPanel = new JPanel(new BorderLayout());
-        controlPanel.setBackground(PANEL_BACKGROUND);
-        controlPanel.setBorder(BorderFactory.createEmptyBorder(15, 20, 15, 20));
-        controlPanel.setPreferredSize(new Dimension(0, 100));
-        
-        JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 20, 0));
-        buttonPanel.setBackground(PANEL_BACKGROUND);
-        
-    startBtn = createControlButton("寮�濮�", THEME_COLOR);
-    applyButtonIcon(startBtn, "image/startzuoye.png");
-    pauseBtn = createControlButton("鏆傚仠", Color.ORANGE);
-    applyButtonIcon(pauseBtn, "image/zantingzuoye.png");
-        pauseBtn.setEnabled(false);
-        
-        buttonPanel.add(startBtn);
-        buttonPanel.add(pauseBtn);
-        
-        controlPanel.add(buttonPanel, BorderLayout.CENTER);
-    }
-    
-    private void createNavigationPanel() {
-        navigationPanel = new JPanel(new GridLayout(1, 3));
-        navigationPanel.setBackground(PANEL_BACKGROUND);
-        navigationPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
-        navigationPanel.setPreferredSize(new Dimension(0, 70));
-        
-        homeNavBtn = createNavButton("棣栭〉", "馃彔");
-        areasNavBtn = createNavButton("鍦板潡", "馃尶");
-        settingsNavBtn = createNavButton("璁剧疆", "鈿欙笍");
-        
-        navigationPanel.add(homeNavBtn);
-        navigationPanel.add(areasNavBtn);
-        navigationPanel.add(settingsNavBtn);
-        
-        // 娣诲姞鍒颁富鐣岄潰搴曢儴
-        add(navigationPanel, BorderLayout.SOUTH);
-    }
-    
-    private JButton createIconButton(String icon, int size) {
-        JButton button = new JButton(icon);
-        button.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 20));
-        button.setPreferredSize(new Dimension(size, size));
-        button.setBackground(PANEL_BACKGROUND);
-        button.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
-        button.setFocusPainted(false);
-        
-        // 鎮仠鏁堟灉
-        button.addMouseListener(new MouseAdapter() {
-            public void mouseEntered(MouseEvent e) {
-                button.setBackground(new Color(240, 240, 240));
-            }
-            public void mouseExited(MouseEvent e) {
-                button.setBackground(PANEL_BACKGROUND);
-            }
-        });
-        
-        return button;
-    }
-    
-    private JButton createFunctionButton(String text, String icon) {
-        JButton button = new JButton("<html><center>" + icon + "<br>" + text + "</center></html>");
-        button.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
-        button.setPreferredSize(new Dimension(80, 70));
-        button.setBackground(new Color(0, 0, 0, 0)); // 瀹屽叏閫忔槑鑳屾櫙
-        button.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); // 鍙繚鐣欏唴杈硅窛
-        button.setFocusPainted(false);
-        button.setOpaque(false); // 璁剧疆涓轰笉閫忔槑锛岀‘淇濊儗鏅�忔槑
-        
-        // 鍘绘帀鎮仠鏁堟灉
-        button.addMouseListener(new MouseAdapter() {
-            public void mouseEntered(MouseEvent e) {
-                // 鎮仠鏃朵笉鏀瑰彉浠讳綍鏍峰紡
-            }
-            public void mouseExited(MouseEvent e) {
-                // 鎮仠鏃朵笉鏀瑰彉浠讳綍鏍峰紡
-            }
-        });
-        
-        return button;
-    }
-    
-    private JButton createControlButton(String text, Color color) {
-        JButton button = new JButton(text);
-        button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
-        button.setBackground(color);
-        button.setForeground(Color.WHITE);
-        button.setBorder(BorderFactory.createEmptyBorder(15, 0, 15, 0));
-        button.setFocusPainted(false);
-        
-        // 鎮仠鏁堟灉
-        button.addMouseListener(new MouseAdapter() {
-            public void mouseEntered(MouseEvent e) {
-                if (button.isEnabled()) {
-                    if (color == THEME_COLOR) {
-                        button.setBackground(THEME_HOVER_COLOR);
-                    } else {
-                        button.setBackground(new Color(255, 165, 0));
-                    }
-                }
-            }
-            public void mouseExited(MouseEvent e) {
-                if (button.isEnabled()) {
-                    button.setBackground(color);
-                }
-            }
-        });
-        
-        return button;
-    }
-
-    private void applyButtonIcon(JButton button, String imagePath) {
-        try {
-            ImageIcon icon = new ImageIcon(imagePath);
-            Image scaledImage = icon.getImage().getScaledInstance(28, 28, Image.SCALE_SMOOTH);
-            button.setIcon(new ImageIcon(scaledImage));
-            button.setHorizontalAlignment(SwingConstants.CENTER);
-            button.setIconTextGap(10);
-            button.setHorizontalTextPosition(SwingConstants.RIGHT);
-            button.setVerticalTextPosition(SwingConstants.CENTER);
-        } catch (Exception e) {
-            System.err.println("鏃犳硶鍔犺浇鎸夐挳鍥炬爣: " + imagePath + " -> " + e.getMessage());
-        }
-    }
-    
-    private JButton createNavButton(String text, String icon) {
-        JButton button = new JButton("<html><center>" + icon + "<br>" + text + "</center></html>");
-        button.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
-        button.setBackground(PANEL_BACKGROUND);
-        button.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 5));
-        button.setFocusPainted(false);
-        
-        // 鎮仠鏁堟灉
-        button.addMouseListener(new MouseAdapter() {
-            public void mouseEntered(MouseEvent e) {
-                if (button != currentNavButton) {
-                    button.setBackground(new Color(240, 240, 240));
-                }
-            }
-            public void mouseExited(MouseEvent e) {
-                if (button != currentNavButton) {
-                    button.setBackground(PANEL_BACKGROUND);
-                }
-            }
-        });
-        
-        return button;
-    }
-    
-    private void setNavigationActive(JButton navButton) {
-        // 閲嶇疆鎵�鏈夊鑸寜閽牱寮�
-        for (Component comp : navigationPanel.getComponents()) {
-            if (comp instanceof JButton) {
-                JButton btn = (JButton) comp;
-                btn.setBackground(PANEL_BACKGROUND);
-                btn.setForeground(Color.BLACK);
-            }
-        }
-        
-        // 璁剧疆褰撳墠閫変腑鎸夐挳鏍峰紡
-        navButton.setBackground(THEME_COLOR);
-        navButton.setForeground(Color.WHITE);
-        currentNavButton = navButton;
-    }
-    
-    private void setupEventHandlers() {
-        // 瀵艰埅鎸夐挳浜嬩欢
-        homeNavBtn.addActionListener(e -> setNavigationActive(homeNavBtn));
-        areasNavBtn.addActionListener(e -> setNavigationActive(areasNavBtn));
-        settingsNavBtn.addActionListener(e -> setNavigationActive(settingsNavBtn));
-        
-        // 鍔熻兘鎸夐挳浜嬩欢
-        legendBtn.addActionListener(e -> showLegendDialog());
-        remoteBtn.addActionListener(e -> showRemoteControlDialog());
-        areaSelectBtn.addActionListener(e -> {
-            // 鐐瑰嚮鈥滃湴鍧椻�濈洿鎺ユ墦寮�鍦板潡绠$悊瀵硅瘽妗嗭紙鑻ラ渶瑕佸彲浼犲叆鐗瑰畾鍦板潡缂栧彿锛�
-            Dikuaiguanli.showDikuaiManagement(this, null);
-        });
-        baseStationBtn.addActionListener(e -> showBaseStationDialog());
-        
-        // 鎺у埗鎸夐挳浜嬩欢
-        startBtn.addActionListener(e -> startMowing());
-        pauseBtn.addActionListener(e -> pauseMowing());
-    }
-    
-    private void showSettingsDialog() {
-        if (settingsDialog == null) {
-            Window parentWindow = SwingUtilities.getWindowAncestor(this);
-            if (parentWindow instanceof JFrame) {
-                settingsDialog = new Sets((JFrame) parentWindow, THEME_COLOR);
-            } else if (parentWindow instanceof JDialog) {
-                settingsDialog = new Sets((JDialog) parentWindow, THEME_COLOR);
-            } else {
-                // Fallback to a frameless dialog when no parent is available
-                settingsDialog = new Sets((JFrame) null, THEME_COLOR);
-            }
-        }
-        settingsDialog.setVisible(true);
-    }
-    
-    private void showLegendDialog() {
-        if (legendDialog == null) {
-            Window parentWindow = SwingUtilities.getWindowAncestor(this);
-            if (parentWindow != null) {
-                legendDialog = new LegendDialog(this, THEME_COLOR);
-            } else {
-                // 濡傛灉娌℃湁鐖剁獥鍙o紝鍒涘缓鏃犵埗绐楀彛鐨勫璇濇
-                legendDialog = new LegendDialog((JFrame) null, THEME_COLOR);
-            }
-        }
-        legendDialog.setVisible(true);
-    }
-    
-    private void showRemoteControlDialog() {
-        if (remoteDialog == null) {
-            Window parentWindow = SwingUtilities.getWindowAncestor(this);
-            if (parentWindow != null) {
-                remoteDialog = new RemoteControlDialog(this, THEME_COLOR);
-            } else {
-                remoteDialog = new RemoteControlDialog((JFrame) null, THEME_COLOR);
-            }
-        }
-        remoteDialog.setVisible(true);
-    }
-    
-    private void showAreaSelectionDialog() {
-        if (areaDialog == null) {
-            Window parentWindow = SwingUtilities.getWindowAncestor(this);
-            if (parentWindow != null) {
-                areaDialog = new AreaSelectionDialog(this, THEME_COLOR, areaNameLabel, statusLabel);
-            } else {
-                areaDialog = new AreaSelectionDialog((JFrame) null, THEME_COLOR, areaNameLabel, statusLabel);
-            }
-        }
-        areaDialog.setVisible(true);
-    }
-    
-    private void showBaseStationDialog() {
-        if (baseStation == null) {
-            baseStation = new BaseStation();
-        }
-        baseStation.load();
-
-        Component dialogParent = this;
-
-        if (!hasValidBaseStationId()) {
-            boolean recorded = promptForBaseStationId(dialogParent);
-            if (!recorded) {
-                return;
-            }
-        }
-
-        Device device = new Device();
-        device.initFromProperties();
-
-        if (baseStationDialog == null) {
-            baseStationDialog = new BaseStationDialog(dialogParent, THEME_COLOR, device, baseStation);
-        } else {
-            baseStationDialog.refreshData();
-        }
-        baseStationDialog.setVisible(true);
-    }
-
-    private boolean hasValidBaseStationId() {
-        if (baseStation == null) {
-            return false;
-        }
-        String deviceId = baseStation.getDeviceId();
-        if (deviceId == null) {
-            return false;
-        }
-        String trimmed = deviceId.trim();
-        return !trimmed.isEmpty() && !"-1".equals(trimmed);
-    }
-
-    private boolean promptForBaseStationId(Component parentComponent) {
-        if (baseStation == null) {
-            baseStation = new BaseStation();
-        }
-
-        while (true) {
-            String input = JOptionPane.showInputDialog(parentComponent,
-                    "璇疯緭鍏ュ熀鍑嗙珯缂栧彿", "褰曞叆鍩哄噯绔欑紪鍙�", JOptionPane.PLAIN_MESSAGE);
-
-            if (input == null) {
-                return false;
-            }
-
-            String trimmed = input.trim();
-            if (trimmed.isEmpty()) {
-                JOptionPane.showMessageDialog(parentComponent,
-                        "鍩哄噯绔欑紪鍙蜂笉鑳戒负绌猴紝璇烽噸鏂拌緭鍏ャ��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
-                continue;
-            }
-
-            try {
-                baseStation.updateByDeviceId(trimmed,
-                        baseStation.getInstallationCoordinates(),
-                        baseStation.getIotSimCardNumber(),
-                        baseStation.getDeviceActivationTime(),
-                        baseStation.getDataUpdateTime());
-                baseStation.load();
-                JOptionPane.showMessageDialog(parentComponent,
-                        "鍩哄噯绔欑紪鍙峰凡淇濆瓨銆�", "鎿嶄綔鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
-                return true;
-            } catch (IllegalArgumentException ex) {
-                JOptionPane.showMessageDialog(parentComponent,
-                        ex.getMessage(), "杈撳叆閿欒", JOptionPane.ERROR_MESSAGE);
-            }
-        }
-    }
-    
-    private void startMowing() {
-        statusLabel.setText("浣滀笟涓�");
-        startBtn.setEnabled(false);
-        pauseBtn.setEnabled(true);
-    }
-    
-    private void pauseMowing() {
-        statusLabel.setText("鏆傚仠涓�");
-        startBtn.setEnabled(true);
-        pauseBtn.setEnabled(false);
-    }
-
-    private JButton createFloatingIconButton() {
-        JButton button = new JButton();
-        button.setContentAreaFilled(false);
-        button.setBorder(BorderFactory.createEmptyBorder());
-        button.setFocusPainted(false);
-        button.setOpaque(false);
-        button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
-        int size = FLOAT_ICON_SIZE + 8;
-        button.setPreferredSize(new Dimension(size, size));
-        return button;
-    }
-
-    private ImageIcon loadScaledIcon(String path, int width, int height) {
-        try {
-            ImageIcon icon = new ImageIcon(path);
-            if (icon.getIconWidth() <= 0 || icon.getIconHeight() <= 0) {
-                return null;
-            }
-            Image scaled = icon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH);
-            return new ImageIcon(scaled);
-        } catch (Exception ex) {
-            System.err.println("鍔犺浇鍥炬爣澶辫触: " + path + " - " + ex.getMessage());
-            return null;
-        }
-    }
-
-    private void ensureFloatingIconsLoaded() {
-        if (pauseIcon == null) {
-            pauseIcon = loadScaledIcon("image/zanting.png", FLOAT_ICON_SIZE, FLOAT_ICON_SIZE);
-        }
-        if (pauseActiveIcon == null) {
-            pauseActiveIcon = loadScaledIcon("image/zantingzhong.png", FLOAT_ICON_SIZE, FLOAT_ICON_SIZE);
-        }
-        if (endIcon == null) {
-            endIcon = loadScaledIcon("image/end.png", FLOAT_ICON_SIZE, FLOAT_ICON_SIZE);
-        }
-    }
-
-    private void updatePauseButtonVisual() {
-        if (drawingPauseButton == null) {
-            return;
-        }
-        if (drawingPaused) {
-            if (pauseActiveIcon != null) {
-                drawingPauseButton.setIcon(pauseActiveIcon);
-                drawingPauseButton.setText(null);
-            } else {
-                drawingPauseButton.setText("鏆傚仠涓�");
-                drawingPauseButton.setIcon(null);
-            }
-            drawingPauseButton.setToolTipText("鐐瑰嚮鎭㈠缁樺埗");
-        } else {
-            if (pauseIcon != null) {
-                drawingPauseButton.setIcon(pauseIcon);
-                drawingPauseButton.setText(null);
-            } else {
-                drawingPauseButton.setText("鏆傚仠");
-                drawingPauseButton.setIcon(null);
-            }
-            drawingPauseButton.setToolTipText("鐐瑰嚮鏆傚仠缁樺埗");
-        }
-    }
-
-    private void applyDrawingPauseState(boolean paused, boolean notifyCoordinate) {
-        drawingPaused = paused;
-        updatePauseButtonVisual();
-        if (notifyCoordinate) {
-            Coordinate.setStartSaveGngga(!paused);
-        }
-    }
-
-    private void toggleDrawingPause() {
-        applyDrawingPauseState(!drawingPaused, true);
-    }
-
-    public void showEndDrawingButton(Runnable callback) {
-        showEndDrawingButton(callback, null);
-    }
-
-    public void showEndDrawingButton(Runnable callback, String drawingShape) {
-        endDrawingCallback = callback;
-        applyDrawingPauseState(false, false);
-        circleDialogMode = drawingShape != null && "circle".equalsIgnoreCase(drawingShape);
-
-        if (circleDialogMode) {
-            hideFloatingDrawingControls();
-            ensureCircleGuidancePanel();
-            showCircleGuidanceStep(1);
-            visualizationPanel.revalidate();
-            visualizationPanel.repaint();
-            return;
-        }
-
-        circleDialogMode = false;
-        hideCircleGuidancePanel();
-
-        ensureFloatingIconsLoaded();
-        ensureFloatingButtonInfrastructure();
-
-        endDrawingButton.setVisible(true);
-        if (drawingPauseButton != null) {
-            drawingPauseButton.setVisible(true);
-        }
-
-        floatingButtonPanel.setVisible(true);
-        if (floatingButtonPanel.getParent() != visualizationPanel) {
-            visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH);
-        }
-
-        rebuildFloatingButtonColumn();
-        visualizationPanel.revalidate();
-        visualizationPanel.repaint();
-    }
-
-    private void ensureFloatingButtonInfrastructure() {
-        if (endDrawingButton == null) {
-            endDrawingButton = createFloatingIconButton();
-            endDrawingButton.addActionListener(e -> {
-                if (endDrawingCallback != null) {
-                    endDrawingCallback.run();
-                }
-            });
-        }
-        if (endIcon != null) {
-            endDrawingButton.setIcon(endIcon);
-            endDrawingButton.setText(null);
-        } else {
-            endDrawingButton.setText("缁撴潫缁樺埗");
-        }
-        endDrawingButton.setToolTipText("缁撴潫缁樺埗");
-
-        if (drawingPauseButton == null) {
-            drawingPauseButton = createFloatingIconButton();
-            drawingPauseButton.addActionListener(e -> toggleDrawingPause());
-        }
-        updatePauseButtonVisual();
-
-        if (floatingButtonPanel == null) {
-            floatingButtonPanel = new JPanel(new BorderLayout());
-            floatingButtonPanel.setOpaque(false);
-            floatingButtonPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 20));
-
-            floatingButtonColumn = new JPanel();
-            floatingButtonColumn.setOpaque(false);
-            floatingButtonColumn.setLayout(new BoxLayout(floatingButtonColumn, BoxLayout.Y_AXIS));
-            floatingButtonPanel.add(floatingButtonColumn, BorderLayout.EAST);
-        }
-    }
-
-    private void hideFloatingDrawingControls() {
-        if (drawingPauseButton != null) {
-            drawingPauseButton.setVisible(false);
-        }
-        if (endDrawingButton != null) {
-            endDrawingButton.setVisible(false);
-        }
-        if (floatingButtonPanel != null) {
-            floatingButtonPanel.setVisible(false);
-        }
-        if (!circleDialogMode) {
-            rebuildFloatingButtonColumn();
-        }
-    }
-
-    private void rebuildFloatingButtonColumn() {
-        if (floatingButtonColumn == null) {
-            return;
-        }
-        floatingButtonColumn.removeAll();
-        boolean added = false;
-        if (!circleDialogMode && circleGuidancePanel != null && circleGuidancePanel.isVisible()) {
-            floatingButtonColumn.add(circleGuidancePanel);
-            added = true;
-        }
-        if (!circleDialogMode && drawingPauseButton != null && drawingPauseButton.isVisible()) {
-            if (added) {
-                floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
-            }
-            floatingButtonColumn.add(drawingPauseButton);
-            added = true;
-        }
-        if (!circleDialogMode && endDrawingButton != null && endDrawingButton.isVisible()) {
-            if (added) {
-                floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
-            }
-            floatingButtonColumn.add(endDrawingButton);
-        }
-        floatingButtonColumn.revalidate();
-        floatingButtonColumn.repaint();
-    }
-
-    private void showCircleGuidanceStep(int step) {
-        ensureCircleGuidancePanel();
-        if (circleGuidancePanel == null) {
-            return;
-        }
-        circleGuidanceStep = step;
-        if (step == 1) {
-            circleGuidanceLabel.setText("鏄惁灏嗗綋鍓嶇偣璁剧疆涓哄渾蹇冿紵");
-            circleGuidancePrimaryButton.setText("鏄�");
-            circleGuidanceSecondaryButton.setText("杩斿洖");
-            circleGuidanceSecondaryButton.setVisible(true);
-        } else if (step == 2) {
-            circleGuidanceLabel.setText("鏄惁灏嗗綋鍓嶇偣璁剧疆涓哄崐寰勭偣锛�");
-            circleGuidancePrimaryButton.setText("鏄�");
-            circleGuidanceSecondaryButton.setVisible(false);
-        } else {
-            hideCircleGuidancePanel();
-            return;
-        }
-        circleGuidancePanel.setVisible(true);
-
-        if (circleDialogMode) {
-            ensureCircleGuidanceDialog();
-            if (circleGuidanceDialog != null) {
-                circleGuidanceDialog.pack();
-                positionCircleGuidanceDialog();
-                circleGuidanceDialog.setVisible(true);
-                circleGuidanceDialog.toFront();
-            }
-        } else {
-            rebuildFloatingButtonColumn();
-        }
-    }
-
-    private void ensureCircleGuidancePanel() {
-        if (circleGuidancePanel != null) {
-            return;
-        }
-        circleGuidancePanel = new JPanel();
-        circleGuidancePanel.setLayout(new BoxLayout(circleGuidancePanel, BoxLayout.Y_AXIS));
-        circleGuidancePanel.setOpaque(true);
-        circleGuidancePanel.setBackground(new Color(255, 255, 255, 235));
-        circleGuidancePanel.setBorder(BorderFactory.createCompoundBorder(
-                BorderFactory.createLineBorder(THEME_COLOR, 1),
-                BorderFactory.createEmptyBorder(10, 12, 10, 12)));
-        circleGuidancePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
-
-        circleGuidanceLabel = new JLabel();
-        circleGuidanceLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 13));
-        circleGuidanceLabel.setForeground(new Color(33, 37, 41));
-        circleGuidanceLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
-
-        JPanel buttonRow = new JPanel();
-        buttonRow.setLayout(new BoxLayout(buttonRow, BoxLayout.X_AXIS));
-        buttonRow.setOpaque(false);
-        buttonRow.setAlignmentX(Component.LEFT_ALIGNMENT);
-
-        circleGuidancePrimaryButton = createGuidanceButton("鏄�", THEME_COLOR, Color.WHITE, true);
-        circleGuidancePrimaryButton.addActionListener(e -> onCircleGuidancePrimary());
-
-        circleGuidanceSecondaryButton = createGuidanceButton("鍚�", Color.WHITE, THEME_COLOR, false);
-        circleGuidanceSecondaryButton.addActionListener(e -> onCircleGuidanceSecondary());
-
-        buttonRow.add(circleGuidancePrimaryButton);
-        buttonRow.add(Box.createRigidArea(new Dimension(8, 0)));
-        buttonRow.add(circleGuidanceSecondaryButton);
-
-        circleGuidancePanel.add(circleGuidanceLabel);
-        circleGuidancePanel.add(Box.createRigidArea(new Dimension(0, 8)));
-        circleGuidancePanel.add(buttonRow);
-        circleGuidancePanel.setVisible(false);
-    }
-
-    private void ensureCircleGuidanceDialog() {
-        if (circleGuidancePanel == null) {
-            return;
-        }
-        if (circleGuidanceDialog != null) {
-            if (circleGuidancePanel.getParent() != circleGuidanceDialog.getContentPane()) {
-                detachCircleGuidancePanel();
-                circleGuidanceDialog.getContentPane().removeAll();
-                circleGuidanceDialog.getContentPane().add(circleGuidancePanel, BorderLayout.CENTER);
-                circleGuidanceDialog.pack();
-            }
-            return;
-        }
-
-        Window owner = SwingUtilities.getWindowAncestor(this);
-        circleGuidanceDialog = new JDialog(owner, "缁樺埗鎻愮ず", Dialog.ModalityType.MODELESS);
-        circleGuidanceDialog.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
-        circleGuidanceDialog.setResizable(false);
-        circleGuidanceDialog.setAlwaysOnTop(true);
-
-        if (owner != null && circleDialogOwnerAdapter == null) {
-            circleDialogOwnerAdapter = new ComponentAdapter() {
-                @Override
-                public void componentMoved(ComponentEvent e) {
-                    positionCircleGuidanceDialog();
-                }
-
-                @Override
-                public void componentResized(ComponentEvent e) {
-                    positionCircleGuidanceDialog();
-                }
-            };
-            owner.addComponentListener(circleDialogOwnerAdapter);
-        }
-
-        detachCircleGuidancePanel();
-        circleGuidanceDialog.getContentPane().setLayout(new BorderLayout());
-        circleGuidanceDialog.getContentPane().add(circleGuidancePanel, BorderLayout.CENTER);
-        circleGuidanceDialog.pack();
-    }
-
-    private void detachCircleGuidancePanel() {
-        if (circleGuidancePanel == null) {
-            return;
-        }
-        Container parent = circleGuidancePanel.getParent();
-        if (parent != null) {
-            parent.remove(circleGuidancePanel);
-            parent.revalidate();
-            parent.repaint();
-        }
-    }
-
-    private void positionCircleGuidanceDialog() {
-        if (circleGuidanceDialog == null) {
-            return;
-        }
-        Window owner = SwingUtilities.getWindowAncestor(this);
-        if (owner == null || !owner.isShowing()) {
-            return;
-        }
-        try {
-            Point ownerLocation = owner.getLocationOnScreen();
-            int x = ownerLocation.x + owner.getWidth() - circleGuidanceDialog.getWidth() - 30;
-            int y = ownerLocation.y + owner.getHeight() - circleGuidanceDialog.getHeight() - 40;
-            x = Math.max(ownerLocation.x, x);
-            y = Math.max(ownerLocation.y, y);
-            circleGuidanceDialog.setLocation(x, y);
-        } catch (IllegalComponentStateException ex) {
-            // Owner not yet displayable; skip positioning
-        }
-    }
-
-    private JButton createGuidanceButton(String text, Color bg, Color fg, boolean filled) {
-        JButton button = new JButton(text);
-        button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 12));
-        button.setForeground(fg);
-        button.setBackground(bg);
-    button.setOpaque(true);
-        button.setFocusPainted(false);
-        button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
-        button.setAlignmentX(Component.LEFT_ALIGNMENT);
-        if (filled) {
-            button.setBorder(BorderFactory.createEmptyBorder(6, 14, 6, 14));
-        } else {
-            button.setBackground(Color.WHITE);
-            button.setOpaque(true);
-            button.setBorder(BorderFactory.createCompoundBorder(
-                    BorderFactory.createLineBorder(THEME_COLOR, 1),
-                    BorderFactory.createEmptyBorder(5, 12, 5, 12)));
-        }
-        button.addMouseListener(new MouseAdapter() {
-            @Override
-            public void mouseEntered(MouseEvent e) {
-                if (filled) {
-                    button.setBackground(THEME_HOVER_COLOR);
-                } else {
-                    button.setBackground(new Color(245, 245, 245));
-                }
-            }
-
-            @Override
-            public void mouseExited(MouseEvent e) {
-                if (filled) {
-                    button.setBackground(bg);
-                } else {
-                    button.setBackground(Color.WHITE);
-                }
-            }
-        });
-        return button;
-    }
-
-    private void onCircleGuidancePrimary() {
-        if (circleGuidanceStep == 1) {
-            showCircleGuidanceStep(2);
-        } else {
-            handleCircleCompletion();
-        }
-    }
-
-    private void onCircleGuidanceSecondary() {
-        if (circleGuidanceStep == 1) {
-            handleCircleAbort(null);
-        } else if (circleGuidanceStep == 2) {
-            handleCircleAbort(null);
-        } else {
-            hideCircleGuidancePanel();
-        }
-    }
-
-    private void hideCircleGuidancePanel() {
-        circleGuidanceStep = 0;
-        if (circleGuidancePanel != null) {
-            circleGuidancePanel.setVisible(false);
-        }
-        if (circleGuidanceDialog != null) {
-            circleGuidanceDialog.setVisible(false);
-        }
-        if (!circleDialogMode) {
-            rebuildFloatingButtonColumn();
-        }
-    }
-
-    private void handleCircleAbort(String message) {
-        // Return the wizard to step 2 without committing any captured points
-        hideEndDrawingButton();
-        addzhangaiwu.abortCircleDrawingAndReturn(message);
-    }
-
-    private void handleCircleCompletion() {
-        hideCircleGuidancePanel();
-        if (endDrawingCallback != null) {
-            endDrawingCallback.run();
-        } else {
-            addzhangaiwu.finishDrawingSession();
-        }
-    }
-
-    public void hideEndDrawingButton() {
-        hideCircleGuidancePanel();
-        hideFloatingDrawingControls();
-        circleDialogMode = false;
-        applyDrawingPauseState(false, false);
-        endDrawingCallback = null;
-        visualizationPanel.revalidate();
-        visualizationPanel.repaint();
-    }
-    
-    /**
-     * 鑾峰彇鍦板浘娓叉煋鍣ㄥ疄渚�
-     */
-    public MapRenderer getMapRenderer() {
-        return mapRenderer;
-    }
-
-    public void updateCurrentAreaName(String areaName) {
-        if (areaNameLabel == null) {
-            return;
-        }
-        if (areaName == null || areaName.trim().isEmpty()) {
-            areaNameLabel.setText("鏈�夋嫨鍦板潡");
-        } else {
-            areaNameLabel.setText(areaName);
-        }
-    }
-    
-    /**
-     * 閲嶇疆鍦板浘瑙嗗浘
-     */
-    public void resetMapView() {
-        if (mapRenderer != null) {
-            mapRenderer.resetView();
-        }
-    }
-
-    private void initializeDefaultAreaSelection() {
-        Dikuai.initFromProperties();
-        Map<String, Dikuai> all = Dikuai.getAllDikuai();
-        if (all.isEmpty()) {
-            Dikuaiguanli.setCurrentWorkLand(null, null);
-        } else if (all.size() == 1) {
-            Dikuai only = all.values().iterator().next();
-            if (only != null) {
-                Dikuaiguanli.setCurrentWorkLand(only.getLandNumber(), only.getLandName());
-            }
-        }
-    }
-
-    private void refreshMapForSelectedArea() {
-        if (mapRenderer == null) {
-            return;
-        }
-
-        String currentLandNumber = Dikuaiguanli.getCurrentWorkLandNumber();
-        if (isMeaningfulValue(currentLandNumber)) {
-            Dikuai current = Dikuai.getDikuai(currentLandNumber);
-            String landName = current != null ? current.getLandName() : null;
-            Dikuaiguanli.setCurrentWorkLand(currentLandNumber, landName);
-            return;
-        }
-
-        String labelName = areaNameLabel != null ? areaNameLabel.getText() : null;
-        if (!isMeaningfulAreaName(labelName)) {
-            Dikuaiguanli.setCurrentWorkLand(null, null);
-            return;
-        }
-
-        Map<String, Dikuai> all = Dikuai.getAllDikuai();
-        for (Dikuai dikuai : all.values()) {
-            if (dikuai == null) {
-                continue;
-            }
-            String candidateName = dikuai.getLandName();
-            if (candidateName != null && candidateName.trim().equals(labelName.trim())) {
-                Dikuaiguanli.setCurrentWorkLand(dikuai.getLandNumber(), candidateName);
-                return;
-            }
-        }
-
-        Dikuaiguanli.setCurrentWorkLand(null, null);
-    }
-
-    private boolean isMeaningfulValue(String value) {
-        if (value == null) {
-            return false;
-        }
-        String trimmed = value.trim();
-        return !trimmed.isEmpty() && !"-1".equals(trimmed);
-    }
-
-    private boolean isMeaningfulAreaName(String value) {
-        if (value == null) {
-            return false;
-        }
-        String trimmed = value.trim();
-        if (trimmed.isEmpty()) {
-            return false;
-        }
-        return !"鏈�夋嫨鍦板潡".equals(trimmed);
-    }
-
-    // 娴嬭瘯鏂规硶
+	// 娴嬭瘯鏂规硶
     public static void main(String[] args) {
         JFrame frame = new JFrame("AutoMow - 棣栭〉");
         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

--
Gitblit v1.10.0