From 32524195d474b74e48916867b2a6c2f022a40d98 Mon Sep 17 00:00:00 2001
From: 张世豪 <979909237@qq.com>
Date: 星期二, 09 十二月 2025 19:36:32 +0800
Subject: [PATCH] 20251209

---
 src/chuankou/SerialPortNativeLoader.java  |   77 +
 src/gecaoji/Gecaoji.java                  |   48 
 dikuai.properties                         |   36 
 src/zhuye/adddikuaiyulan.java             |   29 
 src/zhuye/MapRenderer.java                |  126 ++
 src/zhuye/pointandnumber.java             |    9 
 set.properties                            |    9 
 src/dikuai/Dikuaiguanli.java              |  182 +++
 src/set/Sets.java                         |   72 +
 src/denglu/RegistrationFrame.java         |   50 
 lib/native/windows/x86_64/jSerialComm.dll |    0 
 src/gecaoji/lujingdraw.java               |   11 
 src/chuankou/SerialPortService.java       |    6 
 src/denglu/Denglu.java                    |   95 +
 src/lujing/Lunjingguihua.java             |   37 
 image/URT.png                             |    0 
 src/zhuye/zijian.java                     |  127 ++
 src/dikuai/addzhangaiwu.java              |   10 
 src/homein/Homein.java                    |   35 
 src/zhangaiwu/AddDikuai.java              |  392 ++++++
 src/zhuye/Shouye.java                     |  755 +++++++++++++-
 user.properties                           |   12 
 Obstacledge.properties                    |    3 
 src/bianjie/bianjieguihua2.java           |   37 
 /dev/null                                 |  229 ----
 src/TestPlanner.java                      |    6 
 src/lujing/luoxuan.java                   |  408 ++++++++
 basestation.properties                    |    4 
 src/baseStation/BaseStationDialog.java    |  162 ++-
 src/bianjie/BoundaryAlgorithm.java        |   41 
 30 files changed, 2,449 insertions(+), 559 deletions(-)

diff --git a/Obstacledge.properties b/Obstacledge.properties
index ff75a3a..8845548 100644
--- a/Obstacledge.properties
+++ b/Obstacledge.properties
@@ -1,10 +1,11 @@
 # 鍓茶崏鏈哄湴鍧楅殰纰嶇墿閰嶇疆鏂囦欢
-# 鐢熸垚鏃堕棿锛�2025-12-05T15:37:09.106292500
+# 鐢熸垚鏃堕棿锛�2025-12-09T11:53:37.128295200
 # 鍧愭爣绯伙細WGS84锛堝害鍒嗘牸寮忥級
 
 # ============ 鍦板潡鍩哄噯绔欓厤缃� ============
 # 鏍煎紡锛歱lot.[鍦板潡缂栧彿].baseStation=[缁忓害],[N/S],[绾害],[E/W]
 plot.DK-001.baseStation=3949.902389,N,11616.756920,E
+plot.LAND1.baseStation=3949.902389,N,11616.756920,E
 
 # ============ 闅滅鐗╅厤缃� ============
 # 鏍煎紡锛歱lot.[鍦板潡缂栧彿].obstacle.[闅滅鐗╁悕绉癩.shape=[0|1]
diff --git a/basestation.properties b/basestation.properties
index 9018b20..1a3f4a0 100644
--- a/basestation.properties
+++ b/basestation.properties
@@ -1,7 +1,7 @@
 #Base station properties
-#Sat Nov 29 15:05:51 CST 2025
+#Tue Dec 09 19:03:31 CST 2025
 dataUpdateTime=-1
 deviceActivationTime=-1
-deviceId=1234
+deviceId=4567
 installationCoordinates=3949.90238860,N,11616.75692000,E
 iotSimCardNumber=-1
diff --git a/dikuai.properties b/dikuai.properties
index 482dd1d..9254a7b 100644
--- a/dikuai.properties
+++ b/dikuai.properties
@@ -1,19 +1,19 @@
 #Dikuai Properties
-#Fri Dec 05 19:16:23 CST 2025
-DK-001.angleThreshold=-1 
-DK-001.baseStationCoordinates=3949.90238860,N,11616.75692000,E
-DK-001.boundaryCoordinates=-3.74,1.88;-0.86,2.51;-0.32,0.12;1.15,-1.75;2.31,-7.15;1.56,-10.27;-0.45,-10.60;-1.73,-9.94;-3.61,0.51;-3.74,1.88
-DK-001.boundaryOriginalCoordinates=-1
-DK-001.boundaryPointInterval=-1
-DK-001.createTime=-1
-DK-001.intelligentSceneAnalysis=-1
-DK-001.landArea=300
-DK-001.landName=鍓嶉櫌鑽夊潽
-DK-001.landNumber=DK-001
-DK-001.mowingPattern=parallel
-DK-001.mowingTrack=-1
-DK-001.mowingWidth=100
-DK-001.plannedPath=1.51,-8.35;0.26,-1.43;-1.35,1.89;0.77,-9.89;-0.22,-10.06;-2.33,1.68
-DK-001.returnPointCoordinates=-1
-DK-001.updateTime=2025-12-05 19\:16\:23
-DK-001.userId=-1
+#Tue Dec 09 11:53:37 CST 2025
+LAND1.angleThreshold=-1
+LAND1.baseStationCoordinates=3949.90238860,N,11616.75692000,E
+LAND1.boundaryCoordinates=1.31,-9.59;1.86,-11.61;3.12,-12.49;5.50,-12.06;5.95,-10.88;4.97,-3.86;3.16,-0.87;2.79,-2.61;2.42,-4.35;2.05,-6.10;1.68,-7.84;1.31,-9.59
+LAND1.boundaryOriginalCoordinates=39.831620,116.279297,39.70;39.831618,116.279298,39.80;39.831616,116.279298,39.70;39.831614,116.279298,39.70;39.831612,116.279299,39.70;39.831610,116.279299,39.70;39.831608,116.279300,39.70;39.831606,116.279301,39.70;39.831604,116.279303,39.70;39.831602,116.279304,39.70;39.831600,116.279306,39.70;39.831598,116.279307,39.70;39.831598,116.279309,39.70;39.831597,116.279312,39.60;39.831596,116.279314,39.60;39.831595,116.279317,39.60;39.831594,116.279319,39.60;39.831594,116.279320,39.60;39.831594,116.279323,39.60;39.831595,116.279326,39.60;39.831595,116.279329,39.60;39.831595,116.279331,39.70;39.831595,116.279334,39.70;39.831596,116.279337,39.60;39.831596,116.279339,39.60;39.831596,116.279342,39.60;39.831597,116.279344,39.60;39.831598,116.279346,39.70;39.831600,116.279348,39.70;39.831601,116.279349,39.70;39.831603,116.279350,39.80;39.831605,116.279350,39.70;39.831606,116.279351,39.70;39.831609,116.279352,39.80;39.831611,116.279352,39.70;39.831613,116.279352,39.70;39.831615,116.279352,39.70;39.831617,116.279353,39.70;39.831619,116.279353,39.70;39.831621,116.279353,39.80;39.831623,116.279353,39.80;39.831625,116.279353,39.80;39.831627,116.279353,39.80;39.831629,116.279352,39.80;39.831631,116.279352,39.80;39.831634,116.279351,39.70;39.831636,116.279351,39.70;39.831637,116.279350,39.70;39.831639,116.279350,39.80;39.831641,116.279349,39.70;39.831643,116.279348,39.70;39.831645,116.279348,39.70;39.831647,116.279347,39.70;39.831649,116.279346,39.80;39.831651,116.279346,39.80;39.831653,116.279345,39.80;39.831655,116.279345,39.80;39.831657,116.279344,39.80;39.831659,116.279343,39.70;39.831661,116.279343,39.70;39.831663,116.279342,39.70;39.831665,116.279342,39.70;39.831667,116.279341,39.70;39.831670,116.279341,39.70;39.831672,116.279340,39.70;39.831674,116.279339,39.80;39.831676,116.279338,39.80;39.831678,116.279337,39.80;39.831679,116.279336,39.70;39.831680,116.279334,39.70;39.831681,116.279332,39.70;39.831683,116.279331,39.70;39.831684,116.279329,39.70;39.831686,116.279327,39.70;39.831687,116.279325,39.70;39.831689,116.279323,39.70;39.831691,116.279322,39.70;39.831693,116.279321,39.70;39.831694,116.279320,39.70;39.831696,116.279319,39.70;39.831699,116.279319,39.70
+LAND1.boundaryPointInterval=-1
+LAND1.createTime=2025-12-09 11\:16\:40
+LAND1.intelligentSceneAnalysis=-1
+LAND1.landArea=35.87
+LAND1.landName=1234
+LAND1.landNumber=LAND1
+LAND1.mowingPattern=铻烘棆寮�
+LAND1.mowingTrack=-1
+LAND1.mowingWidth=40
+LAND1.plannedPath=1.88,-7.88;2.62,-4.39;3.25,-1.41;4.78,-3.93;5.74,-10.86;5.35,-11.88;3.17,-12.28;2.03,-11.49;1.52,-9.58;1.88,-7.88;2.27,-7.96;3.01,-4.47;3.43,-2.48;4.39,-4.07;5.33,-10.81;5.06,-11.53;3.26,-11.86;2.38,-11.24;1.93,-9.57;2.27,-7.96;2.66,-8.05;3.40,-4.56;3.61,-3.55;4.01,-4.20;4.92,-10.76;4.77,-11.18;3.35,-11.43;2.73,-11.00;2.34,-9.56;2.66,-8.05;3.05,-8.13;3.71,-4.99;4.51,-10.72;4.47,-10.82;3.44,-11.01;3.08,-10.75;2.75,-9.55;3.05,-8.13;3.44,-8.21;3.63,-7.30;4.08,-10.49;3.54,-10.59;3.43,-10.51;3.16,-9.54;3.44,-8.21
+LAND1.returnPointCoordinates=-1
+LAND1.updateTime=2025-12-09 11\:53\:37
+LAND1.userId=-1
diff --git a/homein.lock b/homein.lock
deleted file mode 100644
index e69de29..0000000
--- a/homein.lock
+++ /dev/null
diff --git a/image/URT.png b/image/URT.png
index 539a79c..79d895d 100644
--- a/image/URT.png
+++ b/image/URT.png
Binary files differ
diff --git a/lib/native/windows/x86_64/jSerialComm.dll b/lib/native/windows/x86_64/jSerialComm.dll
new file mode 100644
index 0000000..d79031c
--- /dev/null
+++ b/lib/native/windows/x86_64/jSerialComm.dll
Binary files differ
diff --git a/set.properties b/set.properties
index c56ad27..19c65d8 100644
--- a/set.properties
+++ b/set.properties
@@ -1,11 +1,12 @@
-#Mower Configuration Properties - Updated
-#Fri Dec 05 14:26:38 CST 2025
+#Current work land selection updated
+#Tue Dec 09 19:35:30 CST 2025
 appVersion=-1
+currentWorkLandNumber=LAND1
 cuttingWidth=200
 firmwareVersion=-1
-handheldMarkerId=2354
+handheldMarkerId=
 idleTrailDurationSeconds=60
-mowerId=1872
+mowerId=1234
 serialAutoConnect=true
 serialBaudRate=921600
 serialPortName=COM15
diff --git a/src/TestPlanner.java b/src/TestPlanner.java
index a2a9498..098e1ed 100644
--- a/src/TestPlanner.java
+++ b/src/TestPlanner.java
@@ -2,10 +2,10 @@
 
 public class TestPlanner {
     public static void main(String[] args) {
-        String polygon = "-3.74,1.88;-0.86,2.51;-0.32,0.12;1.15,-1.75;2.31,-7.15;1.56,-10.27;-0.45,-10.60;-1.73,-9.94;-3.61,0.51;-3.74,1.88";
+    String polygon = "-3.74,1.88;-0.86,2.51;-0.32,0.12;1.15,-1.75;2.31,-7.15;1.56,-10.27;-0.45,-10.60;-1.73,-9.94;-3.61,0.51;-3.74,1.88";
         String obstacles = "";
-        String widthMeters = "0.5";
-        String mode = "parallel";
+    String widthMeters = "0.5";
+    String mode = "parallel";
 
         String pathCoords = Lunjingguihua.generatePathFromStrings(polygon, obstacles, widthMeters, mode);
         System.out.println(pathCoords);
diff --git a/src/baseStation/BaseStationDialog.java b/src/baseStation/BaseStationDialog.java
index 5ea81ea..2b46be8 100644
--- a/src/baseStation/BaseStationDialog.java
+++ b/src/baseStation/BaseStationDialog.java
@@ -13,6 +13,7 @@
 import java.io.IOException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.Locale;
 import java.util.Properties;
 
 public class BaseStationDialog extends JDialog {
@@ -180,26 +181,24 @@
         baseStation.load();
 
         String currentRaw = baseStation.getInstallationCoordinates();
-        String normalizedCurrent = currentRaw == null ? "-1" : currentRaw.trim();
-        if (normalizedCurrent.isEmpty()) {
-            normalizedCurrent = "-1";
-        }
+        String normalizedCurrent = canonicalizeCoordinateValue(currentRaw);
+        boolean hasExistingCoordinate = normalizedCurrent != null && !"-1".equals(normalizedCurrent);
 
-        JTextField inputField = new JTextField("-1".equals(normalizedCurrent) ? "" : normalizedCurrent);
+        JTextField inputField = new JTextField(hasExistingCoordinate ? normalizedCurrent : "");
         inputField.setColumns(28);
 
         JPanel dialogPanel = new JPanel(new BorderLayout(0, 8));
-        JLabel hintLabel = new JLabel("璇疯緭鍏ユ柊鐨勫熀鍑嗙珯鍧愭爣锛堟牸寮忕ず渚嬶細2324.194945,N,11330.938547,E锛�");
+        JLabel hintLabel = new JLabel("璇疯緭鍏ユ柊鐨勫熀鍑嗙珯鍧愭爣锛堢ず渚嬶細3949.90238860,N,11616.75692000,E锛�");
         hintLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
         dialogPanel.add(hintLabel, BorderLayout.NORTH);
         dialogPanel.add(inputField, BorderLayout.CENTER);
 
-    JOptionPane optionPane = new JOptionPane(dialogPanel,
-        JOptionPane.PLAIN_MESSAGE,
-        JOptionPane.OK_CANCEL_OPTION);
-    JDialog dialog = optionPane.createDialog(this, "淇敼鍩哄噯绔欎綅缃�");
-    dialog.setModal(true);
-    dialog.pack();
+        JOptionPane optionPane = new JOptionPane(dialogPanel,
+            JOptionPane.PLAIN_MESSAGE,
+            JOptionPane.OK_CANCEL_OPTION);
+        JDialog dialog = optionPane.createDialog(this, "淇敼鍩哄噯绔欎綅缃�");
+        dialog.setModal(true);
+        dialog.pack();
 
         Dimension packedSize = dialog.getSize();
         Window ownerWindow = getOwner();
@@ -208,12 +207,11 @@
             referenceWidth = 400;
         }
         dialog.setSize(new Dimension(referenceWidth, packedSize.height));
-    dialog.setResizable(false);
-    dialog.setLocationRelativeTo(this);
-    dialog.setVisible(true);
-
-    Object selectedValue = optionPane.getValue();
-    if (!(selectedValue instanceof Integer) || ((Integer) selectedValue) != JOptionPane.OK_OPTION) {
+        dialog.setResizable(false);
+        dialog.setLocationRelativeTo(this);
+        dialog.setVisible(true);
+        Object selectedValue = optionPane.getValue();
+        if (!(selectedValue instanceof Integer) || ((Integer) selectedValue) != JOptionPane.OK_OPTION) {
             return;
         }
 
@@ -234,7 +232,7 @@
             }
         }
 
-        String valueToSave = trimmed.isEmpty() ? "-1" : trimmed;
+        String valueToSave = trimmed.isEmpty() ? "-1" : canonicalizeCoordinateValue(trimmed);
 
         if (!"-1".equals(valueToSave) && !looksLikeCoordinateFormat(valueToSave)) {
             int confirm = JOptionPane.showConfirmDialog(this,
@@ -247,7 +245,7 @@
             }
         }
 
-        if (valueToSave.equals(normalizedCurrent)) {
+        if (normalizedCurrent.equals(valueToSave)) {
             JOptionPane.showMessageDialog(this,
                     "鍩哄噯绔欎綅缃湭鍙戠敓鍙樺寲銆�",
                     "鎻愮ず",
@@ -425,26 +423,11 @@
             return "璁惧娌℃湁瀹夎鍥哄畾";
         }
         String coordinates = baseStation.getInstallationCoordinates();
-        if (coordinates == null) {
+        String canonical = canonicalizeCoordinateValue(coordinates);
+        if (canonical == null || "-1".equals(canonical)) {
             return "璁惧娌℃湁瀹夎鍥哄畾";
         }
-        String trimmed = coordinates.trim();
-        if (trimmed.isEmpty() || "-1".equals(trimmed)) {
-            return "璁惧娌℃湁瀹夎鍥哄畾";
-        }
-        try {
-            String[] parts = trimmed.split(",");
-            if (parts.length == 4) {
-                String lat = formatCoordinate(parts[0].trim(), true);
-                String latDir = parts[1].trim();
-                String lon = formatCoordinate(parts[2].trim(), false);
-                String lonDir = parts[3].trim();
-                return String.format("%s掳%s, %s掳%s", lat, latDir, lon, lonDir);
-            }
-        } catch (Exception e) {
-            // ignore formatting errors and fall back to raw value
-        }
-        return trimmed;
+        return canonical;
     }
 
     private String getSimCardDisplay() {
@@ -497,22 +480,93 @@
         return !trimmed.isEmpty() && !"-1".equals(trimmed);
     }
     
-    private String formatCoordinate(String coordinate, boolean isLatitude) {
-        // 鏍煎紡鍖栧潗鏍囷細2324.194945 -> 23掳24.1949'
-        try {
-            // 搴︽暟鏄墠2浣嶏紙绾害锛夋垨3浣嶏紙缁忓害锛�
-            int degreeDigits = isLatitude ? 2 : 3;
-            String degreeStr = coordinate.substring(0, degreeDigits);
-            String minuteStr = coordinate.substring(degreeDigits);
-            
-            // 淇濈暀4浣嶅皬鏁�
-            double minutes = Double.parseDouble(minuteStr);
-            String formattedMinutes = String.format("%.4f", minutes);
-            
-            return degreeStr + "掳" + formattedMinutes + "'";
-        } catch (Exception e) {
-            return coordinate;
+    // Normalizes coordinate strings into degree-minute format when possible for consistent display/storage
+    private String canonicalizeCoordinateValue(String value) {
+        if (value == null) {
+            return "-1";
         }
+        String trimmed = value.trim();
+        if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+            return "-1";
+        }
+        String[] parts = trimmed.split(",");
+        if (parts.length != 4) {
+            return trimmed;
+        }
+        String latDirRaw = parts[1] == null ? "" : parts[1].trim();
+        String lonDirRaw = parts[3] == null ? "" : parts[3].trim();
+        if (latDirRaw.isEmpty() || lonDirRaw.isEmpty()) {
+            return trimmed;
+        }
+        String latDir = latDirRaw.toUpperCase(Locale.ROOT);
+        String lonDir = lonDirRaw.toUpperCase(Locale.ROOT);
+        if (!isValidDirection(latDir, true) || !isValidDirection(lonDir, false)) {
+            return trimmed;
+        }
+        String formattedLat = convertToDegreeMinuteString(parts[0], latDir, true);
+        String formattedLon = convertToDegreeMinuteString(parts[2], lonDir, false);
+        if (formattedLat == null || formattedLon == null) {
+            return trimmed;
+        }
+        return formattedLat + "," + latDir + "," + formattedLon + "," + lonDir;
+    }
+
+    private boolean isValidDirection(String direction, boolean isLatitude) {
+        if (direction == null) {
+            return false;
+        }
+        if (isLatitude) {
+            return "N".equals(direction) || "S".equals(direction);
+        }
+        return "E".equals(direction) || "W".equals(direction);
+    }
+
+    // Converts decimal degrees or degree-minute input into a canonical degree-minute string (8 decimal places)
+    private String convertToDegreeMinuteString(String rawValue, String direction, boolean isLatitude) {
+        double decimal = parseCoordinateToDecimalDegrees(rawValue, direction, isLatitude);
+        if (!Double.isFinite(decimal)) {
+            return null;
+        }
+        double absDecimal = Math.abs(decimal);
+        int degrees = (int) Math.floor(absDecimal);
+        double minutes = (absDecimal - degrees) * 60.0d;
+        double degreeMinutes = degrees * 100.0d + minutes;
+        return String.format(Locale.US, "%.8f", degreeMinutes);
+    }
+
+    private double parseCoordinateToDecimalDegrees(String rawValue, String direction, boolean isLatitude) {
+        if (rawValue == null) {
+            return Double.NaN;
+        }
+        String trimmed = rawValue.trim();
+        if (trimmed.isEmpty()) {
+            return Double.NaN;
+        }
+        double numeric;
+        try {
+            numeric = Double.parseDouble(trimmed);
+        } catch (NumberFormatException ex) {
+            return Double.NaN;
+        }
+
+        double abs = Math.abs(numeric);
+        double boundary = isLatitude ? 90d : 180d;
+        double decimal;
+        if (abs <= boundary) {
+            decimal = abs;
+        } else {
+            double degrees = Math.floor(abs / 100d);
+            double minutes = abs - degrees * 100d;
+            decimal = degrees + minutes / 60d;
+        }
+
+        String dirUpper = direction == null ? "" : direction.trim().toUpperCase(Locale.ROOT);
+        if ("S".equals(dirUpper) || "W".equals(dirUpper)) {
+            decimal = -decimal;
+        } else if (!"N".equals(dirUpper) && !"E".equals(dirUpper) && numeric < 0d) {
+            decimal = -decimal;
+        }
+        return decimal;
     }
     
     private void lockBaseStationPosition() {
@@ -542,7 +596,7 @@
                 JOptionPane.INFORMATION_MESSAGE);
             
             // 鏇存柊鍩哄噯绔欎綅缃紙杩欓噷浣跨敤妯℃嫙鏁版嵁锛屽疄闄呭簲浠嶨PS鑾峰彇锛�
-            String newPosition = "2324.194945,N,11330.938547,E";
+            String newPosition = canonicalizeCoordinateValue("2324.194945,N,11330.938547,E");
             String timestamp = String.valueOf(System.currentTimeMillis());
 
             if (!hasBaseStationId()) {
diff --git a/src/bianjie/BoundaryAlgorithm.java b/src/bianjie/BoundaryAlgorithm.java
index a38f076..aa4254c 100644
--- a/src/bianjie/BoundaryAlgorithm.java
+++ b/src/bianjie/BoundaryAlgorithm.java
@@ -488,19 +488,22 @@
         if (!optimized.get(0).equals(optimized.get(optimized.size() - 1))) {
             Coordinate first = optimized.get(0);
             Coordinate last = optimized.get(optimized.size() - 1);
-            double closingDistance = calculateDistance(first, last);
-            
+            double closingDistance = calculateDistance(last, first);
+
             if (closingDistance > params.interval) {
-                // 濡傛灉棣栧熬璺濈杈冭繙锛屾坊鍔犱腑闂寸偣
-                List<Coordinate> interpolated = interpolateBoundary(
-                    List.of(last, first), params.interval
-                );
-                optimized.addAll(interpolated.subList(1, interpolated.size() - 1));
+                int segments = (int) Math.ceil(closingDistance / params.interval);
+                for (int i = 1; i < segments; i++) {
+                    double ratio = (double) i / segments;
+                    Coordinate interpolatedPoint = interpolate(last, first, ratio);
+                    if (!interpolatedPoint.equals(last)) {
+                        optimized.add(interpolatedPoint);
+                    }
+                }
             }
             optimized.add(first);
         }
-        
-        return optimized;
+
+        return removeConsecutiveDuplicates(optimized);
     }
 
     // 閬撴牸鎷夋柉-鏅厠绠楁硶
@@ -580,6 +583,26 @@
         return interpolated;
     }
 
+    private List<Coordinate> removeConsecutiveDuplicates(List<Coordinate> points) {
+        if (points == null || points.isEmpty()) {
+            return points;
+        }
+
+        List<Coordinate> cleaned = new ArrayList<>();
+        Coordinate previous = null;
+        for (Coordinate point : points) {
+            if (previous == null || !point.equals(previous)) {
+                cleaned.add(point);
+                previous = point;
+            }
+        }
+
+        if (!cleaned.isEmpty() && !cleaned.get(0).equals(cleaned.get(cleaned.size() - 1))) {
+            cleaned.add(cleaned.get(0));
+        }
+        return cleaned;
+    }
+
     // 绾挎�ф彃鍊�
     private Coordinate interpolate(Coordinate p1, Coordinate p2, double ratio) {
         double x = p1.x + (p2.x - p1.x) * ratio;
diff --git a/src/bianjie/bianjieguihua.java b/src/bianjie/bianjieguihua.java
deleted file mode 100644
index 9d2233e..0000000
--- a/src/bianjie/bianjieguihua.java
+++ /dev/null
@@ -1,339 +0,0 @@
-package bianjie;
-
-import java.io.*;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-
-public class bianjieguihua {
-    
-    /**
-     * 闈欐�佹柟娉曪細澶勭悊杈圭晫鏁版嵁骞惰繑鍥炶竟鐣岀偣鍧愭爣
-     * 
-     * @param filePath GNGGA鏁版嵁鏂囦欢璺緞锛堜粠鏍圭洰褰曠殑GNGGA.txt鏂囦欢鑾峰彇锛�
-     * @param baseStation 鍩哄噯绔欏潗鏍囷紝鏍煎紡锛�"绾害,N/S,缁忓害,E/W" 渚嬪锛�"2324.194945,N,11330.938547,E"
-     * @param interval 杈圭晫鐐归棿闅旓紙绫筹級
-     * @param angleThreshold 瑙掑害闃堝�硷紙搴︼級
-     * @return 杈圭晫鐐瑰潗鏍囧瓧绗︿覆锛屾牸寮忥細"x1,y1;x2,y2;xn,yn"
-     */
-    public static String processBoundary(String filePath, String baseStation, 
-                                       double interval, double angleThreshold) {
-        try {
-            // 1. 浠嶵XT鏂囦欢璇诲彇GNGGA鏁版嵁
-            String gnggaData = readGNGGAFromTxtFile(filePath);
-            
-            if (gnggaData == null || gnggaData.trim().isEmpty()) {
-                throw new RuntimeException("鏃犳硶浠庢枃浠惰鍙朑NGGA鏁版嵁鎴栨暟鎹负绌�");
-            }
-                        
-            // 2. 浣跨敤BoundaryProcessor澶勭悊鏁版嵁
-            String boundaryCoordinates = BoundaryProcessor.processBoundaryData(
-                gnggaData, baseStation, interval, angleThreshold);
-                        
-            return boundaryCoordinates;
-            
-        } catch (Exception e) {
-            throw new RuntimeException("澶勭悊杈圭晫鏁版嵁鏃跺彂鐢熼敊璇�: " + e.getMessage(), e);
-        }
-    }
-    
-    /**
-     * 浠嶵XT鏂囦欢璇诲彇GNGGA鏁版嵁
-     * 鏂囦欢鏍煎紡锛氬涓�$GNGGA鏁版嵁璁板綍锛岀敤$GNGGA鍒嗛殧
-     */
-    private static String readGNGGAFromTxtFile(String filePath) {
-        try {
-            // 棣栧厛灏濊瘯浠庡綋鍓嶅伐浣滅洰褰曪紙鏍圭洰褰曪級鍔犺浇
-            File file = new File(filePath);
-            
-            // 濡傛灉鏂囦欢涓嶅瓨鍦紝灏濊瘯鍦ㄧ敤鎴峰綋鍓嶇洰褰曚笅鏌ユ壘
-            if (!file.exists()) {
-                file = new File(System.getProperty("user.dir") + File.separator + filePath);
-            }
-            
-            // 濡傛灉杩樻槸涓嶅瓨鍦紝灏濊瘯鍦ㄧ被璺緞涓煡鎵�
-            if (!file.exists()) {
-                InputStream input = bianjieguihua.class.getClassLoader().getResourceAsStream(filePath);
-                if (input != null) {
-                    // 浠庣被璺緞璇诲彇
-                    return readFromInputStream(input);
-                } else {
-                    throw new RuntimeException("鏂囦欢鏈壘鍒�: " + filePath + 
-                        " (鎼滅储璺緞: " + new File(".").getAbsolutePath() + ")");
-                }
-            }
-            
-            // 浠庢枃浠剁郴缁熻鍙�           
-            String content = new String(Files.readAllBytes(Paths.get(file.getAbsolutePath())));
-            
-            // 娓呯悊鏁版嵁锛氱Щ闄ゅ浣欑殑绌烘牸鍜屾崲琛岋紝纭繚鏍煎紡姝g‘
-            content = content.replaceAll("\\r\\n|\\r|\\n", ""); // 绉婚櫎鎹㈣绗�
-            content = content.replaceAll("\\s+", ""); // 绉婚櫎绌虹櫧瀛楃
-            content = content.trim();
-            
-            // 纭繚鏁版嵁浠�$GNGGA寮�澶达紙濡傛灉涓嶆槸锛屽彲鑳芥枃浠舵牸寮忎笉瀵癸級
-            if (!content.startsWith("$GNGGA") && content.contains("$GNGGA")) {
-                // 濡傛灉鍖呭惈$GNGGA浣嗕笉浠ュ畠寮�澶达紝鍙兘闇�瑕佸鐞�
-                System.out.println("璀﹀憡: GNGGA鏁版嵁鏍煎紡鍙兘闇�瑕佽皟鏁�");
-            }
-                        
-            return content;
-            
-        } catch (Exception e) {
-            throw new RuntimeException("璇诲彇GNGGA TXT鏂囦欢鏃跺彂鐢熼敊璇�: " + e.getMessage(), e);
-        }
-    }
-    
-    /**
-     * 浠庤緭鍏ユ祦璇诲彇鏁版嵁锛堢敤浜庣被璺緞璧勬簮锛�
-     */
-    private static String readFromInputStream(InputStream inputStream) throws IOException {
-        StringBuilder resultStringBuilder = new StringBuilder();
-        try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
-            String line;
-            while ((line = br.readLine()) != null) {
-                resultStringBuilder.append(line.trim());
-            }
-        }
-        return resultStringBuilder.toString();
-    }
-    
-    /**
-     * 璁$畻鍧愭爣鐐规暟閲�
-     */
-    private static int countCoordinates(String coordinates) {
-        if (coordinates == null || coordinates.trim().isEmpty()) {
-            return 0;
-        }
-        // 鎸夊垎鍙峰垎鍓插潗鏍囩偣
-        String[] points = coordinates.split(";");
-        return points.length;
-    }
-    
-    /**
-     * 楂樼骇澶勭悊鏂规硶 - 鍏佽浣跨敤鑷畾涔夊弬鏁�
-     * 
-     * @param filePath GNGGA鏁版嵁鏂囦欢璺緞
-     * @param baseStation 鍩哄噯绔欏潗鏍�
-     * @param params 杈圭晫鍙傛暟瀵硅薄
-     * @return 杈圭晫鐐瑰潗鏍囧瓧绗︿覆
-     */
-    public static String processBoundaryAdvanced(String filePath, String baseStation, 
-                                               BoundaryAlgorithm.BoundaryParameters params) {
-        try {
-            // 浠庢枃浠惰鍙朑NGGA鏁版嵁
-            String gnggaData = readGNGGAFromTxtFile(filePath);
-            
-            if (gnggaData == null || gnggaData.trim().isEmpty()) {
-                throw new RuntimeException("鏃犳硶浠庢枃浠惰鍙朑NGGA鏁版嵁鎴栨暟鎹负绌�");
-            }
-            
-            // 浣跨敤BoundaryProcessor鐨勯珮绾у鐞嗘柟娉�
-            String boundaryCoordinates = BoundaryProcessor.processBoundaryDataAdvanced(
-                gnggaData, baseStation, params);            
-            return boundaryCoordinates;
-            
-        } catch (Exception e) {
-            throw new RuntimeException("澶勭悊杈圭晫鏁版嵁鏃跺彂鐢熼敊璇�: " + e.getMessage(), e);
-        }
-    }
-    
-    /**
-     * 浠呰繘琛屽満鏅垎鏋愶紝涓嶇敓鎴愯竟鐣�
-     * 
-     * @param filePath GNGGA鏁版嵁鏂囦欢璺緞
-     * @param baseStation 鍩哄噯绔欏潗鏍�
-     * @return 鍦烘櫙鍒嗘瀽缁撴灉
-     */
-    public static BoundaryAlgorithm.SceneAnalysis analyzeScene(String filePath, String baseStation) {
-        try {
-            // 浠庢枃浠惰鍙朑NGGA鏁版嵁
-            String gnggaData = readGNGGAFromTxtFile(filePath);
-            
-            if (gnggaData == null || gnggaData.trim().isEmpty()) {
-                throw new RuntimeException("鏃犳硶浠庢枃浠惰鍙朑NGGA鏁版嵁鎴栨暟鎹负绌�");
-            }
-            
-            // 浣跨敤BoundaryProcessor杩涜鍦烘櫙鍒嗘瀽
-            return BoundaryProcessor.analyzeScene(gnggaData, baseStation);
-            
-        } catch (Exception e) {
-            throw new RuntimeException("鍦烘櫙鍒嗘瀽鏃跺彂鐢熼敊璇�: " + e.getMessage(), e);
-        }
-    }
-    
-    /**
-     * 瀹屾暣澶勭悊娴佺▼锛氬満鏅垎鏋� + 鑷姩鍙傛暟閫夋嫨 + 杈圭晫鐢熸垚
-     * 
-     * @param filePath GNGGA鏁版嵁鏂囦欢璺緞
-     * @param baseStation 鍩哄噯绔欏潗鏍�
-     * @return 杈圭晫鐐瑰潗鏍囧瓧绗︿覆
-     */
-    public static String processBoundaryAuto(String filePath, String baseStation) {
-        try {
-            // 1. 鍦烘櫙鍒嗘瀽
-            BoundaryAlgorithm.SceneAnalysis analysis = analyzeScene(filePath, baseStation);
-                        
-            // 2. 鏍规嵁鍦烘櫙鍒嗘瀽缁撴灉鑾峰彇鍙傛暟
-            BoundaryAlgorithm algorithm = new BoundaryAlgorithm();
-            BoundaryAlgorithm.BoundaryParameters params = 
-                algorithm.getParametersForPreset(analysis.suggestedPreset);
-                        
-            // 3. 浣跨敤楂樼骇澶勭悊鏂规硶鐢熸垚杈圭晫
-            return processBoundaryAdvanced(filePath, baseStation, params);
-            
-        } catch (Exception e) {
-            throw new RuntimeException("鑷姩杈圭晫澶勭悊鏃跺彂鐢熼敊璇�: " + e.getMessage(), e);
-        }
-    }
-    
-    /**
-     * 瑙f瀽GNGGA鏁版嵁骞舵彁鍙栨湁鏁堢殑缁忕含搴︾偣闆嗗悎锛堝害鍒嗘牸寮忎繚瀛橈級
-     * 
-     * @param gnggaData 鍖呭惈澶氭潯GNGGA璁板綍鐨勫瓧绗︿覆
-     * @return 鏈夋晥鐨勭粡绾害鐐归泦鍚堬紝鏍煎紡涓�"绾害鏂瑰悜,缁忓害鏂瑰悜;绾害鏂瑰悜,缁忓害鏂瑰悜;..."
-     */
-    public static String parseGNGGAToCoordinates(String gnggaData) {
-        try {
-            if (gnggaData == null || gnggaData.trim().isEmpty()) {
-                return "";
-            }
-            
-            StringBuilder coordinatesBuilder = new StringBuilder();
-            
-            // 鎸�$GNGGA鍒嗗壊璁板綍
-            String[] records = gnggaData.split("\\$GNGGA");
-            
-            for (String record : records) {
-                try {
-                    String trimmedRecord = record.trim();
-                    if (trimmedRecord.isEmpty()) continue;
-                    
-                    // 纭繚璁板綍浠ラ�楀彿寮�澶翠互渚挎纭垎鍓插瓧娈�
-                    if (!trimmedRecord.startsWith(",")) {
-                        trimmedRecord = "," + trimmedRecord;
-                    }
-                    
-                    String[] fields = trimmedRecord.split(",");
-                    if (fields.length < 7) {
-                        continue; // 瀛楁涓嶈冻锛岃烦杩�
-                    }
-                    
-                    // 妫�鏌ュ畾浣嶈川閲� (绗�7涓瓧娈碉紝绱㈠紩6)
-                    String fixQualityStr = fields[6];
-                    if (fixQualityStr.isEmpty()) {
-                        continue;
-                    }
-                    
-                    int fixQuality;
-                    try {
-                        fixQuality = Integer.parseInt(fixQualityStr);
-                    } catch (NumberFormatException e) {
-                        continue; // 瀹氫綅璐ㄩ噺鏍煎紡閿欒锛岃烦杩�
-                    }
-                    
-                    // 鍙噰鐢ㄩ珮绮惧害瀹氫綅鐐� (4 = RTK鍥哄畾瑙�)
-                    if (fixQuality != 4) {
-                        continue;
-                    }
-                    
-                    // 鎻愬彇绾害搴﹀垎鏍煎紡 (绗�3涓瓧娈碉紝绱㈠紩2) 鍜屾柟鍚� (绗�4涓瓧娈碉紝绱㈠紩3)
-                    String latitudeStr = fields[2];
-                    String latDirection = fields[3];
-                    
-                    // 鎻愬彇缁忓害搴﹀垎鏍煎紡 (绗�5涓瓧娈碉紝绱㈠紩4) 鍜屾柟鍚� (绗�6涓瓧娈碉紝绱㈠紩5)
-                    String longitudeStr = fields[4];
-                    String lonDirection = fields[5];
-                    
-                    if (latitudeStr.isEmpty() || longitudeStr.isEmpty() || 
-                        latDirection.isEmpty() || lonDirection.isEmpty()) {
-                        continue; // 鍧愭爣鏁版嵁涓嶅畬鏁达紝璺宠繃
-                    }
-                    
-                    // 鐩存帴淇濆瓨搴﹀垎鏍煎紡鍜屾柟鍚�
-                    if (coordinatesBuilder.length() > 0) {
-                        coordinatesBuilder.append(";");
-                    }
-                    coordinatesBuilder.append(latitudeStr)
-                                     .append(",")
-                                     .append(latDirection)
-                                     .append(",")
-                                     .append(longitudeStr)
-                                     .append(",")
-                                     .append(lonDirection);
-                    
-                } catch (Exception e) {
-                    // 鍗曟潯璁板綍瑙f瀽澶辫触锛岀户缁鐞嗕笅涓�鏉�
-                    System.err.println("瑙f瀽GNGGA璁板綍澶辫触: " + record + ", 閿欒: " + e.getMessage());
-                }
-            }
-            
-            return coordinatesBuilder.toString();
-            
-        } catch (Exception e) {
-            throw new RuntimeException("瑙f瀽GNGGA鏁版嵁鏃跺彂鐢熼敊璇�: " + e.getMessage(), e);
-        }
-    }
-
-    /**
-     * 灏嗗害鍒嗘牸寮忓潗鏍囪浆鎹负鍗佽繘鍒跺害鏍煎紡
-     * 
-     * @param dmCoord 搴﹀垎鏍煎紡鍧愭爣 (DDMM.MMMMM)
-     * @param direction 鏂瑰悜 (N/S/E/W)
-     * @return 鍗佽繘鍒跺害鏍煎紡鍧愭爣
-     */
-    private static double parseDMToDecimal(String dmCoord, String direction) {
-        try {
-            if (dmCoord == null || dmCoord.isEmpty()) {
-                return 0;
-            }
-            
-            int dotIndex = dmCoord.indexOf('.');
-            if (dotIndex < 2) {
-                return 0;
-            }
-            
-            // 鎻愬彇搴﹂儴鍒嗗拰鍒嗛儴鍒�
-            int degrees = Integer.parseInt(dmCoord.substring(0, dotIndex - 2));
-            double minutes = Double.parseDouble(dmCoord.substring(dotIndex - 2));
-            
-            // 杞崲涓哄崄杩涘埗搴�
-            double decimal = degrees + minutes / 60.0;
-            
-            // 鏍规嵁鏂瑰悜璋冩暣姝h礋
-            if ("S".equals(direction) || "W".equals(direction)) {
-                decimal = -decimal;
-            }
-            
-            return decimal;
-            
-        } catch (Exception e) {
-            throw new IllegalArgumentException("搴﹀垎鍧愭爣瑙f瀽閿欒: " + dmCoord, e);
-        }
-    }
-    
-    
-    
-    /**
-     * 娴嬭瘯鏂规硶
-     */
-    public static void main(String[] args) {
-        try {
-            // 鍒涘缓绀轰緥鏂囦欢锛堝鏋滀笉瀛樺湪锛�
-            File testFile = new File("GNGGA.txt");
-            
-            
-            // 娴嬭瘯鏁版嵁
-            String filePath = "GNGGA.txt";
-            String baseStation = "3949.91202005,N,11616.85440851,E";
-
-            
-            // 鏂规硶2: 鑷姩澶勭悊锛堟帹鑽愶級
-            String result2 = processBoundaryAuto(filePath, baseStation);
-            System.out.println("鑷姩澶勭悊鏂规硶缁撴灉: " + result2);
-            
-        } catch (Exception e) {
-            System.err.println("娴嬭瘯澶辫触: " + e.getMessage());
-            e.printStackTrace();
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/bianjie/bianjieguihua2.java b/src/bianjie/bianjieguihua2.java
index bb6e290..0129d75 100644
--- a/src/bianjie/bianjieguihua2.java
+++ b/src/bianjie/bianjieguihua2.java
@@ -34,9 +34,30 @@
 	        double baseLat = parseDMToDecimal(baseParts[0], baseParts[1]);
 	        double baseLon = parseDMToDecimal(baseParts[2], baseParts[3]);
 	        
-	        // 灏咰oordinate鍒楄〃杞崲涓哄眬閮ㄥ潗鏍囩郴鍧愭爣
-	        List<BoundaryAlgorithm.Coordinate> localCoordinates = 
-	            convertToLocalCoordinates(coordinates, baseLat, baseLon);
+			// 灏咰oordinate鍒楄〃杞崲涓哄眬閮ㄥ潗鏍囩郴鍧愭爣
+			List<BoundaryAlgorithm.Coordinate> localCoordinates = 
+				convertToLocalCoordinates(coordinates, baseLat, baseLon);
+
+			// 涓夎褰㈠皬鍖哄煙鐗规畩澶勭悊锛岄伩鍏嶈繃搴︽彃鍊煎鑷寸偣鏁版墿澧�
+			if (localCoordinates.size() == 3) {
+				double triangleArea = calculatePolygonArea(localCoordinates);
+				double trianglePerimeter = calculatePerimeter(localCoordinates);
+
+				System.out.println("妫�娴嬪埌涓夎褰㈣竟鐣岋紝闈㈢Н=" + String.format("%.2f", triangleArea) +
+						"m虏, 鍛ㄩ暱=" + String.format("%.2f", trianglePerimeter) + "m");
+
+				if (triangleArea < 100.0 || trianglePerimeter < 30.0) {
+					System.out.println("灏忎笁瑙掑舰锛岃烦杩囨彃鍊间紭鍖�");
+					BoundaryAlgorithm.Coordinate firstPoint = localCoordinates.get(0);
+					List<BoundaryAlgorithm.Coordinate> trianglePoints = new ArrayList<>(localCoordinates);
+					trianglePoints.add(new BoundaryAlgorithm.Coordinate(
+							firstPoint.x,
+							firstPoint.y,
+							firstPoint.lat,
+							firstPoint.lon));
+					return convertBoundaryPointsToString(trianglePoints);
+				}
+			}
 	        
 	        // 鍒涘缓绠楁硶瀹炰緥
 	        BoundaryAlgorithm algorithm = new BoundaryAlgorithm();
@@ -264,6 +285,16 @@
 	    
 	    return Math.abs(area) / 2.0;
 	}
+
+	private static double calculatePerimeter(List<BoundaryAlgorithm.Coordinate> points) {
+	    if (points == null || points.size() != 3) {
+	        return 0.0;
+	    }
+	    double d1 = calculateDistance(points.get(0), points.get(1));
+	    double d2 = calculateDistance(points.get(1), points.get(2));
+	    double d3 = calculateDistance(points.get(2), points.get(0));
+	    return d1 + d2 + d3;
+	}
 	
 	// ============ 鍏朵粬鏂规硶淇濇寔涓嶅彉 ============
 	
diff --git a/src/chuankou/SerialPortNativeLoader.java b/src/chuankou/SerialPortNativeLoader.java
new file mode 100644
index 0000000..83eae1b
--- /dev/null
+++ b/src/chuankou/SerialPortNativeLoader.java
@@ -0,0 +1,77 @@
+package chuankou;
+
+import java.io.File;
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Ensures the correct jSerialComm native library is available on Windows/x86_64 systems.
+ */
+public final class SerialPortNativeLoader {
+    private static final String LIB_PROPERTY = "com.fazecast.jSerialComm.library.path";
+    private static final String EXPECTED_DLL = "jSerialComm.dll";
+
+    private SerialPortNativeLoader() {
+        // utility class
+    }
+
+    public static void ensureLibraryPresent() {
+        if (System.getProperty(LIB_PROPERTY) != null) {
+            return;
+        }
+
+        String osName = System.getProperty("os.name", "").toLowerCase();
+        if (!osName.contains("win")) {
+            return;
+        }
+
+        String arch = System.getProperty("os.arch", "").toLowerCase();
+        if (!arch.contains("64")) {
+            return;
+        }
+
+        Path candidateDir = Paths.get("lib", "native", "windows", "x86_64").toAbsolutePath();
+        File dllFile = candidateDir.resolve(EXPECTED_DLL).toFile();
+        if (!dllFile.isFile()) {
+            candidateDir = resolveFromCodeSource();
+            if (candidateDir != null) {
+                dllFile = candidateDir.resolve(EXPECTED_DLL).toFile();
+            }
+        }
+
+        if (dllFile.isFile()) {
+            System.setProperty(LIB_PROPERTY, candidateDir.toString());
+        } else {
+            System.err.println("Expected jSerialComm native library not found. Checked " + dllFile);
+        }
+    }
+
+    private static Path resolveFromCodeSource() {
+        try {
+            java.security.CodeSource codeSource = SerialPortNativeLoader.class.getProtectionDomain().getCodeSource();
+            if (codeSource == null) {
+                return null;
+            }
+
+            Path location = Paths.get(codeSource.getLocation().toURI()).toAbsolutePath();
+            Path baseDir = location.getParent();
+            if (baseDir == null) {
+                return null;
+            }
+
+            Path siblingLibDir = baseDir.resolveSibling("lib").resolve("native").resolve("windows").resolve("x86_64");
+            if (siblingLibDir.toFile().isDirectory()) {
+                return siblingLibDir;
+            }
+
+            Path relativeLibDir = baseDir.resolve("lib").resolve("native").resolve("windows").resolve("x86_64");
+            if (relativeLibDir.toFile().isDirectory()) {
+                return relativeLibDir;
+            }
+        } catch (URISyntaxException | SecurityException ignored) {
+            // ignore
+        }
+        return null;
+    }
+}
diff --git a/src/chuankou/SerialPortService.java b/src/chuankou/SerialPortService.java
index f1c7aa7..64a0b60 100644
--- a/src/chuankou/SerialPortService.java
+++ b/src/chuankou/SerialPortService.java
@@ -1,13 +1,17 @@
 package chuankou;
 
 import com.fazecast.jSerialComm.SerialPort;
-import java.util.function.Consumer;
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.function.Consumer;
 
 public class SerialPortService {
 
+	static {
+		SerialPortNativeLoader.ensureLibraryPresent();
+	}
+
 	private SerialPort port;
 	private volatile boolean capturing = false;
 	private volatile boolean paused = true;
diff --git a/src/denglu/Denglu.java b/src/denglu/Denglu.java
index 93078df..82e306a 100644
--- a/src/denglu/Denglu.java
+++ b/src/denglu/Denglu.java
@@ -1,6 +1,7 @@
 package denglu;
 
 import homein.WenJianSuo;
+import ui.UIConfig;
 import javax.swing.*;
 import java.awt.*;
 import java.awt.event.*;
@@ -35,7 +36,7 @@
 
     private void initializeTranslations() {
         translations = new HashMap<>();
-        
+
         // 涓枃缈昏瘧
         Map<String, String> zh = new HashMap<>();
         zh.put("username_label", "鐢ㄦ埛鍚嶆垨閭");
@@ -45,7 +46,9 @@
         zh.put("login_button", "鐧诲綍");
         zh.put("no_account", "杩樻病鏈夎处鎴凤紵");
         zh.put("sign_up", "绔嬪嵆娉ㄥ唽");
-        zh.put("app_info", "AutoMow 漏 2023 - 鏅鸿兘鍓茶崏瑙e喅鏂规");
+        zh.put("app_title", "鏅鸿兘鍓茶崏绯荤粺");
+        zh.put("window_title", "鏅鸿兘鍓茶崏绯荤粺 - 鐧诲綍");
+        zh.put("app_info", "鏅鸿兘鍓茶崏绯荤粺 漏 2023 - 鏅鸿兘鍓茶崏瑙e喅鏂规");
         zh.put("register_button", "娉ㄥ唽鏂拌处鎴�");
         translations.put("zh", zh);
 
@@ -58,7 +61,9 @@
         en.put("login_button", "Sign In");
         en.put("no_account", "Don't have an account?");
         en.put("sign_up", "Sign Up");
-        en.put("app_info", "AutoMow 漏 2023 - Smart Mowing Solutions");
+        en.put("app_title", "Smart Mowing System");
+        en.put("window_title", "Smart Mowing System - Login");
+        en.put("app_info", "Smart Mowing System 漏 2023 - Intelligent Lawn Care");
         en.put("register_button", "Create New Account");
         translations.put("en", en);
 
@@ -71,7 +76,9 @@
         es.put("login_button", "Iniciar Sesi贸n");
         es.put("no_account", "驴No tienes una cuenta?");
         es.put("sign_up", "Reg铆strate");
-        es.put("app_info", "AutoMow 漏 2023 - Soluciones de Corte Inteligente");
+        es.put("app_title", "Sistema de Corte Inteligente");
+        es.put("window_title", "Sistema de Corte Inteligente - Iniciar Sesi贸n");
+        es.put("app_info", "Sistema de Corte Inteligente 漏 2023 - Soluciones Inteligentes");
         es.put("register_button", "Crear Nueva Cuenta");
         translations.put("es", es);
 
@@ -84,8 +91,10 @@
         fr.put("login_button", "Se connecter");
         fr.put("no_account", "Vous n'avez pas de compte?");
         fr.put("sign_up", "S'inscrire");
-        fr.put("app_info", "AutoMow 漏 2023 - Solutions de Tonte Intelligente");
-        es.put("register_button", "Cr茅er un Nouveau Compte");
+        fr.put("app_title", "Syst猫me de Tonte Intelligent");
+        fr.put("window_title", "Syst猫me de Tonte Intelligent - Connexion");
+        fr.put("app_info", "Syst猫me de Tonte Intelligent 漏 2023 - Solutions de Tonte");
+        fr.put("register_button", "Cr茅er un Nouveau Compte");
         translations.put("fr", fr);
 
         // 寰疯缈昏瘧
@@ -97,18 +106,20 @@
         de.put("login_button", "Anmelden");
         de.put("no_account", "Sie haben noch kein Konto?");
         de.put("sign_up", "Registrieren");
-        de.put("app_info", "AutoMow 漏 2023 - Intelligente M盲h-L枚sungen");
+        de.put("app_title", "Intelligentes M盲hsystem");
+        de.put("window_title", "Intelligentes M盲hsystem - Anmeldung");
+        de.put("app_info", "Intelligentes M盲hsystem 漏 2023 - M盲hl枚sungen");
         de.put("register_button", "Neues Konto Erstellen");
         translations.put("de", de);
     }
 
     private void initializeUI() {
-        setTitle("AutoMow - 鐧诲綍");
+        setTitle(getTranslationValue(currentLanguageCode, "window_title", "绯荤粺鐧诲綍"));
         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
         
         // 璁剧疆绱у噾甯冨眬灏哄
-        setSize(320, 520);
-        setMinimumSize(new Dimension(300, 500));
+        setSize(UIConfig.DIALOG_WIDTH, UIConfig.DIALOG_HEIGHT);
+        setMinimumSize(new Dimension(UIConfig.DIALOG_WIDTH, UIConfig.DIALOG_HEIGHT));
         setResizable(false);
         
         setupCompactLayout();
@@ -159,11 +170,6 @@
         ));
         
         // 鐧诲綍鏍囬
-        JLabel loginTitle = new JLabel("AutoMow", SwingConstants.CENTER);
-        loginTitle.setFont(new Font("PingFang SC", Font.BOLD, 24));
-        loginTitle.setForeground(THEME_COLOR);
-        loginTitle.setAlignmentX(Component.CENTER_ALIGNMENT);
-        
         // 鐢ㄦ埛鍚嶈緭鍏�
         usernameLabel = new JLabel("鐢ㄦ埛鍚嶆垨閭", SwingConstants.CENTER);
         usernameLabel.setFont(new Font("PingFang SC", Font.BOLD, 13));
@@ -212,7 +218,7 @@
         rememberForgotPanel.add(rememberMe, BorderLayout.WEST);
         rememberForgotPanel.add(forgotPassword, BorderLayout.EAST);
         
-        // 鐧诲綍鎸夐挳 - 姘村钩灞呬腑锛岄暱搴︿笌鏂囨湰妗嗙浉鍚�
+        // 鐧诲綍鎸夐挳 - 姘村钩灞呬腑锛岄暱搴︿笌鏂囨湰妗嗗搴︿繚鎸佺浉杩�
         JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
         buttonPanel.setBackground(Color.WHITE);
         buttonPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 50));
@@ -221,8 +227,8 @@
         loginButton.setBackground(THEME_COLOR);
         loginButton.setForeground(Color.WHITE);
         loginButton.setFont(new Font("PingFang SC", Font.BOLD, 15));
-        // 璁剧疆鐧诲綍鎸夐挳闀垮害涓庢枃鏈鐩稿悓
-        loginButton.setPreferredSize(new Dimension(260, 42));
+        // 璁剧疆鐧诲綍鎸夐挳闀垮害鎺ヨ繎鏂囨湰妗嗗搴�
+        loginButton.setPreferredSize(new Dimension(300, 48));
         loginButton.setBorderPainted(false);
         loginButton.setFocusPainted(false);
         loginButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
@@ -248,7 +254,7 @@
         registerButton.setBackground(Color.WHITE);
         registerButton.setForeground(THEME_COLOR);
         registerButton.setFont(new Font("PingFang SC", Font.BOLD, 14));
-        registerButton.setPreferredSize(new Dimension(140, 38));
+        registerButton.setPreferredSize(new Dimension(180, 42));
         registerButton.setBorder(BorderFactory.createCompoundBorder(
             BorderFactory.createLineBorder(THEME_COLOR),
             BorderFactory.createEmptyBorder(5, 15, 5, 15)
@@ -282,12 +288,11 @@
         signupPanel.add(signupLink);
         
         // 缁勮琛ㄥ崟
-        formPanel.add(loginTitle);
-        formPanel.add(Box.createRigidArea(new Dimension(0, 25)));
+    formPanel.add(Box.createRigidArea(new Dimension(0, 15)));
         formPanel.add(usernameLabel);
         formPanel.add(Box.createRigidArea(new Dimension(0, 5)));
         formPanel.add(usernameField);
-        formPanel.add(Box.createRigidArea(new Dimension(0, 15)));
+    formPanel.add(Box.createRigidArea(new Dimension(0, 30)));
         formPanel.add(passwordLabel);
         formPanel.add(Box.createRigidArea(new Dimension(0, 5)));
         formPanel.add(passwordField);
@@ -307,7 +312,9 @@
         JPanel appInfoPanel = new JPanel();
         appInfoPanel.setBackground(Color.WHITE);
         appInfoPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 5, 0));
-        appInfoLabel = new JLabel("AutoMow 漏 2023 - 鏅鸿兘鍓茶崏瑙e喅鏂规");
+        appInfoLabel = new JLabel(
+            getTranslationValue(currentLanguageCode, "app_info", "鏅鸿兘鍓茶崏绯荤粺 漏 2023")
+        );
         appInfoLabel.setFont(new Font("PingFang SC", Font.PLAIN, 10));
         appInfoLabel.setForeground(new Color(150, 150, 150));
         appInfoPanel.add(appInfoLabel);
@@ -356,20 +363,20 @@
         
         if (translation != null) {
             // 鏇存柊鎵�鏈夌晫闈㈡枃鏈�
-            usernameLabel.setText(translation.get("username_label"));
-            passwordLabel.setText(translation.get("password_label"));
-            rememberMe.setText(translation.get("remember_me"));
-            forgotPassword.setText(translation.get("forgot_password"));
-            loginButton.setText(translation.get("login_button"));
-            noAccountLabel.setText(translation.get("no_account"));
-            signupLink.setText(translation.get("sign_up"));
-            appInfoLabel.setText(translation.get("app_info"));
-            
+            usernameLabel.setText(getTranslationValue(langCode, "username_label", usernameLabel.getText()));
+            passwordLabel.setText(getTranslationValue(langCode, "password_label", passwordLabel.getText()));
+            rememberMe.setText(getTranslationValue(langCode, "remember_me", rememberMe.getText()));
+            forgotPassword.setText(getTranslationValue(langCode, "forgot_password", forgotPassword.getText()));
+            loginButton.setText(getTranslationValue(langCode, "login_button", loginButton.getText()));
+            noAccountLabel.setText(getTranslationValue(langCode, "no_account", noAccountLabel.getText()));
+            signupLink.setText(getTranslationValue(langCode, "sign_up", signupLink.getText()));
+            appInfoLabel.setText(getTranslationValue(langCode, "app_info", appInfoLabel.getText()));
+
             // 鏇存柊娉ㄥ唽鎸夐挳鏂囨湰
-            registerButton.setText(translation.get("register_button"));
-            
+            registerButton.setText(getTranslationValue(langCode, "register_button", registerButton.getText()));
+
             // 鏇存柊绐楀彛鏍囬
-            setTitle("AutoMow - " + (langCode.equals("zh") ? "鐧诲綍" : "Login"));
+            setTitle(getTranslationValue(langCode, "window_title", getTitle()));
             
             // 鏇存柊璇█璁剧疆鍒版枃浠�
             updateLanguagePreference(langCode);
@@ -387,6 +394,17 @@
         }
     }
 
+    private String getTranslationValue(String languageCode, String key, String defaultValue) {
+        Map<String, String> translation = translations.get(languageCode);
+        if (translation != null) {
+            String value = translation.get(key);
+            if (value != null && !value.trim().isEmpty()) {
+                return value;
+            }
+        }
+        return defaultValue;
+    }
+
     private void updateLanguagePreference(String languageCode) {
         String currentLanguage = UserChuShiHua.getProperty("language");
         if (!languageCode.equals(currentLanguage)) {
@@ -512,10 +530,5 @@
         RegistrationFrame registerFrame = new RegistrationFrame(this, currentLanguageCode);
         registerFrame.setVisible(true);
     }
-
-    public static void main(String[] args) {
-        SwingUtilities.invokeLater(() -> {
-            new Denglu().setVisible(true);
-        });
-    }
+   
 }
\ No newline at end of file
diff --git a/src/denglu/RegistrationFrame.java b/src/denglu/RegistrationFrame.java
index ace2dbf..7e5cb96 100644
--- a/src/denglu/RegistrationFrame.java
+++ b/src/denglu/RegistrationFrame.java
@@ -1,5 +1,6 @@
 package denglu;
 
+import ui.UIConfig;
 import javax.swing.*;
 import java.awt.*;
 import java.awt.event.*;
@@ -44,7 +45,8 @@
         
         // 涓枃缈昏瘧
         Map<String, String> zh = new HashMap<>();
-        zh.put("title", "鐢ㄦ埛娉ㄥ唽");
+    zh.put("title", "鐢ㄦ埛娉ㄥ唽");
+    zh.put("window_title", "鏅鸿兘鍓茶崏绯荤粺 - 鐢ㄦ埛娉ㄥ唽");
         zh.put("username_label", "鐢ㄦ埛鍚�");
         zh.put("email_label", "閭鍦板潃");
         zh.put("verification_code_label", "楠岃瘉鐮�");
@@ -66,7 +68,8 @@
 
         // 鑻辨枃缈昏瘧
         Map<String, String> en = new HashMap<>();
-        en.put("title", "User Registration");
+    en.put("title", "User Registration");
+    en.put("window_title", "Smart Mowing System - User Registration");
         en.put("username_label", "Username");
         en.put("email_label", "Email Address");
         en.put("verification_code_label", "Verification Code");
@@ -88,7 +91,8 @@
 
         // 瑗跨彮鐗欒缈昏瘧
         Map<String, String> es = new HashMap<>();
-        es.put("title", "Registro de Usuario");
+    es.put("title", "Registro de Usuario");
+    es.put("window_title", "Sistema de Corte Inteligente - Registro de Usuario");
         es.put("username_label", "Usuario");
         es.put("email_label", "Correo Electr贸nico");
         es.put("verification_code_label", "C贸digo de Verificaci贸n");
@@ -110,7 +114,8 @@
 
         // 娉曡缈昏瘧
         Map<String, String> fr = new HashMap<>();
-        fr.put("title", "Inscription Utilisateur");
+    fr.put("title", "Inscription Utilisateur");
+    fr.put("window_title", "Syst猫me de Tonte Intelligent - Inscription");
         fr.put("username_label", "Nom d'utilisateur");
         fr.put("email_label", "Adresse Email");
         fr.put("verification_code_label", "Code de V茅rification");
@@ -132,7 +137,8 @@
 
         // 寰疯缈昏瘧
         Map<String, String> de = new HashMap<>();
-        de.put("title", "Benutzerregistrierung");
+    de.put("title", "Benutzerregistrierung");
+    de.put("window_title", "Intelligentes M盲hsystem - Registrierung");
         de.put("username_label", "Benutzername");
         de.put("email_label", "E-Mail-Adresse");
         de.put("verification_code_label", "Verifizierungscode");
@@ -153,10 +159,22 @@
         translations.put("de", de);
     }
 
+    private String getTranslationValue(String languageCode, String key, String defaultValue) {
+        Map<String, String> translation = translations.get(languageCode);
+        if (translation != null) {
+            String value = translation.get(key);
+            if (value != null && !value.trim().isEmpty()) {
+                return value;
+            }
+        }
+        return defaultValue;
+    }
+
     private void initializeUI() {
-        setTitle("AutoMow - 鐢ㄦ埛娉ㄥ唽");
+    setTitle(getTranslationValue(currentLanguageCode, "window_title", "鐢ㄦ埛娉ㄥ唽"));
         setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
-        setSize(350, 500);
+    setSize(UIConfig.DIALOG_WIDTH, UIConfig.DIALOG_HEIGHT);
+    setMinimumSize(new Dimension(UIConfig.DIALOG_WIDTH, UIConfig.DIALOG_HEIGHT));
         setLocationRelativeTo(parentFrame);
         setResizable(false);
         
@@ -372,15 +390,15 @@
         
         if (translation != null) {
             // 鏇存柊鎵�鏈夌晫闈㈡枃鏈�
-            setTitle("AutoMow - " + translation.get("title"));
-            userLabel.setText(translation.get("username_label"));
-            emailLabel.setText(translation.get("email_label"));
-            verificationCodeLabel.setText(translation.get("verification_code_label"));
-            passLabel.setText(translation.get("password_label"));
-            confirmPassLabel.setText(translation.get("confirm_password_label"));
-            registerButton.setText(translation.get("register_button"));
-            cancelButton.setText(translation.get("cancel_button"));
-            sendCodeButton.setText(translation.get("send_code_button"));
+            setTitle(getTranslationValue(languageCode, "window_title", getTitle()));
+            userLabel.setText(getTranslationValue(languageCode, "username_label", userLabel.getText()));
+            emailLabel.setText(getTranslationValue(languageCode, "email_label", emailLabel.getText()));
+            verificationCodeLabel.setText(getTranslationValue(languageCode, "verification_code_label", verificationCodeLabel.getText()));
+            passLabel.setText(getTranslationValue(languageCode, "password_label", passLabel.getText()));
+            confirmPassLabel.setText(getTranslationValue(languageCode, "confirm_password_label", confirmPassLabel.getText()));
+            registerButton.setText(getTranslationValue(languageCode, "register_button", registerButton.getText()));
+            cancelButton.setText(getTranslationValue(languageCode, "cancel_button", cancelButton.getText()));
+            sendCodeButton.setText(getTranslationValue(languageCode, "send_code_button", sendCodeButton.getText()));
         }
     }
 
diff --git a/src/dikuai/Dikuaiguanli.java b/src/dikuai/Dikuaiguanli.java
index 3d01450..a5e31b6 100644
--- a/src/dikuai/Dikuaiguanli.java
+++ b/src/dikuai/Dikuaiguanli.java
@@ -6,6 +6,9 @@
 import java.awt.datatransfer.*;
 import java.awt.datatransfer.StringSelection;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import ui.UIConfig;
@@ -17,6 +20,8 @@
 import java.util.List;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Objects;
+import java.util.Properties;
 
 import lujing.Lunjingguihua;
 import zhangaiwu.AddDikuai;
@@ -58,6 +63,8 @@
 	private JButton addLandBtn;
 
 	private static String currentWorkLandNumber;
+	private static final String WORK_LAND_KEY = "currentWorkLandNumber";
+	private static final String PROPERTIES_FILE = "set.properties";
 	private static final Map<String, Boolean> boundaryPointVisibility = new HashMap<>();
 	private ImageIcon workSelectedIcon;
 	private ImageIcon workUnselectedIcon;
@@ -504,6 +511,7 @@
 					boolean isCurrent = currentWorkLandNumber != null && currentWorkLandNumber.equals(landNumber);
 					if (isCurrent) {
 						renderer.setBoundaryPointsVisible(desiredState);
+						renderer.setBoundaryPointSizeScale(desiredState ? 0.5d : 1.0d);
 					}
 				}
 			}
@@ -583,17 +591,69 @@
 		JScrollPane scrollPane = new JScrollPane(textArea);
 		scrollPane.setPreferredSize(new Dimension(360, 240));
 
-		int option = JOptionPane.showConfirmDialog(
-			this,
-			scrollPane,
-			title,
-			JOptionPane.OK_CANCEL_OPTION,
-			JOptionPane.PLAIN_MESSAGE);
-
-		if (option == JOptionPane.OK_OPTION) {
-			return textArea.getText();
+		Window owner = SwingUtilities.getWindowAncestor(this);
+		JDialog dialog;
+		if (owner instanceof Frame) {
+			dialog = new JDialog((Frame) owner, title, true);
+		} else if (owner instanceof Dialog) {
+			dialog = new JDialog((Dialog) owner, title, true);
+		} else {
+			dialog = new JDialog((Frame) null, title, true);
 		}
-		return null;
+		dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+
+		JPanel contentPanel = new JPanel(new BorderLayout());
+		contentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+		contentPanel.add(scrollPane, BorderLayout.CENTER);
+
+		JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
+		JButton okButton = new JButton("纭畾");
+		JButton cancelButton = new JButton("鍙栨秷");
+		JButton copyButton = new JButton("澶嶅埗");
+
+		final boolean[] confirmed = new boolean[] {false};
+		final String[] resultHolder = new String[1];
+
+		okButton.addActionListener(e -> {
+			resultHolder[0] = textArea.getText();
+			confirmed[0] = true;
+			dialog.dispose();
+		});
+
+		cancelButton.addActionListener(e -> dialog.dispose());
+
+		copyButton.addActionListener(e -> {
+			String text = textArea.getText();
+			if (text == null) {
+				text = "";
+			}
+			String trimmed = text.trim();
+			if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+				JOptionPane.showMessageDialog(dialog, title + " 鏈缃�", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+				return;
+			}
+			try {
+				StringSelection selection = new StringSelection(text);
+				Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+				clipboard.setContents(selection, selection);
+				JOptionPane.showMessageDialog(dialog, title + " 宸插鍒跺埌鍓创鏉�", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+			} catch (Exception ex) {
+				JOptionPane.showMessageDialog(dialog, "澶嶅埗澶辫触: " + ex.getMessage(), "閿欒", JOptionPane.ERROR_MESSAGE);
+			}
+		});
+
+		buttonPanel.add(okButton);
+		buttonPanel.add(cancelButton);
+		buttonPanel.add(copyButton);
+
+		contentPanel.add(buttonPanel, BorderLayout.SOUTH);
+		dialog.setContentPane(contentPanel);
+		dialog.getRootPane().setDefaultButton(okButton);
+		dialog.pack();
+		dialog.setLocationRelativeTo(this);
+		dialog.setVisible(true);
+
+		return confirmed[0] ? resultHolder[0] : null;
 	}
 
 	private boolean saveFieldAndRefresh(Dikuai dikuai, String fieldName, String value) {
@@ -733,10 +793,26 @@
 		switch (trimmed) {
 			case "1":
 			case "spiral":
+			case "铻烘棆":
+			case "铻烘棆妯″紡":
 				return "spiral";
 			case "0":
 			case "parallel":
+			case "骞宠":
+			case "骞宠妯″紡":
 			default:
+				if (trimmed.contains("铻烘棆")) {
+					return "spiral";
+				}
+				if (trimmed.contains("spiral")) {
+					return "spiral";
+				}
+				if (trimmed.contains("parallel")) {
+					return "parallel";
+				}
+				if (trimmed.contains("骞宠")) {
+					return "parallel";
+				}
 				return "parallel";
 		}
 	}
@@ -1446,10 +1522,19 @@
 	}
 
 	public static void setCurrentWorkLand(String landNumber, String landName) {
-		currentWorkLandNumber = landNumber;
+		String sanitizedLandNumber = sanitizeLandNumber(landNumber);
+		boolean changed = !Objects.equals(currentWorkLandNumber, sanitizedLandNumber);
+		if (!changed) {
+			String persisted = readPersistedWorkLandNumber();
+			if (!Objects.equals(persisted, sanitizedLandNumber)) {
+				changed = true;
+			}
+		}
+		currentWorkLandNumber = sanitizedLandNumber;
+
 		Dikuai dikuai = null;
-		if (landNumber != null) {
-			dikuai = Dikuai.getDikuai(landNumber);
+		if (sanitizedLandNumber != null) {
+			dikuai = Dikuai.getDikuai(sanitizedLandNumber);
 			if (dikuai != null && (landName == null || "-1".equals(landName) || landName.trim().isEmpty())) {
 				landName = dikuai.getLandName();
 			}
@@ -1460,7 +1545,7 @@
 
 		Shouye shouye = Shouye.getInstance();
 		if (shouye != null) {
-			if (landNumber == null) {
+			if (sanitizedLandNumber == null) {
 				shouye.updateCurrentAreaName(null);
 			} else {
 				shouye.updateCurrentAreaName(landName);
@@ -1470,24 +1555,85 @@
 				renderer.applyLandMetadata(dikuai);
 				String boundary = (dikuai != null) ? dikuai.getBoundaryCoordinates() : null;
 				String plannedPath = (dikuai != null) ? dikuai.getPlannedPath() : null;
-				List<Obstacledge.Obstacle> configuredObstacles = (landNumber != null) ? loadObstaclesFromConfig(landNumber) : Collections.emptyList();
+				List<Obstacledge.Obstacle> configuredObstacles = (sanitizedLandNumber != null) ? loadObstaclesFromConfig(sanitizedLandNumber) : Collections.emptyList();
 				if (configuredObstacles == null) {
 					configuredObstacles = Collections.emptyList();
 				}
-				renderer.setCurrentBoundary(boundary, landNumber, landNumber == null ? null : landName);
+				renderer.setCurrentBoundary(boundary, sanitizedLandNumber, sanitizedLandNumber == null ? null : landName);
 				renderer.setCurrentPlannedPath(plannedPath);
-				renderer.setCurrentObstacles(configuredObstacles, landNumber);
-				boolean showBoundaryPoints = landNumber != null && boundaryPointVisibility.getOrDefault(landNumber, false);
+				renderer.setCurrentObstacles(configuredObstacles, sanitizedLandNumber);
+				boolean showBoundaryPoints = sanitizedLandNumber != null && boundaryPointVisibility.getOrDefault(sanitizedLandNumber, false);
 				renderer.setBoundaryPointsVisible(showBoundaryPoints);
+				renderer.setBoundaryPointSizeScale(showBoundaryPoints ? 0.5d : 1.0d);
 			}
 			shouye.refreshMowingIndicators();
 		}
+
+		if (changed) {
+			persistCurrentWorkLand(sanitizedLandNumber);
+		}
 	}
 
 	public static String getCurrentWorkLandNumber() {
 		return currentWorkLandNumber;
 	}
 
+	public static String getPersistedWorkLandNumber() {
+		return readPersistedWorkLandNumber();
+	}
+
+	private static String sanitizeLandNumber(String landNumber) {
+		if (landNumber == null) {
+			return null;
+		}
+		String trimmed = landNumber.trim();
+		if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+			return null;
+		}
+		return trimmed;
+	}
+
+	private static void persistCurrentWorkLand(String landNumber) {
+		synchronized (Dikuaiguanli.class) {
+			Properties props = new Properties();
+			try (FileInputStream in = new FileInputStream(PROPERTIES_FILE)) {
+				props.load(in);
+			} catch (IOException ignored) {
+				// Use empty defaults when the configuration file is missing.
+			}
+
+			if (landNumber == null) {
+				props.setProperty(WORK_LAND_KEY, "-1");
+			} else {
+				props.setProperty(WORK_LAND_KEY, landNumber);
+			}
+
+			try (FileOutputStream out = new FileOutputStream(PROPERTIES_FILE)) {
+				props.store(out, "Current work land selection updated");
+			} catch (IOException ex) {
+				System.err.println("鏃犳硶淇濆瓨褰撳墠浣滀笟鍦板潡: " + ex.getMessage());
+			}
+		}
+	}
+
+	private static String readPersistedWorkLandNumber() {
+		Properties props = new Properties();
+		try (FileInputStream in = new FileInputStream(PROPERTIES_FILE)) {
+			props.load(in);
+			String value = props.getProperty(WORK_LAND_KEY);
+			if (value == null) {
+				return null;
+			}
+			String trimmed = value.trim();
+			if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+				return null;
+			}
+			return trimmed;
+		} catch (IOException ex) {
+			return null;
+		}
+	}
+
 	private ImageIcon loadIcon(String path, int width, int height) {
 		try {
 			ImageIcon rawIcon = new ImageIcon(path);
diff --git a/src/dikuai/addzhangaiwu.java b/src/dikuai/addzhangaiwu.java
index b1c1405..f14b481 100644
--- a/src/dikuai/addzhangaiwu.java
+++ b/src/dikuai/addzhangaiwu.java
@@ -46,6 +46,7 @@
 import set.Setsys;
 import ui.UIConfig;
 import zhuye.Coordinate;
+import zhuye.MapRenderer;
 import zhuye.Shouye;
 import zhangaiwu.AddDikuai;
 import zhangaiwu.Obstacledge;
@@ -887,6 +888,15 @@
 
         activeDrawingShape = shape.toLowerCase(Locale.ROOT);
 
+        Shouye shouyeInstance = Shouye.getInstance();
+        if (shouyeInstance != null) {
+            shouyeInstance.setHandheldMowerIconActive("handheld".equalsIgnoreCase(method));
+            MapRenderer renderer = shouyeInstance.getMapRenderer();
+            if (renderer != null) {
+                renderer.clearIdleTrail();
+            }
+        }
+
         Coordinate.coordinates.clear();
         Coordinate.setActiveDeviceIdFilter(deviceId);
         Coordinate.setStartSaveGngga(true);
diff --git a/src/gecaoji/Gecaoji.java b/src/gecaoji/Gecaoji.java
index 177832f..2aca713 100644
--- a/src/gecaoji/Gecaoji.java
+++ b/src/gecaoji/Gecaoji.java
@@ -15,27 +15,35 @@
     private static final double ICON_SCALE_FACTOR = 0.8;
     private static final double MIN_SCALE = 1e-6;
     private static final Color FALLBACK_FILL = new Color(0, 150, 0);
+    private static final String DEFAULT_ICON_PATH = "image/gecaoji.png";
+    private static final String HANDHELD_ICON_PATH = "image/URT.png";
 
-    private final Image mowerIcon;
+    private final Image defaultIcon;
+    private final Image handheldIcon;
+    private Image activeIcon;
+    private boolean handheldIconActive;
     private final Ellipse2D.Double fallbackShape = new Ellipse2D.Double();
     private Point2D.Double position = new Point2D.Double();
     private boolean positionValid;
     private double headingDegrees;
 
     public Gecaoji() {
-        mowerIcon = loadIcon();
+        defaultIcon = loadIcon(DEFAULT_ICON_PATH);
+        handheldIcon = loadIcon(HANDHELD_ICON_PATH);
+        activeIcon = defaultIcon != null ? defaultIcon : handheldIcon;
+        handheldIconActive = false;
         refreshFromDevice();
     }
 
-    private Image loadIcon() {
+    private Image loadIcon(String path) {
         try {
-            ImageIcon icon = new ImageIcon("image/gecaoji.png");
+            ImageIcon icon = new ImageIcon(path);
             if (icon.getIconWidth() <= 0 || icon.getIconHeight() <= 0) {
                 return null;
             }
             return icon.getImage();
         } catch (Exception ex) {
-            System.err.println("Unable to load mower icon: " + ex.getMessage());
+            System.err.println("Unable to load mower icon from " + path + ": " + ex.getMessage());
             return null;
         }
     }
@@ -51,7 +59,7 @@
         double y = parseCoordinate(device.getRealtimeY());
         double heading = parseHeading(device.getHeading());
         if (Double.isNaN(x) || Double.isNaN(y)) {
-            positionValid = false;
+            // Keep showing the last known mower position when temporary sensor glitches occur.
             return;
         }
 
@@ -109,16 +117,17 @@
         }
 
     double worldSize = (ICON_PIXEL_SIZE * ICON_SCALE_FACTOR) / Math.max(scale, MIN_SCALE);
-        if (mowerIcon != null && mowerIcon.getWidth(null) > 0 && mowerIcon.getHeight(null) > 0) {
-            drawIcon(g2d, worldSize);
+        Image icon = activeIcon;
+        if (icon != null && icon.getWidth(null) > 0 && icon.getHeight(null) > 0) {
+            drawIcon(g2d, worldSize, icon);
         } else {
             drawFallback(g2d, worldSize);
         }
     }
 
-    private void drawIcon(Graphics2D g2d, double worldSize) {
-        double iconWidth = mowerIcon.getWidth(null);
-        double iconHeight = mowerIcon.getHeight(null);
+    private void drawIcon(Graphics2D g2d, double worldSize, Image icon) {
+        double iconWidth = icon.getWidth(null);
+        double iconHeight = icon.getHeight(null);
         double maxSide = Math.max(iconWidth, iconHeight);
         double scaleFactor = worldSize / Math.max(maxSide, MIN_SCALE);
         double rotationRadians = Math.toRadians(-headingDegrees);
@@ -132,7 +141,7 @@
         transformed.scale(scaleFactor, scaleFactor);
         transformed.translate(-iconWidth / 2.0, -iconHeight / 2.0);
         g2d.setTransform(transformed);
-        g2d.drawImage(mowerIcon, 0, 0, null);
+        g2d.drawImage(icon, 0, 0, null);
         g2d.setTransform(original);
     }
 
@@ -177,4 +186,19 @@
         double worldSize = (ICON_PIXEL_SIZE * ICON_SCALE_FACTOR) / Math.max(scale, MIN_SCALE);
         return worldSize / 2.0;
     }
+
+    public boolean useHandheldIcon(boolean handheldMode) {
+        if (handheldIconActive == handheldMode) {
+            return false;
+        }
+        handheldIconActive = handheldMode;
+        Image next = handheldMode
+            ? (handheldIcon != null ? handheldIcon : defaultIcon)
+            : defaultIcon;
+        if (next == null) {
+            next = handheldIcon;
+        }
+        activeIcon = next;
+        return true;
+    }
 }
diff --git a/src/gecaoji/lujingdraw.java b/src/gecaoji/lujingdraw.java
index 15a4c46..813472d 100644
--- a/src/gecaoji/lujingdraw.java
+++ b/src/gecaoji/lujingdraw.java
@@ -70,7 +70,7 @@
     /** // 鏂囨。娉ㄩ噴寮�濮�
      * Draw the planned mowing path. // 缁樺埗璺緞
      */ // 鏂囨。娉ㄩ噴缁撴潫
-    public static void drawPlannedPath(Graphics2D g2d, List<Point2D.Double> path, double scale) { // 缁樺埗涓绘柟娉�
+    public static void drawPlannedPath(Graphics2D g2d, List<Point2D.Double> path, double scale, double arrowScale) { // 缁樺埗涓绘柟娉�
         if (path == null || path.size() < 2) { // 鍒ゅ畾鐐规暟
             return; // 鏁版嵁涓嶈冻鐩存帴杩斿洖
         } // if缁撴潫
@@ -97,13 +97,13 @@
         Point2D.Double end = path.get(path.size() - 1); // 缁堢偣
         Point2D.Double prev = path.get(path.size() - 2); // 鍊掓暟绗簩涓偣
 
-        drawArrowMarker(g2d, start, second, START_POINT_COLOR, scale); // 缁樺埗璧风偣绠ご
-        drawArrowMarker(g2d, prev, end, END_POINT_COLOR, scale); // 缁樺埗缁堢偣绠ご
+    drawArrowMarker(g2d, start, second, START_POINT_COLOR, scale, arrowScale); // 缁樺埗璧风偣绠ご
+    drawArrowMarker(g2d, prev, end, END_POINT_COLOR, scale, arrowScale); // 缁樺埗缁堢偣绠ご
 
         g2d.setStroke(previous); // 鎭㈠鍘熸弿杈�
     } // 鏂规硶缁撴潫
 
-    private static void drawArrowMarker(Graphics2D g2d, Point2D.Double from, Point2D.Double to, Color color, double scale) { // 缁樺埗绠ご杈呭姪
+    private static void drawArrowMarker(Graphics2D g2d, Point2D.Double from, Point2D.Double to, Color color, double scale, double sizeScale) { // 缁樺埗绠ご杈呭姪
         if (from == null || to == null) { // 鍒ょ┖
             return; // 鏁版嵁涓嶈冻杩斿洖
         } // if缁撴潫
@@ -115,7 +115,8 @@
         } // if缁撴潫
 
     double arrowLength = Math.max(2.5, 5.5 / Math.max(0.5, scale)); // 璁$畻绠ご闀垮害
-    arrowLength *= 0.5; // 缂╁皬绠ご灏哄涓哄師鏉ョ殑涓�鍗�
+    double clampedScale = sizeScale > 0 ? sizeScale : 1.0; // 闃叉闈炴硶缂╂斁
+    arrowLength *= 0.25 * clampedScale; // 缂╁皬绠ご鑷冲師鏉ョ殑涓�鍗�
         double arrowWidth = arrowLength * 0.45; // 璁$畻绠ご瀹藉害
 
         double ux = dx / length; // 鍗曚綅鍚戦噺X
diff --git a/src/homein/Homein.java b/src/homein/Homein.java
index fd2ad95..acd24f6 100644
--- a/src/homein/Homein.java
+++ b/src/homein/Homein.java
@@ -3,9 +3,11 @@
 import denglu.UserChuShiHua;
 import gecaoji.Device;
 import chuankou.SerialPortAutoConnector;
+import chuankou.SerialPortNativeLoader;
 import set.Setsys;
 import udpdell.UDPServer;
 import denglu.Denglu;
+import java.awt.EventQueue;
 import javax.swing.JOptionPane;
 
 public class Homein {
@@ -26,15 +28,16 @@
         }));
 
         try {
-                // 鍒濆鍖栨暟鎹�
-                UserChuShiHua.initialize();
+            SerialPortNativeLoader.ensureLibraryPresent();
+            // 鍒濆鍖栨暟鎹�
+            UserChuShiHua.initialize();
 
-                Setsys setsys = new Setsys();
-                setsys.initializeFromProperties();
-                Device.initializeActiveDevice(setsys.getMowerId());
+            Setsys setsys = new Setsys();
+            setsys.initializeFromProperties();
+            Device.initializeActiveDevice(setsys.getMowerId());
 
             UDPServer.startAsync();//鍚姩鏁版嵁鎺ユ敹绾跨▼
-            SerialPortAutoConnector.initialize();//鍚姩涓插彛鑷姩杩炴帴
+//            SerialPortAutoConnector.initialize();//鍚姩涓插彛鑷姩杩炴帴
             
             // 鏄剧ず鍒濆鏁版嵁
             System.out.println("鍒濆鐢ㄦ埛鍚�: " + UserChuShiHua.getProperty("userName"));
@@ -54,17 +57,15 @@
     
     private static void startLoginInterface() {
         // 鍦‥DT涓惎鍔ㄧ櫥褰曠晫闈�
-        javax.swing.SwingUtilities.invokeLater(new Runnable() {
-            public void run() {
-                try {
-                    new Denglu().setVisible(true);
-                } catch (Exception e) {
-                    e.printStackTrace();
-                    JOptionPane.showMessageDialog(null, 
-                        "鐧诲綍鐣岄潰鍚姩澶辫触: " + e.getMessage(), 
-                        "閿欒", 
-                        JOptionPane.ERROR_MESSAGE);
-                }
+        EventQueue.invokeLater(() -> {
+            try {
+                new Denglu().setVisible(true);
+            } catch (Exception e) {
+                e.printStackTrace();
+                JOptionPane.showMessageDialog(null, 
+                    "鐧诲綍鐣岄潰鍚姩澶辫触: " + e.getMessage(), 
+                    "閿欒", 
+                    JOptionPane.ERROR_MESSAGE);
             }
         });
     }
diff --git a/src/lujing/Lunjingguihua.java b/src/lujing/Lunjingguihua.java
index 3b1c99b..907a4f7 100644
--- a/src/lujing/Lunjingguihua.java
+++ b/src/lujing/Lunjingguihua.java
@@ -266,8 +266,8 @@
         }
 
         List<PathSegment> generate() {
-            if (!"spiral".equals(mode)) {
-                return generateParallelPath();
+            if ("spiral".equals(mode)) {
+                return generateSpiralPath();
             }
             return generateParallelPath();
         }
@@ -361,6 +361,39 @@
             return path;
         }
 
+        private List<PathSegment> generateSpiralPath() {
+            Geometry safeArea = buildSafeArea();
+            if (safeArea == null || safeArea.isEmpty()) {
+                System.err.println("瀹夊叏鍖哄煙涓虹┖锛屾棤娉曠敓鎴愯灪鏃嬭矾寰�");
+                return new ArrayList<>();
+            }
+
+            List<PathSegment> spiral = luoxuan.generateSpiralPath(safeArea, width);
+            if (spiral.isEmpty()) {
+                return spiral;
+            }
+
+            postProcess(spiral);
+            PathSegment firstMowing = null;
+            PathSegment endCandidate = null;
+            for (int i = 0; i < spiral.size(); i++) {
+                PathSegment seg = spiral.get(i);
+                if (seg != null && seg.isMowing) {
+                    if (firstMowing == null) {
+                        firstMowing = seg;
+                    }
+                    endCandidate = seg;
+                }
+            }
+            if (firstMowing != null) {
+                firstMowing.setAsStartPoint();
+            }
+            if (endCandidate != null && endCandidate != firstMowing) {
+                endCandidate.setAsEndPoint();
+            }
+            return spiral;
+        }
+
         private Geometry buildSafeArea() {
             try {
                 Coordinate[] coords = polygon.toArray(new Coordinate[0]);
diff --git a/src/lujing/luoxuan.java b/src/lujing/luoxuan.java
new file mode 100644
index 0000000..c660e05
--- /dev/null
+++ b/src/lujing/luoxuan.java
@@ -0,0 +1,408 @@
+package lujing;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.LinearRing;
+import org.locationtech.jts.geom.Polygon;
+import org.locationtech.jts.geom.TopologyException;
+import org.locationtech.jts.operation.buffer.BufferParameters;
+
+import lujing.Lunjingguihua.PathSegment;
+
+/**
+ * Utility class that produces spiral mowing paths by iteratively offsetting a safe area polygon.
+ * 浼樺寲鐗堬細鏀硅繘璺緞鐢熸垚閫昏緫锛屽噺灏戠┖椹惰窛绂伙紝浼樺寲璺緞杩炵画鎬�
+ */
+public final class luoxuan {
+
+    private static final int MAX_ITERATIONS = 512;
+    private static final double AREA_EPSILON = 1e-2;
+    private static final double LENGTH_EPSILON = 1e-6;
+    private static final double MIN_BUFFER_RATIO = 0.6;  // 鏈�灏忕紦鍐叉瘮渚�
+
+    private luoxuan() {
+    }
+
+    /**
+     * Generate optimized spiral mowing paths with improved continuity
+     */
+    public static List<PathSegment> generateOptimizedSpiralPath(Geometry safeArea, double laneWidth) {
+        if (safeArea == null || safeArea.isEmpty() || !Double.isFinite(laneWidth) || laneWidth <= 0) {
+            return Collections.emptyList();
+        }
+
+        // 1. 娓呯悊鍑犱綍浣擄紝纭繚鏈夋晥鎬�
+        Geometry working = cleanGeometry(safeArea);
+        if (working.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        // 2. 鎻愬彇涓诲杈瑰舰锛堥�夋嫨闈㈢Н鏈�澶х殑锛�
+        Polygon mainPolygon = extractMainPolygon(working);
+        if (mainPolygon == null) {
+            return Collections.emptyList();
+        }
+
+        // 3. 璁$畻铻烘棆璺緞
+    List<PathSegment> segments = new ArrayList<>();
+    Coordinate cursor = clone(findOptimalStartPoint(mainPolygon));
+    Geometry currentLayer = mainPolygon; // start from the outer boundaryand peel inwards
+
+        for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++) {
+            Geometry layerGeometry = cleanGeometry(currentLayer);
+            if (layerGeometry == null || layerGeometry.isEmpty()) {
+                break;
+            }
+
+            List<Polygon> polygons = extractPolygons(layerGeometry);
+            if (polygons.isEmpty()) {
+                break;
+            }
+            polygons.sort(Comparator.comparingDouble(Polygon::getArea).reversed());
+
+            try {
+                for (Polygon polygon : polygons) {
+                    LinearRing outer = polygon.getExteriorRing();
+                    if (outer == null || outer.getNumPoints() < 4) {
+                        continue;
+                    }
+
+                    cursor = processRing(outer.getCoordinates(), true, cursor, segments);
+
+                    for (int holeIndex = 0; holeIndex < polygon.getNumInteriorRing(); holeIndex++) {
+                        LinearRing hole = polygon.getInteriorRingN(holeIndex);
+                        if (hole == null || hole.getNumPoints() < 4) {
+                            continue;
+                        }
+                        cursor = processRing(hole.getCoordinates(), false, cursor, segments);
+                    }
+                }
+            } catch (TopologyException ex) {
+                break;
+            }
+
+            if (!canShrinkFurther(polygons, laneWidth)) {
+                break;
+            }
+
+            Geometry nextLayer;
+            try {
+                nextLayer = layerGeometry.buffer(
+                    -laneWidth,
+                    BufferParameters.DEFAULT_QUADRANT_SEGMENTS,
+                    BufferParameters.CAP_FLAT
+                );
+            } catch (TopologyException ex) {
+                break;
+            }
+
+            if (nextLayer.isEmpty() || nextLayer.getArea() < AREA_EPSILON) {
+                break;
+            }
+
+            double areaDelta = Math.abs(layerGeometry.getArea() - nextLayer.getArea());
+            if (areaDelta <= AREA_EPSILON) {
+                break;
+            }
+
+            currentLayer = nextLayer;
+        }
+
+        // 4. 浼樺寲璺緞杩炴帴
+        optimizePathConnections(segments);
+        
+        // 5. 鏍囪绔偣
+        markEndpoints(segments);
+        
+        return segments;
+    }
+
+    /**
+     * Backward compatible entry that delegates to the optimized implementation.
+     */
+    public static List<PathSegment> generateSpiralPath(Geometry safeArea, double laneWidth) {
+        return generateOptimizedSpiralPath(safeArea, laneWidth);
+    }
+
+    /**
+     * 娓呯悊鍑犱綍浣擄紝纭繚鏈夋晥鎬�
+     */
+    private static Geometry cleanGeometry(Geometry geometry) {
+        if (geometry == null) return null;
+        try {
+            return geometry.buffer(0.0);
+        } catch (Exception e) {
+            return geometry;
+        }
+    }
+
+    /**
+     * 鎻愬彇涓诲杈瑰舰锛堥潰绉渶澶х殑锛�
+     */
+    private static Polygon extractMainPolygon(Geometry geometry) {
+        List<Polygon> polygons = extractPolygons(geometry);
+        if (polygons.isEmpty()) return null;
+        
+        // 鎸夐潰绉帓搴忥紝閫夋嫨鏈�澶х殑
+        polygons.sort((p1, p2) -> Double.compare(p2.getArea(), p1.getArea()));
+        return polygons.get(0);
+    }
+
+    /**
+     * 瀵绘壘鏈�浼樿捣鐐癸紙绂诲杈瑰舰涓績鏈�杩戠殑鐐癸級
+     */
+    private static Coordinate findOptimalStartPoint(Polygon polygon) {
+        if (polygon == null) return null;
+        
+        Coordinate center = polygon.getCentroid().getCoordinate();
+        LinearRing ring = polygon.getExteriorRing();
+        Coordinate[] coords = ring.getCoordinates();
+        
+        Coordinate nearest = coords[0];
+        double minDist = Double.MAX_VALUE;
+        
+        for (Coordinate coord : coords) {
+            double dist = coord.distance(center);
+            if (dist < minDist) {
+                minDist = dist;
+                nearest = coord;
+            }
+        }
+        
+        return nearest;
+    }
+
+    /**
+     * 浼樺寲璺緞杩炴帴锛屽噺灏戠┖椹惰窛绂�
+     */
+    private static void optimizePathConnections(List<PathSegment> segments) {
+        if (segments == null || segments.size() < 2) {
+            return;
+        }
+
+        List<PathSegment> optimized = new ArrayList<>(segments.size());
+        PathSegment previous = null;
+
+        for (PathSegment segment : segments) {
+            if (segment == null || segment.start == null || segment.end == null) {
+                continue;
+            }
+            if (isDegenerate(segment)) {
+                continue;
+            }
+
+            if (previous != null
+                && previous.isMowing == segment.isMowing
+                && equals2D(previous.start, segment.start)
+                && equals2D(previous.end, segment.end)) {
+                continue; // 璺宠繃閲嶅娈�
+            }
+
+            optimized.add(segment);
+            previous = segment;
+        }
+
+        segments.clear();
+        segments.addAll(optimized);
+    }
+
+    /**
+     * 妫�鏌ユ槸鍚﹀彲浠ョ户缁紦鍐�
+     */
+    private static boolean canShrinkFurther(List<Polygon> polygons, double bufferDistance) {
+        if (polygons == null || polygons.isEmpty()) {
+            return false;
+        }
+
+        for (Polygon polygon : polygons) {
+            if (polygon == null || polygon.isEmpty()) {
+                continue;
+            }
+
+            double width = polygon.getEnvelopeInternal().getWidth();
+            double height = polygon.getEnvelopeInternal().getHeight();
+            double minDimension = Math.min(width, height);
+
+            if (minDimension <= bufferDistance * 2 * MIN_BUFFER_RATIO) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * 鏍囪璧风偣鍜岀粓鐐�
+     */
+    private static void markEndpoints(List<PathSegment> segments) {
+        if (segments == null || segments.isEmpty()) {
+            return;
+        }
+        
+        // 瀵绘壘绗竴涓壊鑽夋浣滀负璧风偣
+        PathSegment firstMowing = null;
+        for (PathSegment seg : segments) {
+            if (seg != null && seg.isMowing) {
+                firstMowing = seg;
+                break;
+            }
+        }
+        
+        // 瀵绘壘鏈�鍚庝竴涓壊鑽夋浣滀负缁堢偣
+        PathSegment lastMowing = null;
+        for (int i = segments.size() - 1; i >= 0; i--) {
+            PathSegment seg = segments.get(i);
+            if (seg != null && seg.isMowing) {
+                lastMowing = seg;
+                break;
+            }
+        }
+        
+        if (firstMowing != null) {
+            firstMowing.setAsStartPoint();
+        }
+        if (lastMowing != null && lastMowing != firstMowing) {
+            lastMowing.setAsEndPoint();
+        }
+    }
+
+    /**
+     * 妫�鏌ョ嚎娈垫槸鍚﹂��鍖栵紙闀垮害杩囧皬锛�
+     */
+    private static boolean isDegenerate(PathSegment segment) {
+        if (segment == null || segment.start == null || segment.end == null) {
+            return true;
+        }
+        double dx = segment.start.x - segment.end.x;
+        double dy = segment.start.y - segment.end.y;
+        return Math.hypot(dx, dy) <= LENGTH_EPSILON;
+    }
+
+    /**
+     * 鎻愬彇澶氳竟褰紙涓庡師鏂规硶鐩稿悓锛�
+     */
+    private static List<Polygon> extractPolygons(Geometry geometry) {
+        if (geometry == null || geometry.isEmpty()) {
+            return Collections.emptyList();
+        }
+        
+        List<Polygon> result = new ArrayList<>();
+        
+        if (geometry instanceof Polygon) {
+            result.add((Polygon) geometry);
+        } else if (geometry instanceof org.locationtech.jts.geom.MultiPolygon) {
+            org.locationtech.jts.geom.MultiPolygon mp = (org.locationtech.jts.geom.MultiPolygon) geometry;
+            for (int i = 0; i < mp.getNumGeometries(); i++) {
+                Geometry g = mp.getGeometryN(i);
+                if (g instanceof Polygon) {
+                    result.add((Polygon) g);
+                }
+            }
+        } else if (geometry instanceof org.locationtech.jts.geom.GeometryCollection) {
+            org.locationtech.jts.geom.GeometryCollection gc = (org.locationtech.jts.geom.GeometryCollection) geometry;
+            for (int i = 0; i < gc.getNumGeometries(); i++) {
+                Geometry child = gc.getGeometryN(i);
+                result.addAll(extractPolygons(child));
+            }
+        }
+        
+        return result;
+    }
+
+    /**
+     * 澶嶅埗鍧愭爣
+     */
+    private static Coordinate clone(Coordinate source) {
+        return source == null ? null : new Coordinate(source.x, source.y);
+    }
+
+    /**
+     * 姣旇緝涓や釜鍧愭爣鏄惁鐩稿悓锛�2D锛�
+     */
+    private static boolean equals2D(Coordinate a, Coordinate b) {
+        if (a == b) return true;
+        if (a == null || b == null) return false;
+        return a.distance(b) <= LENGTH_EPSILON;
+    }
+
+    private static Coordinate processRing(Coordinate[] coords,
+                                          boolean forward,
+                                          Coordinate cursor,
+                                          List<PathSegment> segments) {
+        if (coords == null || coords.length < 4) {
+            return cursor;
+        }
+
+        List<Coordinate> base = new ArrayList<>(coords.length - 1);
+        for (int i = 0; i < coords.length - 1; i++) {
+            Coordinate cloned = clone(coords[i]);
+            if (cloned != null) {
+                base.add(cloned);
+            }
+        }
+
+        if (base.size() < 2) {
+            return cursor;
+        }
+
+        if (!forward) {
+            Collections.reverse(base);
+        }
+
+        int startIndex = 0;
+        if (cursor != null) {
+            startIndex = findNearestIndex(base, cursor);
+        }
+
+        List<Coordinate> ordered = new ArrayList<>(base.size());
+        for (int i = 0; i < base.size(); i++) {
+            int index = (startIndex + i) % base.size();
+            ordered.add(clone(base.get(index)));
+        }
+
+        Coordinate firstCoord = ordered.get(0);
+        if (cursor != null && !equals2D(cursor, firstCoord)) {
+            PathSegment transfer = new PathSegment(clone(cursor), clone(firstCoord), false);
+            if (!isDegenerate(transfer)) {
+                segments.add(transfer);
+            }
+        }
+
+        for (int i = 0; i < ordered.size(); i++) {
+            Coordinate start = ordered.get(i);
+            Coordinate end = ordered.get((i + 1) % ordered.size());
+            if (equals2D(start, end)) {
+                continue;
+            }
+            PathSegment mowing = new PathSegment(clone(start), clone(end), true);
+            segments.add(mowing);
+        }
+
+        return clone(firstCoord);
+    }
+
+    private static int findNearestIndex(List<Coordinate> coordinates, Coordinate reference) {
+        if (coordinates == null || coordinates.isEmpty() || reference == null) {
+            return 0;
+        }
+        double bestDistance = Double.MAX_VALUE;
+        int bestIndex = 0;
+        for (int i = 0; i < coordinates.size(); i++) {
+            Coordinate candidate = coordinates.get(i);
+            if (candidate == null) {
+                continue;
+            }
+            double distance = reference.distance(candidate);
+            if (distance < bestDistance) {
+                bestDistance = distance;
+                bestIndex = i;
+            }
+        }
+        return bestIndex;
+    }
+}
\ No newline at end of file
diff --git a/src/set/Sets.java b/src/set/Sets.java
index 224795f..deaacd8 100644
--- a/src/set/Sets.java
+++ b/src/set/Sets.java
@@ -30,6 +30,7 @@
     
     // 璁剧疆椤圭粍浠�
     private JLabel mowerIdLabel;
+    private JLabel baseStationIdLabel;
     private JLabel handheldMarkerLabel;
     private JLabel simCardNumberLabel;
     private JLabel baseStationSimLabel;
@@ -38,6 +39,7 @@
     private JLabel idleTrailDurationLabel;
     
     private JButton mowerIdEditBtn;
+    private JButton baseStationIdEditBtn;
     private JButton handheldEditBtn;
     private JButton checkUpdateBtn;
     private JButton systemDebugButton;
@@ -105,6 +107,11 @@
             setData.getMowerId() != null ? setData.getMowerId() : "鏈缃�", true);
         mowerIdLabel = (JLabel) mowerIdPanel.getClientProperty("valueLabel");
         mowerIdEditBtn = (JButton) mowerIdPanel.getClientProperty("editButton");
+
+        JPanel baseStationIdPanel = createSettingItemPanel("宸垎鍩哄噯绔欑紪鍙�",
+            resolveBaseStationId(), true);
+        baseStationIdLabel = (JLabel) baseStationIdPanel.getClientProperty("valueLabel");
+        baseStationIdEditBtn = (JButton) baseStationIdPanel.getClientProperty("editButton");
         
         JPanel handheldPanel = createSettingItemPanel("渚挎惡鎵撶偣鍣ㄧ紪鍙�",
             setData.getHandheldMarkerId() != null ? setData.getHandheldMarkerId() : "鏈缃�", true);
@@ -135,7 +142,8 @@
         // APP鐗堟湰
         JPanel appVersionPanel = createAppVersionPanel();
         
-        addRowWithSpacing(panel, mowerIdPanel);
+    addRowWithSpacing(panel, mowerIdPanel);
+    addRowWithSpacing(panel, baseStationIdPanel);
         addRowWithSpacing(panel, handheldPanel);
     addRowWithSpacing(panel, simCardPanel);
     addRowWithSpacing(panel, baseStationSimPanel);
@@ -432,6 +440,10 @@
             mowerIdLabel.setText(setData.getMowerId() != null ? setData.getMowerId() : "鏈缃�");
         }
 
+        if (baseStationIdLabel != null) {
+            baseStationIdLabel.setText(resolveBaseStationId());
+        }
+
         if (handheldMarkerLabel != null) {
             handheldMarkerLabel.setText(setData.getHandheldMarkerId() != null ? setData.getHandheldMarkerId() : "鏈缃�");
         }
@@ -477,12 +489,31 @@
         }
         return trimmed;
     }
+
+    private String resolveBaseStationId() {
+        if (baseStation == null) {
+            return "鏈缃�";
+        }
+        String value = baseStation.getDeviceId();
+        if (value == null) {
+            return "鏈缃�";
+        }
+        String trimmed = value.trim();
+        if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+            return "鏈缃�";
+        }
+        return trimmed;
+    }
     
     private void setupEventHandlers() {
         // 鍓茶崏鏈虹紪鍙风紪杈戞寜閽簨浠�
         if (mowerIdEditBtn != null) {
             mowerIdEditBtn.addActionListener(e -> editMowerId());
         }
+
+        if (baseStationIdEditBtn != null) {
+            baseStationIdEditBtn.addActionListener(e -> editBaseStationId());
+        }
         
         // 妫�鏌ユ洿鏂版寜閽簨浠�
         if (checkUpdateBtn != null) {
@@ -559,6 +590,45 @@
         }
     }
 
+    private void editBaseStationId() {
+        String currentValue = "鏈缃�".equals(resolveBaseStationId()) ? "" : resolveBaseStationId();
+        String newValue = (String) JOptionPane.showInputDialog(this,
+                "璇疯緭鍏ュ樊鍒嗗熀鍑嗙珯缂栧彿:",
+                "淇敼宸垎鍩哄噯绔欑紪鍙�",
+                JOptionPane.QUESTION_MESSAGE,
+                null,
+                null,
+                currentValue);
+
+        if (newValue == null) {
+            return;
+        }
+
+        newValue = newValue.trim();
+        if (newValue.isEmpty()) {
+            JOptionPane.showMessageDialog(this, "宸垎鍩哄噯绔欑紪鍙蜂笉鑳戒负绌�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+
+        try {
+            baseStation.updateByDeviceId(newValue,
+                    baseStation.getInstallationCoordinates(),
+                    baseStation.getIotSimCardNumber(),
+                    baseStation.getDeviceActivationTime(),
+                    baseStation.getDataUpdateTime());
+            baseStation.load();
+            if (baseStationIdLabel != null) {
+                baseStationIdLabel.setText(resolveBaseStationId());
+            }
+            JOptionPane.showMessageDialog(this, "宸垎鍩哄噯绔欑紪鍙锋洿鏂版垚鍔�", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+        } catch (IllegalArgumentException ex) {
+            JOptionPane.showMessageDialog(this, ex.getMessage(), "杈撳叆閿欒", JOptionPane.WARNING_MESSAGE);
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            JOptionPane.showMessageDialog(this, "宸垎鍩哄噯绔欑紪鍙锋洿鏂板け璐�", "閿欒", JOptionPane.ERROR_MESSAGE);
+        }
+    }
+
     private void editIdleTrailDuration() {
         int currentSeconds = setData != null ? setData.getIdleTrailDurationSeconds() : Setsys.DEFAULT_IDLE_TRAIL_DURATION_SECONDS;
         if (currentSeconds <= 0) {
diff --git a/src/zhangaiwu/AddDikuai.java b/src/zhangaiwu/AddDikuai.java
index ee81258..263b539 100644
--- a/src/zhangaiwu/AddDikuai.java
+++ b/src/zhangaiwu/AddDikuai.java
@@ -14,16 +14,17 @@
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Comparator;
 import java.awt.geom.Point2D;
 
+import baseStation.BaseStation;
 import bianjie.jisuanmianjie;
 import dikuai.Dikuai;
 import dikuai.Dikuaiguanli;
-import gecaoji.Device;
 import bianjie.bianjieguihua2;
 import lujing.Lunjingguihua;
 import ui.UIConfig;
@@ -49,6 +50,9 @@
     private final Color LIGHT_TEXT = new Color(108, 117, 125);
     private final Color BORDER_COLOR = new Color(222, 226, 230);
     private final Color SUCCESS_COLOR = new Color(40, 167, 69);
+    private final Color ERROR_COLOR = new Color(220, 53, 69);
+    private static final String KEY_PATH_MESSAGE_TEXT = "__pathMessageText";
+    private static final String KEY_PATH_MESSAGE_SUCCESS = "__pathMessageSuccess";
     
     // 姝ラ闈㈡澘
     private JPanel mainPanel;
@@ -68,8 +72,12 @@
     private JButton prevButton;
     private JButton nextButton;
     private JButton createButton;
+    private JButton previewButton;
+    private Component previewButtonSpacer;
     private JLabel boundaryCountLabel;
     private JPanel obstacleListContainer;
+    private JTextArea pathGenerationMessageArea;
+    private JPanel pathMessageWrapper;
     
     // 鍦板潡鏁版嵁
     private Map<String, String> dikuaiData = new HashMap<>();
@@ -967,6 +975,29 @@
 
         stepPanel.add(Box.createRigidArea(new Dimension(0, 20)));
         stepPanel.add(generatePathButton);
+        stepPanel.add(Box.createRigidArea(new Dimension(0, 12)));
+
+        pathMessageWrapper = new JPanel(new BorderLayout());
+        pathMessageWrapper.setAlignmentX(Component.LEFT_ALIGNMENT);
+        pathMessageWrapper.setBackground(PRIMARY_LIGHT);
+        pathMessageWrapper.setBorder(BorderFactory.createCompoundBorder(
+            BorderFactory.createLineBorder(PRIMARY_COLOR, 1),
+            BorderFactory.createEmptyBorder(12, 12, 12, 12)
+        ));
+        pathMessageWrapper.setVisible(false);
+
+        pathGenerationMessageArea = new JTextArea();
+        pathGenerationMessageArea.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+        pathGenerationMessageArea.setForeground(TEXT_COLOR);
+        pathGenerationMessageArea.setOpaque(false);
+        pathGenerationMessageArea.setEditable(false);
+        pathGenerationMessageArea.setLineWrap(true);
+        pathGenerationMessageArea.setWrapStyleWord(true);
+        pathGenerationMessageArea.setFocusable(false);
+        pathGenerationMessageArea.setBorder(null);
+
+        pathMessageWrapper.add(pathGenerationMessageArea, BorderLayout.CENTER);
+        stepPanel.add(pathMessageWrapper);
         stepPanel.add(Box.createVerticalGlue());
 
         return stepPanel;
@@ -1026,6 +1057,9 @@
     private void generateMowingPath() {
         if (!dikuaiData.containsKey("boundaryDrawn")) {
             JOptionPane.showMessageDialog(this, "璇峰厛瀹屾垚杈圭晫缁樺埗鍚庡啀鐢熸垚璺緞", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("璇峰厛瀹屾垚杈圭晫缁樺埗鍚庡啀鐢熸垚璺緞銆�", false);
+            setPathAvailability(false);
             showStep(2);
             return;
         }
@@ -1039,9 +1073,9 @@
         }
         if (boundaryCoords == null) {
             JOptionPane.showMessageDialog(this, "鏈壘鍒版湁鏁堢殑鍦板潡杈圭晫鍧愭爣锛屾棤娉曠敓鎴愯矾寰�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
-            if (createButton != null) {
-                createButton.setEnabled(false);
-            }
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("鏈壘鍒版湁鏁堢殑鍦板潡杈圭晫鍧愭爣锛屾棤娉曠敓鎴愯矾寰勩��", false);
+            setPathAvailability(false);
             return;
         }
 
@@ -1060,17 +1094,17 @@
         Object widthObj = mowingWidthSpinner.getValue();
         if (!(widthObj instanceof Number)) {
             JOptionPane.showMessageDialog(this, "鍓茶崏瀹藉害杈撳叆鏃犳晥", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
-            if (createButton != null) {
-                createButton.setEnabled(false);
-            }
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("鍓茶崏瀹藉害杈撳叆鏃犳晥锛岃閲嶆柊杈撳叆銆�", false);
+            setPathAvailability(false);
             return;
         }
         double widthCm = ((Number) widthObj).doubleValue();
         if (widthCm <= 0) {
             JOptionPane.showMessageDialog(this, "鍓茶崏瀹藉害蹇呴』澶т簬0", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
-            if (createButton != null) {
-                createButton.setEnabled(false);
-            }
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("鍓茶崏瀹藉害蹇呴』澶т簬0锛岃閲嶆柊璁剧疆銆�", false);
+            setPathAvailability(false);
             return;
         }
         dikuaiData.put("mowingWidth", widthObj.toString());
@@ -1088,31 +1122,141 @@
             String plannedPath = Lunjingguihua.formatPathSegments(segments);
             if (!isMeaningfulValue(plannedPath)) {
                 JOptionPane.showMessageDialog(this, "鐢熸垚鍓茶崏璺緞澶辫触: 鐢熸垚缁撴灉涓虹┖", "閿欒", JOptionPane.ERROR_MESSAGE);
-                if (createButton != null) {
-                    createButton.setEnabled(false);
-                }
+                dikuaiData.remove("plannedPath");
+                showPathGenerationMessage("鐢熸垚鍓茶崏璺緞澶辫触锛氱敓鎴愮粨鏋滀负绌恒��", false);
+                setPathAvailability(false);
                 return;
             }
-            dikuaiData.put("plannedPath", plannedPath);
-            if (createButton != null) {
-                createButton.setEnabled(true);
+            if (isMeaningfulValue(boundaryCoords)) {
+                dikuaiData.put("boundaryCoordinates", boundaryCoords);
             }
-            JOptionPane.showMessageDialog(this,
-                "宸叉牴鎹綋鍓嶈缃敓鎴愬壊鑽夎矾寰勶紝鍏辩敓鎴� " + segments.size() + " 娈点��",
-                "鎴愬姛",
-                JOptionPane.INFORMATION_MESSAGE);
+            if (isMeaningfulValue(obstacleCoords)) {
+                dikuaiData.put("obstacleCoordinates", obstacleCoords);
+            }
+            dikuaiData.put("plannedPath", plannedPath);
+            setPathAvailability(true);
+            showPathGenerationMessage(
+                "宸叉牴鎹綋鍓嶈缃敓鎴愬壊鑽夎矾寰勶紝鍏辩敓鎴� " + segments.size() + " 娈点�俓n鐐瑰嚮鈥滈瑙堚�濇寜閽彲鍦ㄤ富椤甸潰鏌ョ湅鏁堟灉銆�",
+                true);
         } catch (IllegalArgumentException ex) {
             JOptionPane.showMessageDialog(this, "鐢熸垚鍓茶崏璺緞澶辫触: " + ex.getMessage(), "閿欒", JOptionPane.ERROR_MESSAGE);
-            if (createButton != null) {
-                createButton.setEnabled(false);
-            }
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("鐢熸垚鍓茶崏璺緞澶辫触锛�" + ex.getMessage(), false);
+            setPathAvailability(false);
         } catch (Exception ex) {
             ex.printStackTrace();
             JOptionPane.showMessageDialog(this, "鐢熸垚鍓茶崏璺緞鏃跺彂鐢熷紓甯�: " + ex.getMessage(), "閿欒", JOptionPane.ERROR_MESSAGE);
-            if (createButton != null) {
-                createButton.setEnabled(false);
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("鐢熸垚鍓茶崏璺緞鏃跺彂鐢熷紓甯革細" + ex.getMessage(), false);
+            setPathAvailability(false);
+        }
+    }
+
+    private void previewMowingPath() {
+        if (!hasGeneratedPath()) {
+            showPathGenerationMessage("璇峰厛鐢熸垚鍓茶崏璺緞鍚庡啀棰勮銆�", false);
+            setPathAvailability(false);
+            return;
+        }
+
+        persistStep3Inputs();
+
+        String landNumber = getPendingLandNumber();
+        String trimmedAreaName = areaNameField.getText() != null ? areaNameField.getText().trim() : "";
+        String displayAreaName = isMeaningfulValue(trimmedAreaName) ? trimmedAreaName : landNumber;
+
+        String plannedPath = dikuaiData.get("plannedPath");
+        if (!isMeaningfulValue(plannedPath)) {
+            showPathGenerationMessage("璇峰厛鐢熸垚鍓茶崏璺緞鍚庡啀棰勮銆�", false);
+            setPathAvailability(false);
+            return;
+        }
+
+        String boundary = null;
+        Dikuai pending = getOrCreatePendingDikuai();
+        if (pending != null) {
+            boundary = normalizeCoordinateValue(pending.getBoundaryCoordinates());
+        }
+        if (boundary == null) {
+            boundary = normalizeCoordinateValue(dikuaiData.get("boundaryCoordinates"));
+        }
+
+        String obstacles = normalizeCoordinateValue(dikuaiData.get("obstacleCoordinates"));
+        if (!isMeaningfulValue(obstacles)) {
+            obstacles = resolveObstaclePayloadFromConfig(landNumber);
+            if (isMeaningfulValue(obstacles)) {
+                dikuaiData.put("obstacleCoordinates", obstacles);
             }
         }
+
+        Shouye shouye = Shouye.getInstance();
+        if (shouye == null) {
+            JOptionPane.showMessageDialog(this, "鏃犳硶鎵撳紑涓婚〉闈紝璇风◢鍚庨噸璇�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+
+        dikuaiData.put("areaName", trimmedAreaName);
+        if (isMeaningfulValue(boundary)) {
+            dikuaiData.put("boundaryCoordinates", boundary);
+        }
+
+        pendingLandNumber = landNumber;
+        captureSessionSnapshot();
+
+        resumeRequested = true;
+        boolean started = shouye.startMowingPathPreview(
+            landNumber,
+            displayAreaName,
+            boundary,
+            obstacles,
+            plannedPath,
+            AddDikuai::resumeFromPreview
+        );
+        if (!started) {
+            resumeRequested = false;
+            JOptionPane.showMessageDialog(this, "鏃犳硶鍚姩棰勮锛岃绋嶅悗鍐嶈瘯", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+
+        closePreviewAndDispose();
+    }
+
+    private void persistStep3Inputs() {
+        String trimmedName = areaNameField.getText() != null ? areaNameField.getText().trim() : "";
+        dikuaiData.put("areaName", trimmedName);
+
+        if (mowingPatternCombo != null) {
+            Object selection = mowingPatternCombo.getSelectedItem();
+            if (selection != null) {
+                dikuaiData.put("mowingPattern", selection.toString());
+            }
+        }
+
+        if (mowingWidthSpinner != null) {
+            Object widthValue = mowingWidthSpinner.getValue();
+            if (widthValue instanceof Number) {
+                int widthInt = ((Number) widthValue).intValue();
+                dikuaiData.put("mowingWidth", Integer.toString(widthInt));
+            } else if (widthValue != null) {
+                dikuaiData.put("mowingWidth", widthValue.toString());
+            }
+        }
+    }
+
+    private void captureSessionSnapshot() {
+        if (activeSession == null) {
+            activeSession = new DrawingSession();
+        }
+        String landNumber = getPendingLandNumber();
+        activeSession.landNumber = landNumber;
+        activeSession.areaName = areaNameField.getText() != null ? areaNameField.getText().trim() : "";
+        activeSession.drawingCompleted = true;
+        activeSession.data = new HashMap<>(dikuaiData);
+    }
+
+    private void closePreviewAndDispose() {
+        setVisible(false);
+        dispose();
     }
     
     private JButton createPrimaryButton(String text, int fontSize) {
@@ -1146,6 +1290,44 @@
         
         return button;
     }
+
+    private void showPathGenerationMessage(String message, boolean success) {
+        if (pathGenerationMessageArea == null || pathMessageWrapper == null) {
+            return;
+        }
+        String display = message == null ? "" : message.trim();
+        if (display.isEmpty()) {
+            dikuaiData.remove(KEY_PATH_MESSAGE_TEXT);
+            dikuaiData.remove(KEY_PATH_MESSAGE_SUCCESS);
+        } else {
+            dikuaiData.put(KEY_PATH_MESSAGE_TEXT, display);
+            dikuaiData.put(KEY_PATH_MESSAGE_SUCCESS, success ? "true" : "false");
+        }
+        pathGenerationMessageArea.setText(display);
+        Color borderColor = success ? PRIMARY_COLOR : ERROR_COLOR;
+        Color textColor = success ? PRIMARY_DARK : ERROR_COLOR;
+        Color backgroundColor = success ? PRIMARY_LIGHT : new Color(255, 235, 238);
+        pathGenerationMessageArea.setForeground(textColor);
+        pathMessageWrapper.setBackground(backgroundColor);
+        pathMessageWrapper.setBorder(BorderFactory.createCompoundBorder(
+            BorderFactory.createLineBorder(borderColor, 1),
+            BorderFactory.createEmptyBorder(12, 12, 12, 12)
+        ));
+        pathMessageWrapper.setVisible(!display.isEmpty());
+        pathMessageWrapper.revalidate();
+        pathMessageWrapper.repaint();
+    }
+
+    private void setPathAvailability(boolean available) {
+        boolean effective = available && currentStep == 3;
+        if (createButton != null) {
+            createButton.setEnabled(effective);
+        }
+        if (previewButton != null) {
+            boolean visible = previewButton.isVisible();
+            previewButton.setEnabled(effective && visible);
+        }
+    }
     
     private JPanel createButtonPanel() {
         JPanel buttonPanel = new JPanel();
@@ -1167,11 +1349,20 @@
         nextButton = createPrimaryButton("涓嬩竴姝�", 16);
         createButton = createPrimaryButton("淇濆瓨", 16);
         createButton.setVisible(false);
-    createButton.setEnabled(false);
+        createButton.setEnabled(false);
+
+        previewButton = createPrimaryButton("棰勮", 16);
+        previewButton.setVisible(false);
+        previewButton.setEnabled(false);
+
+        previewButtonSpacer = Box.createHorizontalStrut(15);
+        previewButtonSpacer.setVisible(false);
 
         buttonPanel.add(prevButton);
         buttonPanel.add(Box.createHorizontalGlue());
         buttonPanel.add(nextButton);
+        buttonPanel.add(previewButtonSpacer);
+        buttonPanel.add(previewButton);
         buttonPanel.add(Box.createHorizontalStrut(15));
         buttonPanel.add(createButton);
 
@@ -1223,14 +1414,40 @@
         return true;
     }
 
-    private static String buildOriginalBoundaryString() {
-        if (Coordinate.coordinates == null || Coordinate.coordinates.isEmpty()) {
+    private static List<Coordinate> sanitizeCoordinateList(List<Coordinate> source) {
+        if (source == null || source.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        List<Coordinate> snapshot = new ArrayList<>();
+        for (Coordinate coordinate : source) {
+            if (coordinate != null) {
+                snapshot.add(coordinate);
+            }
+        }
+        if (snapshot.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        DecimalFormat latLonFormat = new DecimalFormat("0.000000");
+        LinkedHashMap<String, Coordinate> unique = new LinkedHashMap<>();
+        for (Coordinate coord : snapshot) {
+            double lat = convertToDecimalDegree(coord.getLatitude(), coord.getLatDirection());
+            double lon = convertToDecimalDegree(coord.getLongitude(), coord.getLonDirection());
+            String key = latLonFormat.format(lat) + "," + latLonFormat.format(lon);
+            unique.putIfAbsent(key, coord);
+        }
+        return new ArrayList<>(unique.values());
+    }
+
+    private static String buildOriginalBoundaryString(List<Coordinate> coordinates) {
+        if (coordinates == null || coordinates.isEmpty()) {
             return "-1";
         }
         StringBuilder sb = new StringBuilder();
         DecimalFormat latLonFormat = new DecimalFormat("0.000000");
         DecimalFormat elevationFormat = new DecimalFormat("0.00");
-        for (Coordinate coord : Coordinate.coordinates) {
+        for (Coordinate coord : coordinates) {
             double lat = convertToDecimalDegree(coord.getLatitude(), coord.getLatDirection());
             double lon = convertToDecimalDegree(coord.getLongitude(), coord.getLonDirection());
             double elevation = coord.getElevation();
@@ -1334,8 +1551,14 @@
     }
 
     private static BoundarySnapshotResult computeBoundarySnapshot() {
-        int count = Coordinate.coordinates != null ? Coordinate.coordinates.size() : 0;
-        if (count < 3) {
+        List<Coordinate> uniqueCoordinates;
+        synchronized (Coordinate.coordinates) {
+            uniqueCoordinates = sanitizeCoordinateList(Coordinate.coordinates);
+            Coordinate.coordinates.clear();
+            Coordinate.coordinates.addAll(uniqueCoordinates);
+        }
+
+        if (uniqueCoordinates.size() < 3) {
             return BoundarySnapshotResult.failure("閲囬泦鐨勮竟鐣岀偣涓嶈冻锛屾棤娉曠敓鎴愬湴鍧楄竟鐣�", JOptionPane.WARNING_MESSAGE);
         }
 
@@ -1344,9 +1567,9 @@
             return BoundarySnapshotResult.failure("褰撳墠鍦板潡闈㈢Н涓�0锛屾棤娉曠户缁�", JOptionPane.WARNING_MESSAGE);
         }
 
-        Device device = new Device();
-        device.initFromProperties();
-        String baseStationCoordinates = normalizeCoordinateValue(device.getBaseStationCoordinates());
+    BaseStation baseStation = new BaseStation();
+    baseStation.load();
+    String baseStationCoordinates = normalizeCoordinateValue(baseStation.getInstallationCoordinates());
         if (!isMeaningfulValue(baseStationCoordinates)) {
             return BoundarySnapshotResult.failure("鏈幏鍙栧埌鏈夋晥鐨勫熀鍑嗙珯鍧愭爣锛岃鍏堝湪鍩哄噯绔欑鐞嗕腑璁剧疆", JOptionPane.WARNING_MESSAGE);
         }
@@ -1359,7 +1582,7 @@
             return BoundarySnapshotResult.failure("鐢熸垚鍦板潡杈圭晫澶辫触: " + ex.getMessage(), JOptionPane.ERROR_MESSAGE);
         }
 
-        String originalBoundary = buildOriginalBoundaryString();
+    String originalBoundary = buildOriginalBoundaryString(uniqueCoordinates);
         DecimalFormat areaFormat = new DecimalFormat("0.00");
         String areaString = areaFormat.format(area);
 
@@ -1407,6 +1630,10 @@
         
         // 鍒涘缓鍦板潡鎸夐挳
         createButton.addActionListener(e -> createDikuai());
+
+        if (previewButton != null) {
+            previewButton.addActionListener(e -> previewMowingPath());
+        }
         
         // 鍏抽棴瀵硅瘽妗�
         addWindowListener(new WindowAdapter() {
@@ -1435,11 +1662,24 @@
         if (step < 3) {
             nextButton.setVisible(true);
             createButton.setVisible(false);
-            createButton.setEnabled(false);
+            setPathAvailability(false);
+            if (previewButton != null) {
+                previewButton.setVisible(false);
+                previewButton.setEnabled(false);
+            }
+            if (previewButtonSpacer != null) {
+                previewButtonSpacer.setVisible(false);
+            }
         } else {
             nextButton.setVisible(false);
             createButton.setVisible(true);
-            createButton.setEnabled(hasGeneratedPath());
+            if (previewButton != null) {
+                previewButton.setVisible(true);
+            }
+            if (previewButtonSpacer != null) {
+                previewButtonSpacer.setVisible(true);
+            }
+            setPathAvailability(hasGeneratedPath());
         }
 
         Container parent = prevButton.getParent();
@@ -1736,6 +1976,71 @@
             showStep(1);
             hideBoundaryPointSummary();
         }
+
+        restoreGeneratedPathState(session);
+    }
+
+    private void restoreGeneratedPathState(DrawingSession session) {
+        if (session == null || session.data == null) {
+            showPathGenerationMessage("", true);
+            return;
+        }
+
+        Map<String, String> data = session.data;
+
+        if (mowingPatternCombo != null) {
+            String pattern = data.get("mowingPattern");
+            if (pattern != null) {
+                ComboBoxModel<String> model = mowingPatternCombo.getModel();
+                for (int i = 0; i < model.getSize(); i++) {
+                    String candidate = model.getElementAt(i);
+                    if (pattern.equals(candidate)) {
+                        mowingPatternCombo.setSelectedIndex(i);
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (mowingWidthSpinner != null) {
+            String width = data.get("mowingWidth");
+            if (isMeaningfulValue(width)) {
+                try {
+                    double parsed = Double.parseDouble(width.trim());
+                    SpinnerNumberModel model = (SpinnerNumberModel) mowingWidthSpinner.getModel();
+                    int min = ((Number) model.getMinimum()).intValue();
+                    int max = ((Number) model.getMaximum()).intValue();
+                    int rounded = (int) Math.round(parsed);
+                    if (rounded < min) {
+                        rounded = min;
+                    } else if (rounded > max) {
+                        rounded = max;
+                    }
+                    mowingWidthSpinner.setValue(rounded);
+                } catch (NumberFormatException ignored) {
+                    // 淇濇寔褰撳墠鍊�
+                }
+            }
+        }
+
+        boolean hasPath = isMeaningfulValue(data.get("plannedPath"));
+        if (!hasPath) {
+            showPathGenerationMessage("", true);
+            if (currentStep == 3) {
+                setPathAvailability(false);
+            }
+            return;
+        }
+
+        String message = data.get(KEY_PATH_MESSAGE_TEXT);
+        boolean success = !"false".equalsIgnoreCase(data.get(KEY_PATH_MESSAGE_SUCCESS));
+        showStep(3);
+        if (isMeaningfulValue(message)) {
+            showPathGenerationMessage(message, success);
+        } else {
+            showPathGenerationMessage("宸茬敓鎴愬壊鑽夎矾寰勶紝鍙偣鍑烩�滈瑙堚�濇寜閽煡鐪嬫晥鏋溿��", true);
+        }
+        setPathAvailability(true);
     }
 
     public static void finishDrawingSession() {
@@ -1769,6 +2074,19 @@
         Component parent = shouye != null ? shouye : null;
         showAddDikuaiDialog(parent);
     }
+
+    public static void resumeFromPreview() {
+        Shouye shouye = Shouye.getInstance();
+        if (shouye != null) {
+            shouye.exitMowingPathPreview();
+        }
+        if (activeSession == null) {
+            return;
+        }
+        resumeRequested = true;
+        Component parent = shouye != null ? shouye : null;
+        SwingUtilities.invokeLater(() -> showAddDikuaiDialog(parent));
+    }
     
     private void createDikuai() {
         if (!validateCurrentStep()) {
diff --git a/src/zhuye/HandheldBoundaryCaptureDialog.java b/src/zhuye/HandheldBoundaryCaptureDialog.java
deleted file mode 100644
index bb56d7b..0000000
--- a/src/zhuye/HandheldBoundaryCaptureDialog.java
+++ /dev/null
@@ -1,229 +0,0 @@
-package zhuye;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.IllegalComponentStateException;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-
-import gecaoji.Device;
-
-/**
- * 鎵嬫寔缁樺埗杈圭晫鐨勯�愮偣閲囬泦瀵硅瘽妗嗐��
- */
-class HandheldBoundaryCaptureDialog extends JDialog {
-    private final Shouye owner;
-    private final JLabel instructionLabel;
-    private final JButton confirmButton;
-    private final JButton finishButton;
-    private final JPanel mapPanel;
-    private final Timer statusMonitor;
-    private final Color enabledColor;
-    private int nextPointIndex = 1;
-
-    HandheldBoundaryCaptureDialog(Window parent, Shouye owner, JPanel mapPanel, Color enabledColor) {
-        super(parent, "鎵嬫寔閲囬泦杈圭晫", ModalityType.APPLICATION_MODAL);
-        this.owner = owner;
-        this.mapPanel = mapPanel;
-        this.enabledColor = enabledColor != null ? enabledColor : new Color(46, 139, 87);
-        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
-        setResizable(false);
-        setAlwaysOnTop(true);
-
-        instructionLabel = new JLabel("", SwingConstants.CENTER);
-        instructionLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, Math.max(11, (int) Math.round(16 * 0.7))));
-        instructionLabel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));
-        updateInstructionText();
-
-        confirmButton = new JButton("纭畾");
-        confirmButton.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
-        confirmButton.setFocusPainted(false);
-        confirmButton.setOpaque(true);
-        confirmButton.setBorder(BorderFactory.createEmptyBorder(8, 18, 8, 18));
-        confirmButton.addActionListener(e -> handleConfirm());
-
-        finishButton = new JButton("缁撴潫");
-        finishButton.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
-        finishButton.setFocusPainted(false);
-        finishButton.setOpaque(true);
-        finishButton.setBackground(new Color(255, 153, 0));
-        finishButton.setForeground(Color.WHITE);
-        finishButton.setEnabled(false);
-        finishButton.setVisible(false);
-        finishButton.setBorder(BorderFactory.createEmptyBorder(8, 18, 8, 18));
-        finishButton.addActionListener(e -> handleFinish());
-
-        buildLayout();
-        alignButtonSizes();
-        getRootPane().setDefaultButton(confirmButton);
-        pack();
-        Dimension initialSize = getSize();
-        if (initialSize != null && initialSize.width > 0) {
-            setSize(new Dimension(initialSize.width * 2, initialSize.height));
-        }
-        adjustPreferredSize();
-        positionNearMap();
-        updateActionButtons();
-        SwingUtilities.invokeLater(confirmButton::requestFocusInWindow);
-
-        statusMonitor = new Timer(400, e -> refreshStatus());
-        statusMonitor.setRepeats(true);
-        statusMonitor.start();
-        refreshStatus();
-
-        addWindowListener(new WindowAdapter() {
-            @Override
-            public void windowClosing(WindowEvent e) {
-                dispose();
-            }
-        });
-    }
-
-    private void buildLayout() {
-        JPanel content = new JPanel(new BorderLayout());
-        content.setBorder(BorderFactory.createEmptyBorder(20, 24, 20, 24));
-        content.add(instructionLabel, BorderLayout.CENTER);
-
-        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 16, 0));
-        buttonPanel.add(confirmButton);
-        buttonPanel.add(finishButton);
-        content.add(buttonPanel, BorderLayout.SOUTH);
-
-        setContentPane(content);
-    }
-
-    private void adjustPreferredSize() {
-        Dimension size = getSize();
-        if (size.height > 0) {
-            int adjustedHeight = Math.max((int) (size.height * 0.7), 160);
-            setSize(new Dimension(size.width, adjustedHeight));
-        }
-    }
-
-    private void positionNearMap() {
-        final int margin = 16;
-        if (owner != null && owner.isShowing()) {
-            try {
-                Point ownerLocation = owner.getLocationOnScreen();
-                Dimension ownerSize = owner.getSize();
-                Dimension dialogSize = getSize();
-                int x = ownerLocation.x + (ownerSize.width - dialogSize.width) / 2;
-                int y = ownerLocation.y + ownerSize.height - dialogSize.height - margin;
-                if (x < ownerLocation.x + margin) {
-                    x = ownerLocation.x + margin;
-                }
-                if (y < ownerLocation.y + margin) {
-                    y = ownerLocation.y + margin;
-                }
-                setLocation(x, y);
-                return;
-            } catch (IllegalComponentStateException ignored) {
-                // fallback handled below
-            }
-        } else if (mapPanel != null && mapPanel.isShowing()) {
-            try {
-                Point mapLocation = mapPanel.getLocationOnScreen();
-                Dimension mapSize = mapPanel.getSize();
-                Dimension dialogSize = getSize();
-                int x = mapLocation.x + (mapSize.width - dialogSize.width) / 2;
-                int y = mapLocation.y + mapSize.height - dialogSize.height - margin;
-                setLocation(x, y);
-                return;
-            } catch (IllegalComponentStateException ignored) {
-                // fallback handled below
-            }
-        }
-        setLocationRelativeTo(owner);
-    }
-
-    private void handleConfirm() {
-        if (!confirmButton.isEnabled()) {
-            return;
-        }
-        int count = owner.captureHandheldBoundaryPoint();
-        if (count <= 0) {
-            return;
-        }
-        nextPointIndex = count + 1;
-        updateInstructionText();
-        updateActionButtons();
-    }
-
-    private void handleFinish() {
-        if (owner.finishHandheldBoundaryCapture()) {
-            dispose();
-        }
-    }
-
-    @Override
-    public void dispose() {
-        if (statusMonitor != null) {
-            statusMonitor.stop();
-        }
-        super.dispose();
-        owner.handheldBoundaryCaptureDialogClosed(this);
-    }
-
-    private void refreshStatus() {
-        boolean hasFix = isFixQualityAvailable();
-        boolean hasValidPosition = hasFix && hasValidCurrentPosition();
-        boolean duplicate = hasValidPosition && isDuplicateCurrentPosition();
-        boolean enabled = hasValidPosition && !duplicate;
-        confirmButton.setEnabled(enabled);
-        if (enabled) {
-            confirmButton.setBackground(enabledColor);
-            confirmButton.setForeground(Color.WHITE);
-        } else {
-            confirmButton.setBackground(new Color(200, 200, 200));
-            confirmButton.setForeground(new Color(130, 130, 130));
-        }
-        if (!hasFix) {
-            confirmButton.setToolTipText("褰撳墠瀹氫綅璐ㄩ噺涓嶈冻锛屾棤娉曢噰闆�");
-        } else if (!hasValidPosition) {
-            confirmButton.setToolTipText("褰撳墠瀹氫綅鏁版嵁鏃犳晥锛岃绋嶅悗鍐嶈瘯");
-        } else if (duplicate) {
-            confirmButton.setToolTipText("褰撳墠鍧愭爣宸查噰闆嗭紝璇风Щ鍔ㄥ埌鏂扮殑浣嶇疆");
-        } else {
-            confirmButton.setToolTipText(null);
-        }
-    }
-
-    private boolean isDuplicateCurrentPosition() {
-        return owner != null && owner.isCurrentHandheldPointDuplicate();
-    }
-
-    private boolean hasValidCurrentPosition() {
-        return owner != null && owner.hasValidRealtimeHandheldPosition();
-    }
-
-    private void updateInstructionText() {
-        instructionLabel.setText("鏄惁閲囬泦褰撳墠鐐�" + nextPointIndex);
-    }
-
-    private void alignButtonSizes() {
-        Dimension confirmSize = confirmButton.getPreferredSize();
-        finishButton.setPreferredSize(confirmSize);
-        finishButton.setMinimumSize(confirmSize);
-        finishButton.setMaximumSize(confirmSize);
-    }
-
-    private void updateActionButtons() {
-        int totalPoints = owner != null ? owner.getHandheldCapturedPointCount() : 0;
-        boolean hasMinimumPoints = totalPoints >= 3;
-        finishButton.setVisible(hasMinimumPoints);
-        finishButton.setEnabled(hasMinimumPoints);
-        confirmButton.setVisible(true);
-    }
-
-    private boolean isFixQualityAvailable() {
-        Device device = Device.getGecaoji();
-        if (device == null) {
-            return false;
-        }
-        String status = device.getPositioningStatus();
-        if (status == null) {
-            return false;
-        }
-        return "4".equals(status.trim());
-    }
-}
diff --git a/src/zhuye/MapRenderer.java b/src/zhuye/MapRenderer.java
index 5f5dd06..4562a15 100644
--- a/src/zhuye/MapRenderer.java
+++ b/src/zhuye/MapRenderer.java
@@ -50,6 +50,7 @@
     private static final double CIRCLE_SAMPLE_SIZE = 0.54d;
     private static final double BOUNDARY_POINT_MERGE_THRESHOLD = 0.05;
     private static final double BOUNDARY_CONTAINS_TOLERANCE = 0.05;
+    private static final double PREVIEW_BOUNDARY_MARKER_SCALE = 0.25d;
     
     // 缁勪欢寮曠敤
     private JPanel visualizationPanel;
@@ -64,6 +65,8 @@
     private String currentObstacleLandNumber;
     private String boundaryName;
     private boolean boundaryPointsVisible;
+    private double boundaryPointSizeScale = 1.0d;
+    private boolean previewSizingEnabled;
     private String currentBoundaryLandNumber;
     private boolean dragInProgress;
     private final Gecaoji mower;
@@ -74,6 +77,7 @@
     private final List<Point2D.Double> realtimeMowingTrack = new ArrayList<>();
     private final Deque<tuowei.TrailSample> idleMowerTrail = new ArrayDeque<>();
     private final List<Point2D.Double> handheldBoundaryPreview = new ArrayList<>();
+    private double boundaryPreviewMarkerScale = 1.0d;
     private boolean realtimeTrackRecording;
     private String realtimeTrackLandNumber;
     private double mowerEffectiveWidthMeters;
@@ -264,19 +268,21 @@
             drawCircleCaptureOverlay(g2d, circleCaptureOverlay, scale);
         }
 
-        adddikuaiyulan.drawPreview(g2d, handheldBoundaryPreview, scale, handheldBoundaryPreviewActive);
+    adddikuaiyulan.drawPreview(g2d, handheldBoundaryPreview, scale, handheldBoundaryPreviewActive, boundaryPreviewMarkerScale);
 
         if (hasPlannedPath) {
             drawCurrentPlannedPath(g2d);
         }
 
         if (boundaryPointsVisible && hasBoundary) {
+            double markerScale = boundaryPointSizeScale * (previewSizingEnabled ? PREVIEW_BOUNDARY_MARKER_SCALE : 1.0d);
             pointandnumber.drawBoundaryPoints(
                 g2d,
                 currentBoundary,
                 scale,
                 BOUNDARY_POINT_MERGE_THRESHOLD,
-                BOUNDARY_POINT_COLOR
+                BOUNDARY_POINT_COLOR,
+                markerScale
             );
         }
 
@@ -648,6 +654,10 @@
         visualizationPanel.repaint();
     }
 
+    public void clearIdleTrail() {
+        clearIdleMowerTrail();
+    }
+
     public void setIdleTrailDurationSeconds(int seconds) {
         int sanitized = seconds;
         if (sanitized < 5 || sanitized > 600) {
@@ -865,7 +875,8 @@
     }
 
     private void drawCurrentPlannedPath(Graphics2D g2d) {
-        lujingdraw.drawPlannedPath(g2d, currentPlannedPath, scale);
+        double arrowScale = previewSizingEnabled ? 0.5d : 1.0d;
+        lujingdraw.drawPlannedPath(g2d, currentPlannedPath, scale, arrowScale);
     }
 
     private void drawCircleSampleMarkers(Graphics2D g2d, List<double[]> markers, double scale) {
@@ -1054,7 +1065,11 @@
 
     private double computeSelectionThresholdPixels() {
         double scaleFactor = Math.max(0.5, scale);
-        double markerDiameterWorld = Math.max(1.0, (10.0 / scaleFactor) * 0.2);
+        double diameterScale = boundaryPointSizeScale * (previewSizingEnabled ? PREVIEW_BOUNDARY_MARKER_SCALE : 1.0d);
+        if (!Double.isFinite(diameterScale) || diameterScale <= 0.0d) {
+            diameterScale = 1.0d;
+        }
+        double markerDiameterWorld = Math.max(1.0, (10.0 / scaleFactor) * 0.2 * diameterScale);
         double markerDiameterPixels = markerDiameterWorld * scale;
         return Math.max(8.0, markerDiameterPixels * 1.5);
     }
@@ -1830,6 +1845,65 @@
         visualizationPanel.repaint();
     }
 
+    public void setBoundaryPointSizeScale(double sizeScale) {
+        double normalized = (Double.isFinite(sizeScale) && sizeScale > 0.0d) ? sizeScale : 1.0d;
+        if (Math.abs(boundaryPointSizeScale - normalized) < 1e-6) {
+            return;
+        }
+        boundaryPointSizeScale = normalized;
+        if (visualizationPanel == null) {
+            return;
+        }
+        if (SwingUtilities.isEventDispatchThread()) {
+            visualizationPanel.repaint();
+        } else {
+            SwingUtilities.invokeLater(visualizationPanel::repaint);
+        }
+    }
+
+    public void setPathPreviewSizingEnabled(boolean enabled) {
+        previewSizingEnabled = enabled;
+        if (visualizationPanel == null) {
+            return;
+        }
+        if (SwingUtilities.isEventDispatchThread()) {
+            visualizationPanel.repaint();
+        } else {
+            SwingUtilities.invokeLater(visualizationPanel::repaint);
+        }
+    }
+
+    public void setBoundaryPreviewMarkerScale(double markerScale) {
+        double normalized = Double.isFinite(markerScale) && markerScale > 0.0d ? markerScale : 1.0d;
+        if (Math.abs(boundaryPreviewMarkerScale - normalized) < 1e-6) {
+            return;
+        }
+        boundaryPreviewMarkerScale = normalized;
+        if (visualizationPanel == null) {
+            return;
+        }
+        if (SwingUtilities.isEventDispatchThread()) {
+            visualizationPanel.repaint();
+        } else {
+            SwingUtilities.invokeLater(visualizationPanel::repaint);
+        }
+    }
+
+    public boolean setHandheldMowerIconActive(boolean handheldActive) {
+        if (mower == null) {
+            return false;
+        }
+        boolean changed = mower.useHandheldIcon(handheldActive);
+        if (changed && visualizationPanel != null) {
+            if (SwingUtilities.isEventDispatchThread()) {
+                visualizationPanel.repaint();
+            } else {
+                SwingUtilities.invokeLater(visualizationPanel::repaint);
+            }
+        }
+        return changed;
+    }
+
     public void beginHandheldBoundaryPreview() {
         handheldBoundaryPreviewActive = true;
         handheldBoundaryPreview.clear();
@@ -1859,6 +1933,7 @@
     public void clearHandheldBoundaryPreview() {
         handheldBoundaryPreviewActive = false;
         handheldBoundaryPreview.clear();
+        boundaryPreviewMarkerScale = 1.0d;
         visualizationPanel.repaint();
     }
 
@@ -1914,8 +1989,10 @@
             return;
         }
 
-        double width = Math.max(bounds.width, 1);
-        double height = Math.max(bounds.height, 1);
+        Rectangle2D.Double targetBounds = includeMowerInBounds(bounds);
+
+        double width = Math.max(targetBounds.width, 1);
+        double height = Math.max(targetBounds.height, 1);
 
         double targetWidth = width * 1.2;
         double targetHeight = height * 1.2;
@@ -1927,8 +2004,41 @@
         newScale = Math.max(0.05, Math.min(newScale, 50.0));
 
         this.scale = newScale;
-        this.translateX = -bounds.getCenterX();
-        this.translateY = -bounds.getCenterY();
+        this.translateX = -targetBounds.getCenterX();
+        this.translateY = -targetBounds.getCenterY();
+    }
+
+    // Keep the mower marker inside the viewport whenever the camera refits to scene bounds.
+    private Rectangle2D.Double includeMowerInBounds(Rectangle2D.Double bounds) {
+        Rectangle2D.Double expanded = new Rectangle2D.Double(
+            bounds.x,
+            bounds.y,
+            Math.max(0.0, bounds.width),
+            Math.max(0.0, bounds.height)
+        );
+
+        if (mower == null || !mower.hasValidPosition()) {
+            return expanded;
+        }
+
+        Point2D.Double mowerPosition = mower.getPosition();
+        if (mowerPosition == null
+            || !Double.isFinite(mowerPosition.x)
+            || !Double.isFinite(mowerPosition.y)) {
+            return expanded;
+        }
+
+        double minX = Math.min(expanded.x, mowerPosition.x);
+        double minY = Math.min(expanded.y, mowerPosition.y);
+        double maxX = Math.max(expanded.x + expanded.width, mowerPosition.x);
+        double maxY = Math.max(expanded.y + expanded.height, mowerPosition.y);
+
+        expanded.x = minX;
+        expanded.y = minY;
+        expanded.width = Math.max(0.0, maxX - minX);
+        expanded.height = Math.max(0.0, maxY - minY);
+
+        return expanded;
     }
 
     public void dispose() {
diff --git a/src/zhuye/Shouye.java b/src/zhuye/Shouye.java
index 10f9ca0..1dfd462 100644
--- a/src/zhuye/Shouye.java
+++ b/src/zhuye/Shouye.java
@@ -92,6 +92,11 @@
     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;
@@ -112,7 +117,6 @@
     private double[] circleBaseLatLon;
     private Timer circleDataMonitor;
     private Coordinate lastCapturedCoordinate;
-    private HandheldBoundaryCaptureDialog handheldCaptureDialog;
     private boolean handheldCaptureActive;
     private int handheldCapturedPoints;
     private final List<Point2D.Double> handheldTemporaryPoints = new ArrayList<>();
@@ -127,14 +131,26 @@
     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);
+        dellmessage.registerLineListener(serialLineListener);
         initializeUI();
         setupEventHandlers();
+        scheduleIdentifierCheck();
     }
 
     public static Shouye getInstance() {
@@ -174,6 +190,26 @@
         refreshMapForSelectedArea();
     }
 
+    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();
+                    });
+                }
+            }
+        };
+        addHierarchyListener(listener);
+    }
+
+    private void showInitialMowerSelfCheckDialogIfNeeded() {
+        zijian.showInitialPromptIfNeeded(this, this::showRemoteControlDialog);
+    }
+
     private void applyIdleTrailDurationFromSettings() {
         if (mapRenderer == null) {
             return;
@@ -584,8 +620,9 @@
         }
         if (remoteDialog != null) {
             positionRemoteDialogBottomCenter(remoteDialog);
+            zijian.markSelfCheckCompleted();
+            remoteDialog.setVisible(true);
         }
-        remoteDialog.setVisible(true);
     }
 
     private void positionRemoteDialogBottomCenter(RemoteControlDialog dialog) {
@@ -663,6 +700,142 @@
         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;
@@ -713,9 +886,22 @@
     }
     
     private void toggleStartPause() {
+        if (handheldCaptureInlineUiActive) {
+            handleHandheldConfirmAction();
+            return;
+        }
+        if (drawingControlModeActive) {
+            toggleDrawingPause();
+            return;
+        }
         if (startBtn == null) {
             return;
         }
+        if (startButtonShowingPause) {
+            if (!zijian.ensureBeforeMowing(this, this::showRemoteControlDialog)) {
+                return;
+            }
+        }
         startButtonShowingPause = !startButtonShowingPause;
         if (!startButtonShowingPause) {
             statusLabel.setText("浣滀笟涓�");
@@ -737,6 +923,14 @@
     }
 
     private void handleStopAction() {
+        if (handheldCaptureInlineUiActive) {
+            handleHandheldFinishAction();
+            return;
+        }
+        if (drawingControlModeActive) {
+            handleDrawingStopFromControlPanel();
+            return;
+        }
         stopButtonActive = !stopButtonActive;
         updateStopButtonIcon();
         if (stopButtonActive) {
@@ -751,6 +945,268 @@
         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;
@@ -1028,6 +1484,13 @@
         refreshMowerSpeedLabel();
     }
 
+    public void setHandheldMowerIconActive(boolean active) {
+        if (mapRenderer == null) {
+            return;
+        }
+        mapRenderer.setHandheldMowerIconActive(active);
+    }
+
     public boolean startMowerBoundaryCapture() {
         if (mapRenderer == null) {
             return false;
@@ -1037,6 +1500,8 @@
             return false;
         }
 
+        mapRenderer.clearIdleTrail();
+
         activeBoundaryMode = BoundaryCaptureMode.MOWER;
         mowerBoundaryCaptureActive = true;
         mowerBaseLatLon = baseLatLonCandidate;
@@ -1053,9 +1518,12 @@
         Coordinate.setStartSaveGngga(true);
 
         if (mapRenderer != null) {
+            mapRenderer.setBoundaryPreviewMarkerScale(2.0d);
             mapRenderer.beginHandheldBoundaryPreview();
         }
 
+        setHandheldMowerIconActive(false);
+
         startMowerBoundaryMonitor();
         return true;
     }
@@ -1064,15 +1532,12 @@
         if (mapRenderer == null) {
             return false;
         }
-        if (handheldCaptureDialog != null && handheldCaptureDialog.isShowing()) {
-            handheldCaptureDialog.toFront();
-            return true;
-        }
-
         if (activeBoundaryMode == BoundaryCaptureMode.MOWER) {
             stopMowerBoundaryCapture();
         }
 
+        mapRenderer.clearIdleTrail();
+
         activeBoundaryMode = BoundaryCaptureMode.HANDHELD;
         handheldCaptureActive = true;
         handheldCapturedPoints = 0;
@@ -1084,19 +1549,10 @@
             handheldTemporaryPoints.clear();
         }
         AddDikuai.recordTemporaryBoundaryPoints(Collections.emptyList());
-        mapRenderer.beginHandheldBoundaryPreview();
-
-        Window ownerWindow = SwingUtilities.getWindowAncestor(this);
-        SwingUtilities.invokeLater(() -> {
-            Window targetOwner = ownerWindow;
-            if (targetOwner == null) {
-                targetOwner = SwingUtilities.getWindowAncestor(Shouye.this);
-            }
-            HandheldBoundaryCaptureDialog dialog = new HandheldBoundaryCaptureDialog(targetOwner, Shouye.this, visualizationPanel, THEME_COLOR);
-            handheldCaptureDialog = dialog;
-            dialog.setVisible(true);
-        });
-
+    mapRenderer.setBoundaryPreviewMarkerScale(1.0d);
+    mapRenderer.beginHandheldBoundaryPreview();
+        setHandheldMowerIconActive(true);
+        enterHandheldCaptureInlineUi();
         return true;
     }
 
@@ -1188,6 +1644,7 @@
         if (activeBoundaryMode == BoundaryCaptureMode.MOWER) {
             activeBoundaryMode = BoundaryCaptureMode.NONE;
         }
+        setHandheldMowerIconActive(false);
     }
 
     private void discardLatestCoordinate(Coordinate coordinate) {
@@ -1282,6 +1739,7 @@
 
         List<Point2D.Double> closedSnapshot = createClosedHandheldPointSnapshot();
         handheldCaptureActive = false;
+        activeBoundaryMode = BoundaryCaptureMode.NONE;
         Coordinate.setStartSaveGngga(false);
         if (mapRenderer != null) {
             mapRenderer.clearHandheldBoundaryPreview();
@@ -1289,20 +1747,12 @@
 
     AddDikuai.recordTemporaryBoundaryPoints(closedSnapshot);
 
+        exitHandheldCaptureInlineUi();
+
         SwingUtilities.invokeLater(AddDikuai::finishDrawingSession);
         return true;
     }
 
-    void handheldBoundaryCaptureDialogClosed(HandheldBoundaryCaptureDialog dialog) {
-        if (handheldCaptureDialog == dialog) {
-            handheldCaptureDialog = null;
-        }
-        handheldCaptureActive = false;
-        if (activeBoundaryMode == BoundaryCaptureMode.HANDHELD) {
-            activeBoundaryMode = BoundaryCaptureMode.NONE;
-        }
-    }
-
     int getHandheldCapturedPointCount() {
         return handheldCapturedPoints;
     }
@@ -1571,9 +2021,9 @@
         if (statusLabel == null) {
             return;
         }
-        if ("浣滀笟涓�".equals(statusText)) {
+        if ("浣滀笟涓�".equals(statusText) || "缁樺埗涓�".equals(statusText)) {
             statusLabel.setForeground(THEME_COLOR);
-        } else if ("鏆傚仠涓�".equals(statusText)) {
+        } else if ("鏆傚仠涓�".equals(statusText) || "缁樺埗鏆傚仠".equals(statusText)) {
             statusLabel.setForeground(STATUS_PAUSE_COLOR);
         } else {
             statusLabel.setForeground(Color.GRAY);
@@ -1601,6 +2051,29 @@
         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);
@@ -1658,6 +2131,12 @@
         if (notifyCoordinate) {
             Coordinate.setStartSaveGngga(!paused);
         }
+        if (drawingControlModeActive) {
+            updateDrawingControlButtonLabels();
+            if (statusLabel != null) {
+                statusLabel.setText(paused ? "缁樺埗鏆傚仠" : "缁樺埗涓�");
+            }
+        }
     }
 
     private void toggleDrawingPause() {
@@ -1670,36 +2149,33 @@
 
     public void showEndDrawingButton(Runnable callback, String drawingShape) {
         endDrawingCallback = callback;
-        applyDrawingPauseState(false, false);
         circleDialogMode = false;
         hideCircleGuidancePanel();
-
-        ensureFloatingIconsLoaded();
-        ensureFloatingButtonInfrastructure();
+        enterDrawingControlMode();
 
         boolean enableCircleGuidance = drawingShape != null
                 && "circle".equalsIgnoreCase(drawingShape.trim());
         if (enableCircleGuidance) {
-            prepareCircleGuidanceState();
-            showCircleGuidanceStep(1);
-            endDrawingButton.setVisible(false);
+            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();
-            endDrawingButton.setVisible(true);
-            if (drawingPauseButton != null) {
-                drawingPauseButton.setVisible(true);
-            }
+            hideFloatingDrawingControls();
         }
 
-        floatingButtonPanel.setVisible(true);
-        if (floatingButtonPanel.getParent() != visualizationPanel) {
-            visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH);
-        }
-
-        rebuildFloatingButtonColumn();
         visualizationPanel.revalidate();
         visualizationPanel.repaint();
     }
@@ -1776,6 +2252,14 @@
                 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();
@@ -2184,21 +2668,27 @@
 
     private double[] resolveCircleBaseLatLon() {
         String coords = null;
-        String landNumber = Dikuaiguanli.getCurrentWorkLandNumber();
-        if (isMeaningfulValue(landNumber)) {
-            Dikuai current = Dikuai.getDikuai(landNumber);
-            if (current != null) {
-                coords = current.getBaseStationCoordinates();
+
+        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) && baseStation != null) {
-            coords = baseStation.getInstallationCoordinates();
-        }
-        if (!isMeaningfulValue(coords)) {
-            return null;
+            if (!isMeaningfulValue(coords)) {
+                coords = addzhangaiwu.getActiveSessionBaseStation();
+            }
+            if (!isMeaningfulValue(coords)) {
+                return null;
+            }
         }
         String[] parts = coords.split(",");
         if (parts.length < 4) {
@@ -2330,7 +2820,9 @@
         clearCircleGuidanceArtifacts();
         hideFloatingDrawingControls();
         circleDialogMode = false;
-        applyDrawingPauseState(false, false);
+        exitHandheldCaptureInlineUi();
+        handheldCaptureActive = false;
+        exitDrawingControlMode();
         if (activeBoundaryMode == BoundaryCaptureMode.MOWER) {
             stopMowerBoundaryCapture();
         } else if (activeBoundaryMode == BoundaryCaptureMode.HANDHELD && !handheldCaptureActive) {
@@ -2339,6 +2831,129 @@
         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 || !isMeaningfulValue(plannedPath)) {
+            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);
+        mapRenderer.setCurrentPlannedPath(plannedPath);
+        mapRenderer.clearHandheldBoundaryPreview();
+    mapRenderer.setBoundaryPointSizeScale(1.0d);
+        mapRenderer.setBoundaryPointsVisible(isMeaningfulValue(boundary));
+
+        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();
     }
     
     /**
@@ -2370,6 +2985,16 @@
 
     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);
diff --git a/src/zhuye/adddikuaiyulan.java b/src/zhuye/adddikuaiyulan.java
index caa584d..3307106 100644
--- a/src/zhuye/adddikuaiyulan.java
+++ b/src/zhuye/adddikuaiyulan.java
@@ -17,6 +17,10 @@
     private static final Color HANDHELD_BOUNDARY_FILL = new Color(51, 153, 255, 60);
     private static final Color HANDHELD_BOUNDARY_BORDER = new Color(0, 100, 0, 220);
     private static final Color HANDHELD_BOUNDARY_POINT = new Color(0, 100, 0);
+    private static final double BASE_WORLD_MARKER_SIZE = 0.27d; // halve the base diameter for subtler markers
+    private static final double MIN_PIXEL_DIAMETER = 3.0d;
+    private static final double MAX_PIXEL_DIAMETER = 9.0d;
+    private static volatile double cachedMarkerPixelDiameter = -1.0d;
 
     private adddikuaiyulan() {
     }
@@ -24,7 +28,12 @@
     public static void drawPreview(Graphics2D g2d,
                                    List<Point2D.Double> previewPoints,
                                    double scale,
-                                   boolean previewActive) {
+                                   boolean previewActive,
+                                   double diameterScale) {
+        if (!previewActive) {
+            cachedMarkerPixelDiameter = -1.0d;
+        }
+
         if (g2d == null || !previewActive || previewPoints == null || previewPoints.isEmpty()) {
             return;
         }
@@ -56,13 +65,25 @@
             g2d.fill(path);
         }
 
-        float outlineWidth =0.1f;
+        float outlineWidth = 0.1f;
         g2d.setStroke(new BasicStroke(outlineWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
         g2d.setColor(HANDHELD_BOUNDARY_BORDER);
         g2d.draw(path);
 
-        double markerSize = 0.2d;
-        double markerRadius = markerSize / 2.0d;
+        if (cachedMarkerPixelDiameter <= 0.0d) {
+            double previousPixelDiameter = Math.abs(BASE_WORLD_MARKER_SIZE * scale);
+            if (previousPixelDiameter <= 0.0d) {
+                previousPixelDiameter = MIN_PIXEL_DIAMETER;
+            }
+            cachedMarkerPixelDiameter = Math.max(MIN_PIXEL_DIAMETER,
+                Math.min(MAX_PIXEL_DIAMETER, previousPixelDiameter));
+        }
+
+    double effectiveScale = Math.max(0.01d, scale);
+    double markerSize = cachedMarkerPixelDiameter / effectiveScale;
+    double normalizedScale = Double.isFinite(diameterScale) && diameterScale > 0.0d ? diameterScale : 1.0d;
+    markerSize *= normalizedScale;
+    double markerRadius = markerSize / 2.0d;
 
         for (Point2D.Double point : previewPoints) {
             if (point == null || !Double.isFinite(point.x) || !Double.isFinite(point.y)) {
diff --git a/src/zhuye/pointandnumber.java b/src/zhuye/pointandnumber.java
index fb95ad8..ac1df42 100644
--- a/src/zhuye/pointandnumber.java
+++ b/src/zhuye/pointandnumber.java
@@ -20,7 +20,8 @@
                                           List<Point2D.Double> boundary, // 杈圭晫鐐归泦鍚�
                                           double scale, // 缂╂斁姣斾緥
                                           double mergeThreshold, // 鍚堝苟闃堝��
-                                          Color pointColor) { // 鐐归鑹�
+                                          Color pointColor, // 鐐归鑹�
+                                          double diameterScale) { // 鐩村緞缂╂斁鍥犲瓙
         if (boundary == null || boundary.size() < 2) { // 鍒ゆ柇鏁版嵁鏄惁鏈夋晥
             return; // 鏃犳晥鐩存帴杩斿洖
         }
@@ -32,8 +33,10 @@
             return; // 鏃犳晥杩斿洖
         }
 
-        double scaleFactor = Math.max(0.5, scale); // 闃叉杩囧皬缂╂斁
-        double markerDiameter = Math.max(1.0, (10.0 / scaleFactor) * 0.2); // 鎻忕偣鐩村緞
+    double scaleFactor = Math.max(0.5, scale); // 闃叉杩囧皬缂╂斁
+    double clampedScale = diameterScale > 0 ? diameterScale : 1.0; // 闃叉闈炴硶缂╂斁
+    double minimumDiameter = clampedScale < 1.0 ? 0.5 : 1.0; // 缂╁皬鏃跺厑璁告洿灏忕殑鏈�灏忓��
+    double markerDiameter = Math.max(minimumDiameter, (10.0 / scaleFactor) * 0.2 * clampedScale); // 鎻忕偣鐩村緞
         double markerRadius = markerDiameter / 2.0; // 鍗婂緞
 
         for (int i = 0; i < effectiveCount; i++) { // 閬嶅巻鏈夋晥鐐�
diff --git a/src/zhuye/zijian.java b/src/zhuye/zijian.java
new file mode 100644
index 0000000..3241748
--- /dev/null
+++ b/src/zhuye/zijian.java
@@ -0,0 +1,127 @@
+package zhuye;
+
+import javax.swing.JDialog;
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Toolkit;
+import java.awt.Window;
+
+/**
+ * 鑷鎻愮ず宸ュ叿绫伙紝闆嗕腑绠$悊鍓茶崏鏈轰綔涓氬墠鐨勮嚜妫�鎻愮ず閫昏緫銆�
+ */
+public final class zijian {
+    private static boolean selfCheckCompleted;
+    private static boolean initialPromptShown;
+
+    private zijian() {
+        // Utility class
+    }
+
+    public static boolean ensureBeforeMowing(Component anchorComponent, Runnable onSelfCheckConfirmed) {
+        if (selfCheckCompleted) {
+            return true;
+        }
+        showSelfCheckDialog(anchorComponent, onSelfCheckConfirmed);
+        return false;
+    }
+
+    public static void showInitialPromptIfNeeded(Component anchorComponent, Runnable onSelfCheckConfirmed) {
+        if (selfCheckCompleted || initialPromptShown) {
+            return;
+        }
+        initialPromptShown = true;
+        showSelfCheckDialog(anchorComponent, onSelfCheckConfirmed);
+    }
+
+    public static void markSelfCheckCompleted() {
+        selfCheckCompleted = true;
+    }
+
+    private static void showSelfCheckDialog(Component anchorComponent, Runnable onSelfCheckConfirmed) {
+    Component parent = resolveDialogParent(anchorComponent);
+    Object[] options = {"绔嬪嵆鑷", "鍙栨秷"};
+    String message = "<html>鍓茶崏鍓嶈鍏堝畬鎴愬鍓茶崏鏈虹殑鑷鎿嶄綔銆�<br>閬ユ帶鍓茶崏鏈哄悜鍓嶅紑绾�2绫筹紝鐒跺悗鍘熷湴杞湀瀹屾垚鑷鍔熻兘銆�</html>";
+
+    JOptionPane optionPane = new JOptionPane(
+        message,
+        JOptionPane.INFORMATION_MESSAGE,
+        JOptionPane.DEFAULT_OPTION,
+        null,
+        options,
+        options[0]);
+
+    JDialog dialog = optionPane.createDialog(parent, "鑷鎻愮ず");
+    dialog.pack();
+    applyTargetWidth(anchorComponent, dialog);
+    dialog.setLocationRelativeTo(parent instanceof Component ? (Component) parent : null);
+    dialog.setVisible(true);
+
+        Object selectedValue = optionPane.getValue();
+        dialog.dispose();
+
+        boolean confirmed = options[0].equals(selectedValue);
+        if (confirmed) {
+            selfCheckCompleted = true;
+            if (onSelfCheckConfirmed != null) {
+                SwingUtilities.invokeLater(onSelfCheckConfirmed);
+            }
+        }
+    }
+
+    private static void applyTargetWidth(Component anchorComponent, JDialog dialog) {
+        if (dialog == null) {
+            return;
+        }
+        int targetWidth = resolveTargetDialogWidth(anchorComponent);
+        if (targetWidth <= 0) {
+            return;
+        }
+        Dimension currentSize = dialog.getSize();
+        if (currentSize == null) {
+            currentSize = dialog.getPreferredSize();
+        }
+        if (currentSize == null) {
+            currentSize = new Dimension(targetWidth, 0);
+        }
+        int width = Math.max(targetWidth, currentSize.width);
+        int height = currentSize.height > 0 ? currentSize.height : dialog.getHeight();
+        if (height <= 0) {
+            height = 200;
+        }
+        dialog.setSize(new Dimension(width, height));
+    }
+
+    private static int resolveTargetDialogWidth(Component anchorComponent) {
+        int baseWidth = 0;
+        if (anchorComponent != null) {
+            baseWidth = anchorComponent.getWidth();
+            if (baseWidth <= 0) {
+                Component parent = resolveDialogParent(anchorComponent);
+                if (parent != null) {
+                    baseWidth = parent.getWidth();
+                }
+            }
+        }
+        if (baseWidth <= 0) {
+            Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+            baseWidth = screenSize != null ? screenSize.width : 0;
+        }
+        if (baseWidth <= 0) {
+            return 0;
+        }
+        return (int) Math.round(baseWidth * 0.8);
+    }
+
+    private static Component resolveDialogParent(Component anchorComponent) {
+        if (anchorComponent == null) {
+            return null;
+        }
+        if (anchorComponent instanceof Window) {
+            return anchorComponent;
+        }
+        Window window = SwingUtilities.getWindowAncestor(anchorComponent);
+        return window != null ? window : anchorComponent;
+    }
+}
diff --git a/user.properties b/user.properties
index c427148..ca5d233 100644
--- a/user.properties
+++ b/user.properties
@@ -1,10 +1,10 @@
 #Updated User Properties
-#Sun Nov 23 11:24:29 GMT+08:00 2025
-registrationTime=-1
+#Tue Dec 09 17:21:03 CST 2025
+email=789
+language=zh
 lastLoginTime=-1
 password=123
-language=zh
-userName=233
-userId=-1
-email=789
+registrationTime=-1
 status=-1
+userId=-1
+userName=233

--
Gitblit v1.10.0