From 87d7cf316e983b0398b270de03a8092412af8487 Mon Sep 17 00:00:00 2001
From: 张世豪 <979909237@qq.com>
Date: 星期五, 19 十二月 2025 16:56:30 +0800
Subject: [PATCH] 新增了收到绘制边界模式

---
 shoudongbianjie.properties   |   12 
 src/set/Sets.java            |  373 +++++++++++++++++++++++
 src/set/Setsys.java          |   15 
 src/bianjie/shudongdraw.java |  202 ++++++++++++
 src/zhuye/MapRenderer.java   |   62 +++
 set.properties               |    9 
 src/zhuye/Shouye.java        |  253 +++++++++++++++
 image/closepage.png          |    0 
 8 files changed, 922 insertions(+), 4 deletions(-)

diff --git a/image/closepage.png b/image/closepage.png
new file mode 100644
index 0000000..804be0b
--- /dev/null
+++ b/image/closepage.png
Binary files differ
diff --git a/set.properties b/set.properties
index 61e0310..9e70bde 100644
--- a/set.properties
+++ b/set.properties
@@ -1,5 +1,5 @@
 #Mower Configuration Properties - Updated
-#Fri Dec 19 13:00:28 CST 2025
+#Fri Dec 19 16:56:03 CST 2025
 appVersion=-1
 boundaryLengthVisible=false
 currentWorkLandNumber=LAND1
@@ -7,12 +7,13 @@
 firmwareVersion=-1
 handheldMarkerId=1872
 idleTrailDurationSeconds=60
-mapScale=8.11
+manualBoundaryDrawingMode=false
+mapScale=11.63
 measurementModeEnabled=false
 mowerId=860
 serialAutoConnect=true
 serialBaudRate=115200
 serialPortName=COM15
 simCardNumber=-1
-viewCenterX=-15.67
-viewCenterY=9.92
+viewCenterX=-17.03
+viewCenterY=4.72
diff --git a/shoudongbianjie.properties b/shoudongbianjie.properties
new file mode 100644
index 0000000..d121ab7
--- /dev/null
+++ b/shoudongbianjie.properties
@@ -0,0 +1,12 @@
+#\u624B\u52A8\u7ED8\u5236\u8FB9\u754C\u5750\u6807 - \u683C\u5F0F: x1,y1;x2,y2;...;xn,yn (\u5355\u4F4D:\u7C73,\u7CBE\u786E\u5230\u5C0F\u6570\u70B9\u540E2\u4F4D)
+#Fri Dec 19 16:55:22 CST 2025
+boundaryCoordinates=2.64,9.22;1.40,10.29;9.39,20.06;19.16,22.54;19.69,12.60;13.48,8.34;6.20,6.74
+email=789
+language=zh
+lastLoginTime=-1
+password=123
+pointCount=7
+registrationTime=-1
+status=-1
+userId=-1
+userName=233
diff --git a/src/bianjie/shudongdraw.java b/src/bianjie/shudongdraw.java
new file mode 100644
index 0000000..022e3d5
--- /dev/null
+++ b/src/bianjie/shudongdraw.java
@@ -0,0 +1,202 @@
+package bianjie;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 鎵嬪姩缁樺埗杈圭晫缁樺埗鍣�
+ * 鐢ㄤ簬澶勭悊鎵嬪姩缁樺埗杈圭晫妯″紡鐨勬墍鏈夊姛鑳�
+ */
+public class shudongdraw {
+    
+    // 鎵嬪姩缁樺埗杈圭晫妯″紡鐘舵��
+    private boolean manualBoundaryDrawingMode = false;
+    
+    // 鎵嬪姩缁樺埗鐨勮竟鐣岀偣鍒楄〃
+    private final List<Point2D.Double> manualBoundaryPoints = new ArrayList<>();
+    
+    // 榧犳爣瀹炴椂浣嶇疆锛堜笘鐣屽潗鏍囷級
+    private Point2D.Double manualBoundaryMousePosition = null;
+    
+    // 棰滆壊瀹氫箟
+    private static final Color MANUAL_BOUNDARY_COLOR = new Color(255, 0, 0); // 绾㈣壊
+    private static final Color MANUAL_BOUNDARY_FILL_COLOR = new Color(255, 0, 0, 50); // 鍗婇�忔槑绾㈣壊濉厖
+    private static final Color MOUSE_POSITION_COLOR = new Color(255, 0, 0, 128); // 鍗婇�忔槑绾㈣壊锛堥紶鏍囦綅缃級
+    
+    /**
+     * 璁剧疆鎵嬪姩缁樺埗杈圭晫妯″紡
+     */
+    public void setManualBoundaryDrawingMode(boolean active) {
+        manualBoundaryDrawingMode = active;
+        if (!active) {
+            manualBoundaryPoints.clear();
+            manualBoundaryMousePosition = null;
+        }
+    }
+    
+    /**
+     * 妫�鏌ユ墜鍔ㄧ粯鍒惰竟鐣屾ā寮忔槸鍚︽縺娲�
+     */
+    public boolean isManualBoundaryDrawingMode() {
+        return manualBoundaryDrawingMode;
+    }
+    
+    /**
+     * 澶勭悊榧犳爣鐐瑰嚮锛屾坊鍔犺竟鐣岀偣
+     */
+    public boolean handleClick(Point2D.Double worldPoint) {
+        if (!manualBoundaryDrawingMode) {
+            return false;
+        }
+        manualBoundaryPoints.add(worldPoint);
+        return true;
+    }
+    
+    /**
+     * 鏇存柊榧犳爣浣嶇疆
+     */
+    public void updateMousePosition(Point2D.Double worldPoint) {
+        if (manualBoundaryDrawingMode) {
+            manualBoundaryMousePosition = worldPoint;
+        } else {
+            manualBoundaryMousePosition = null;
+        }
+    }
+    
+    /**
+     * 娓呴櫎榧犳爣浣嶇疆
+     */
+    public void clearMousePosition() {
+        manualBoundaryMousePosition = null;
+    }
+    
+    /**
+     * 鑾峰彇鎵嬪姩缁樺埗鐨勮竟鐣岀偣鍒楄〃
+     */
+    public List<Point2D.Double> getManualBoundaryPoints() {
+        return new ArrayList<>(manualBoundaryPoints);
+    }
+    
+    /**
+     * 娓呯┖鎵嬪姩缁樺埗鐨勮竟鐣岀偣
+     */
+    public void clearManualBoundaryPoints() {
+        manualBoundaryPoints.clear();
+    }
+    
+    /**
+     * 缁樺埗鎵嬪姩缁樺埗鐨勮竟鐣�
+     */
+    public void drawBoundary(Graphics2D g2d, double scale) {
+        if (!manualBoundaryDrawingMode || manualBoundaryPoints.isEmpty()) {
+            return;
+        }
+        
+        List<Point2D.Double> points = manualBoundaryPoints;
+        if (points == null || points.isEmpty()) {
+            return;
+        }
+        
+        // 淇濆瓨鍘熷鐘舵��
+        Color originalColor = g2d.getColor();
+        BasicStroke originalStroke = (BasicStroke) g2d.getStroke();
+        
+        // 濡傛灉鐐规暟>=3锛岀粯鍒跺~鍏呭尯鍩�
+        if (points.size() >= 3) {
+            Path2D.Double fillPath = new Path2D.Double();
+            fillPath.moveTo(points.get(0).x, points.get(0).y);
+            for (int i = 1; i < points.size(); i++) {
+                fillPath.lineTo(points.get(i).x, points.get(i).y);
+            }
+            fillPath.closePath(); // 鑷姩杩炴帴璧风偣鍜岀粓鐐�
+            
+            g2d.setColor(MANUAL_BOUNDARY_FILL_COLOR);
+            g2d.fill(fillPath);
+        }
+        
+        // 璁$畻绾挎潯瀹藉害锛堢敤浜庣粯鍒剁偣鍜岃竟鐣岀嚎锛�
+        float strokeWidth = (float) (3 / Math.max(0.5, scale));
+        
+        // 缁樺埗杈圭晫绾匡紙鑷冲皯闇�瑕�2涓偣锛�
+        if (points.size() >= 2) {
+            g2d.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+            g2d.setColor(MANUAL_BOUNDARY_COLOR);
+            
+            Path2D.Double borderPath = new Path2D.Double();
+            borderPath.moveTo(points.get(0).x, points.get(0).y);
+            for (int i = 1; i < points.size(); i++) {
+                borderPath.lineTo(points.get(i).x, points.get(i).y);
+            }
+            // 鑷姩杩炴帴璧风偣鍜岀粓鐐�
+            if (points.size() >= 3) {
+                borderPath.closePath();
+            }
+            g2d.draw(borderPath);
+        }
+        
+        // 缁樺埗鐐规爣璁� - 瀹炲績鍦嗗湀锛屽渾鍦堢洿寰勬槸绾挎潯瀹藉害鐨�2鍊�
+        // 鍗充娇鍙湁涓�涓偣涔熻缁樺埗
+        if (points.size() >= 1) {
+            // 灏嗙嚎鏉″搴﹁浆鎹负涓栫晫鍧愭爣鐨勭洿寰勶紝鐩村緞鏄嚎鏉″搴︾殑2鍊�
+            double pointDiameter = (strokeWidth * 2) / scale;
+            g2d.setColor(MANUAL_BOUNDARY_COLOR);
+            for (Point2D.Double point : points) {
+                Ellipse2D.Double marker = new Ellipse2D.Double(
+                    point.x - pointDiameter / 2,
+                    point.y - pointDiameter / 2,
+                    pointDiameter,
+                    pointDiameter
+                );
+                g2d.fill(marker); // 瀹炲績鍦嗗湀
+            }
+        }
+        
+        // 鎭㈠鍘熷鐘舵��
+        g2d.setColor(originalColor);
+        g2d.setStroke(originalStroke);
+    }
+    
+    /**
+     * 缁樺埗榧犳爣瀹炴椂浣嶇疆锛堟墜鍔ㄧ粯鍒惰竟鐣屾ā寮忔椂锛�
+     */
+    public void drawMousePosition(Graphics2D g2d, double scale) {
+        if (!manualBoundaryDrawingMode || manualBoundaryMousePosition == null) {
+            return;
+        }
+        
+        Point2D.Double mousePos = manualBoundaryMousePosition;
+        if (mousePos == null || !Double.isFinite(mousePos.x) || !Double.isFinite(mousePos.y)) {
+            return;
+        }
+        
+        // 淇濆瓨鍘熷鐘舵��
+        Color originalColor = g2d.getColor();
+        BasicStroke originalStroke = (BasicStroke) g2d.getStroke();
+        
+        // 璁$畻绾挎潯瀹藉害
+        float strokeWidth = (float) (3 / Math.max(0.5, scale));
+        // 榧犳爣浣嶇疆鍦嗗湀鐩村緞鏄嚎鏉″搴︾殑2鍊�
+        double mouseCircleDiameter = (strokeWidth * 2) / scale;
+        
+        // 缁樺埗榧犳爣浣嶇疆鐨勫渾鍦堬紙浣跨敤鍗婇�忔槑棰滆壊锛�
+        g2d.setColor(MOUSE_POSITION_COLOR);
+        
+        Ellipse2D.Double mouseCircle = new Ellipse2D.Double(
+            mousePos.x - mouseCircleDiameter / 2,
+            mousePos.y - mouseCircleDiameter / 2,
+            mouseCircleDiameter,
+            mouseCircleDiameter
+        );
+        g2d.fill(mouseCircle);
+        
+        // 鎭㈠鍘熷鐘舵��
+        g2d.setColor(originalColor);
+        g2d.setStroke(originalStroke);
+    }
+}
diff --git a/src/set/Sets.java b/src/set/Sets.java
index 5b2bafa..6f5ea93 100644
--- a/src/set/Sets.java
+++ b/src/set/Sets.java
@@ -13,9 +13,11 @@
 
 import java.awt.*;
 import java.awt.event.*;
+import java.awt.geom.Point2D;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * 璁剧疆瀵硅瘽妗� - 鍙傝�僑houye.java鏍峰紡
@@ -45,6 +47,7 @@
     private JLabel idleTrailDurationLabel;
     private JLabel boundaryLengthVisibleLabel;
     private JLabel measurementModeEnabledLabel;
+    private JLabel manualBoundaryDrawingModeLabel;
     
     private JButton mowerIdEditBtn;
     private JButton mowerSizeEditBtn;
@@ -177,6 +180,10 @@
         // 寮�鍚祴閲忔ā寮忚缃」
         JPanel measurementModePanel = createMeasurementModePanel();
         measurementModeEnabledLabel = (JLabel) measurementModePanel.getClientProperty("valueLabel");
+        
+        // 鎵嬪姩缁樺埗杈圭晫妯″紡璁剧疆椤�
+        JPanel manualBoundaryDrawingPanel = createManualBoundaryDrawingPanel();
+        manualBoundaryDrawingModeLabel = (JLabel) manualBoundaryDrawingPanel.getClientProperty("valueLabel");
 
         JPanel feedbackPanel = createFeedbackPanel();
         
@@ -195,6 +202,7 @@
         addSettingItem(panel, idleTrailPanel, true);
         addSettingItem(panel, boundaryLengthPanel, true);
         addSettingItem(panel, measurementModePanel, true);
+        addSettingItem(panel, manualBoundaryDrawingPanel, true);
         addSettingItem(panel, feedbackPanel, true);
         addSettingItem(panel, appVersionPanel, false);  // 鏈�鍚庝竴椤逛笉鍔犲垎鍓茬嚎
         
@@ -469,6 +477,157 @@
     }
     
     /**
+     * 鍒涘缓鎵嬪姩缁樺埗杈圭晫妯″紡璁剧疆闈㈡澘
+     */
+    private JPanel createManualBoundaryDrawingPanel() {
+        JPanel panel = new JPanel(new GridBagLayout());
+        panel.setOpaque(false);  // 閫忔槑鑳屾櫙
+        panel.setAlignmentX(Component.LEFT_ALIGNMENT);
+        panel.setMaximumSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT));
+        panel.setPreferredSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT));
+        panel.setMinimumSize(new Dimension(0, ROW_HEIGHT));
+        panel.setBorder(BorderFactory.createEmptyBorder(ITEM_PADDING, ITEM_PADDING, ITEM_PADDING, ITEM_PADDING));
+
+        GridBagConstraints gbc = new GridBagConstraints();
+
+        JLabel titleLabel = new JLabel("鎵嬪姩缁樺埗杈圭晫妯″紡");
+        titleLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
+        titleLabel.setForeground(Color.BLACK);
+        titleLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+        titleLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
+        // 娣诲姞鐐瑰嚮浜嬩欢锛屾樉绀哄潗鏍囧璇濇
+        titleLabel.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                showManualBoundaryCoordinatesDialog();
+            }
+            
+            @Override
+            public void mouseEntered(MouseEvent e) {
+                titleLabel.setForeground(THEME_COLOR);
+            }
+            
+            @Override
+            public void mouseExited(MouseEvent e) {
+                titleLabel.setForeground(Color.BLACK);
+            }
+        });
+        gbc.gridx = 0;
+        gbc.gridy = 0;
+        gbc.weightx = 0;
+        gbc.anchor = GridBagConstraints.EAST;
+        gbc.insets = new Insets(0, 0, 0, 12);
+        panel.add(titleLabel, gbc);
+
+        manualBoundaryDrawingModeLabel = new JLabel(setData.isManualBoundaryDrawingMode() ? "宸插紑鍚�" : "宸插叧闂�");
+        manualBoundaryDrawingModeLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+        manualBoundaryDrawingModeLabel.setForeground(Color.DARK_GRAY);
+        gbc = new GridBagConstraints();
+        gbc.gridx = 1;
+        gbc.gridy = 0;
+        gbc.weightx = 1.0;
+        gbc.anchor = GridBagConstraints.EAST;
+        panel.add(manualBoundaryDrawingModeLabel, gbc);
+
+        panel.putClientProperty("valueLabel", manualBoundaryDrawingModeLabel);
+
+        // 鍒涘缓鍒囨崲鎸夐挳锛堜娇鐢ㄥ浘鏍囷級
+        JButton toggleBtn = createManualBoundaryDrawingToggleButton();
+        gbc = new GridBagConstraints();
+        gbc.gridx = 2;
+        gbc.gridy = 0;
+        gbc.weightx = 0;
+        gbc.anchor = GridBagConstraints.EAST;
+        panel.add(toggleBtn, gbc);
+        panel.putClientProperty("toggleButton", toggleBtn);
+
+        return panel;
+    }
+    
+    /**
+     * 鍒涘缓鎵嬪姩缁樺埗杈圭晫妯″紡鍒囨崲鎸夐挳
+     */
+    private JButton createManualBoundaryDrawingToggleButton() {
+        JButton button = new JButton();
+        button.setContentAreaFilled(false);
+        button.setBorder(null);
+        button.setFocusPainted(false);
+        button.setCursor(new Cursor(Cursor.HAND_CURSOR));
+        button.setPreferredSize(new Dimension(32, 32));
+        button.setMinimumSize(new Dimension(32, 32));
+        button.setMaximumSize(new Dimension(32, 32));
+        
+        updateManualBoundaryDrawingToggleButton(button);
+        
+        button.addActionListener(e -> toggleManualBoundaryDrawingMode(button));
+        
+        return button;
+    }
+    
+    /**
+     * 鏇存柊鎵嬪姩缁樺埗杈圭晫妯″紡鍒囨崲鎸夐挳鍥炬爣
+     */
+    private void updateManualBoundaryDrawingToggleButton(JButton button) {
+        boolean isEnabled = setData.isManualBoundaryDrawingMode();
+        try {
+            String iconPath = isEnabled ? "image/open.png" : "image/close.png";
+            ImageIcon icon = new ImageIcon(iconPath);
+            if (icon.getIconWidth() > 0) {
+                Image scaledImage = icon.getImage().getScaledInstance(32, 32, Image.SCALE_SMOOTH);
+                button.setIcon(new ImageIcon(scaledImage));
+                button.setText(null);
+            } else {
+                button.setIcon(null);
+                button.setText(isEnabled ? "寮�" : "鍏�");
+            }
+        } catch (Exception e) {
+            button.setIcon(null);
+            button.setText(isEnabled ? "寮�" : "鍏�");
+            System.err.println("鏃犳硶鍔犺浇鎵嬪姩缁樺埗杈圭晫妯″紡鍥炬爣: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 鍒囨崲鎵嬪姩缁樺埗杈圭晫妯″紡鐘舵��
+     */
+    private void toggleManualBoundaryDrawingMode(JButton button) {
+        boolean newValue = !setData.isManualBoundaryDrawingMode();
+        setData.setManualBoundaryDrawingMode(newValue);
+        
+        // 淇濆瓨鍒伴厤缃枃浠�
+        setData.updateProperty("manualBoundaryDrawingMode", String.valueOf(newValue));
+        
+        // 鏇存柊UI
+        if (manualBoundaryDrawingModeLabel != null) {
+            manualBoundaryDrawingModeLabel.setText(newValue ? "宸插紑鍚�" : "宸插叧闂�");
+        }
+        updateManualBoundaryDrawingToggleButton(button);
+        
+        // 閫氱煡MapRenderer鏇存柊鎵嬪姩缁樺埗杈圭晫妯″紡鐘舵��
+        Shouye shouye = Shouye.getInstance();
+        if (shouye != null) {
+            MapRenderer renderer = shouye.getMapRenderer();
+            if (renderer != null) {
+                renderer.setManualBoundaryDrawingMode(newValue);
+                if (!newValue) {
+                    // 濡傛灉鍏抽棴妯″紡锛屾竻绌哄凡缁樺埗鐨勭偣
+                    renderer.clearManualBoundaryPoints();
+                }
+            }
+            // 閫氱煡棣栭〉鏇存柊鎮诞杩斿洖鎸夐挳鏄剧ず鐘舵��
+            shouye.updateSettingsReturnButtonVisibility();
+        }
+        
+        // 濡傛灉寮�鍚紝鍏抽棴绯荤粺璁剧疆椤甸潰骞惰繑鍥為椤�
+        if (newValue) {
+            SwingUtilities.invokeLater(() -> {
+                setVisible(false);
+                dispose();
+            });
+        }
+    }
+    
+    /**
      * 鍒涘缓寮�鍚祴閲忔ā寮忚缃潰鏉�
      */
     private JPanel createMeasurementModePanel() {
@@ -593,6 +752,16 @@
             if (renderer != null) {
                 renderer.repaint();
             }
+            // 閫氱煡棣栭〉鏇存柊鎮诞杩斿洖鎸夐挳鏄剧ず鐘舵��
+            shouye.updateSettingsReturnButtonVisibility();
+        }
+        
+        // 濡傛灉寮�鍚紝鍏抽棴绯荤粺璁剧疆椤甸潰骞惰繑鍥為椤�
+        if (newValue) {
+            SwingUtilities.invokeLater(() -> {
+                setVisible(false);
+                dispose();
+            });
         }
     }
     
@@ -662,6 +831,16 @@
             if (renderer != null) {
                 renderer.setBoundaryLengthVisible(newValue);
             }
+            // 閫氱煡棣栭〉鏇存柊鎮诞杩斿洖鎸夐挳鏄剧ず鐘舵��
+            shouye.updateSettingsReturnButtonVisibility();
+        }
+        
+        // 濡傛灉寮�鍚紝鍏抽棴绯荤粺璁剧疆椤甸潰骞惰繑鍥為椤�
+        if (newValue) {
+            SwingUtilities.invokeLater(() -> {
+                setVisible(false);
+                dispose();
+            });
         }
     }
     
@@ -821,6 +1000,19 @@
             }
         }
         
+        // 鏇存柊鎵嬪姩缁樺埗杈圭晫妯″紡鐘舵��
+        if (manualBoundaryDrawingModeLabel != null) {
+            manualBoundaryDrawingModeLabel.setText(setData.isManualBoundaryDrawingMode() ? "宸插紑鍚�" : "宸插叧闂�");
+        }
+        // 鏇存柊鎵嬪姩缁樺埗杈圭晫妯″紡鍒囨崲鎸夐挳鍥炬爣
+        JPanel manualBoundaryDrawingPanel = (JPanel) manualBoundaryDrawingModeLabel.getParent();
+        if (manualBoundaryDrawingPanel != null) {
+            JButton toggleBtn = (JButton) manualBoundaryDrawingPanel.getClientProperty("toggleButton");
+            if (toggleBtn != null) {
+                updateManualBoundaryDrawingToggleButton(toggleBtn);
+            }
+        }
+        
         // 鏇存柊APP鐗堟湰鏄剧ず
         if (appVersionLabel != null) {
             appVersionLabel.setText(setData.getAppVersion() != null ? 
@@ -1362,6 +1554,187 @@
         }
     }
     
+    /**
+     * 鏄剧ず鎵嬪姩缁樺埗杈圭晫鍧愭爣瀵硅瘽妗�
+     */
+    private void showManualBoundaryCoordinatesDialog() {
+        // 浠� shoudongbianjie.properties 鏂囦欢璇诲彇鍧愭爣
+        String coordinates = null;
+        int pointCount = 0;
+        
+        try {
+            java.util.Properties props = new java.util.Properties();
+            java.io.File file = new java.io.File("shoudongbianjie.properties");
+            
+            if (file.exists()) {
+                try (java.io.FileInputStream input = new java.io.FileInputStream(file)) {
+                    props.load(input);
+                    coordinates = props.getProperty("boundaryCoordinates");
+                    String pointCountStr = props.getProperty("pointCount");
+                    if (pointCountStr != null && !pointCountStr.trim().isEmpty()) {
+                        try {
+                            pointCount = Integer.parseInt(pointCountStr.trim());
+                        } catch (NumberFormatException e) {
+                            // 濡傛灉鏃犳硶瑙f瀽锛屼粠鍧愭爣瀛楃涓茶绠楃偣鏁�
+                            if (coordinates != null && !coordinates.trim().isEmpty()) {
+                                pointCount = coordinates.split(";").length;
+                            }
+                        }
+                    } else if (coordinates != null && !coordinates.trim().isEmpty()) {
+                        // 濡傛灉娌℃湁鐐规暟閲忥紝浠庡潗鏍囧瓧绗︿覆璁$畻
+                        pointCount = coordinates.split(";").length;
+                    }
+                }
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            JOptionPane.showMessageDialog(this, 
+                "璇诲彇鍧愭爣鏂囦欢澶辫触: " + ex.getMessage(), 
+                "閿欒", 
+                JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+        
+        // 濡傛灉娌℃湁鍧愭爣鏁版嵁锛屾樉绀烘彁绀�
+        if (coordinates == null || coordinates.trim().isEmpty()) {
+            JOptionPane.showMessageDialog(this, 
+                "褰撳墠娌℃湁淇濆瓨鐨勮竟鐣屽潗鏍�", 
+                "鎻愮ず", 
+                JOptionPane.INFORMATION_MESSAGE);
+            return;
+        }
+        
+        // 鍒涘缓瀵硅瘽妗� - 瀹藉害鍜岀郴缁熻缃〉闈竴鏍凤紙400锛�
+        JDialog dialog = new JDialog(this, "鎵嬪姩缁樺埗杈圭晫鍧愭爣", true);
+        dialog.setLayout(new BorderLayout(0, 12));
+        dialog.setResizable(true);
+        dialog.setPreferredSize(new Dimension(400, 400));
+        dialog.setSize(new Dimension(400, 400));
+        dialog.setMinimumSize(new Dimension(400, 300));
+        
+        JPanel content = new JPanel();
+        content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
+        content.setBorder(BorderFactory.createEmptyBorder(20, 24, 20, 24));
+        dialog.add(content, BorderLayout.CENTER);
+        
+        // 鏍囬
+        String titleText = pointCount > 0 
+            ? "鍧愭爣鐐瑰垪琛紙鍏� " + pointCount + " 涓偣锛夛細"
+            : "鍧愭爣鐐瑰垪琛細";
+        JLabel titleLabel = new JLabel(titleText);
+        titleLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
+        titleLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+        content.add(titleLabel);
+        
+        content.add(Box.createRigidArea(new Dimension(0, 10)));
+        
+        // 鍧愭爣鏂囨湰鍖哄煙
+        JTextArea coordinatesArea = new JTextArea(coordinates);
+        coordinatesArea.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+        coordinatesArea.setLineWrap(true);
+        coordinatesArea.setWrapStyleWord(false);
+        coordinatesArea.setEditable(false);
+        coordinatesArea.setBackground(Color.WHITE);
+        JScrollPane scrollPane = new JScrollPane(coordinatesArea);
+        scrollPane.setAlignmentX(Component.LEFT_ALIGNMENT);
+        scrollPane.setPreferredSize(new Dimension(0, 200));
+        content.add(scrollPane);
+        
+        content.add(Box.createRigidArea(new Dimension(0, 16)));
+        
+        // 鎸夐挳闈㈡澘
+        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0));
+        buttonPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+        
+        // 鍒涘缓澶嶅埗鎸夐挳锛屼娇鐢ㄥ浘鏍�
+        JButton copyButton = new JButton();
+        try {
+            ImageIcon copyIcon = new ImageIcon("image/fuzhi.png");
+            if (copyIcon.getIconWidth() > 0) {
+                // 璋冩暣鍥炬爣澶у皬
+                Image scaledImage = copyIcon.getImage().getScaledInstance(32, 32, Image.SCALE_SMOOTH);
+                copyButton.setIcon(new ImageIcon(scaledImage));
+            } else {
+                copyButton.setText("澶嶅埗鍧愭爣");
+            }
+        } catch (Exception ex) {
+            copyButton.setText("澶嶅埗鍧愭爣");
+            System.err.println("鏃犳硶鍔犺浇澶嶅埗鍥炬爣: " + ex.getMessage());
+        }
+        copyButton.setContentAreaFilled(false);
+        copyButton.setBorder(null);
+        copyButton.setFocusPainted(false);
+        copyButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
+        copyButton.setToolTipText("澶嶅埗鍧愭爣");
+        
+        // 鍔犺浇鎴愬姛鍥炬爣
+        ImageIcon successIcon = null;
+        try {
+            ImageIcon originalSuccessIcon = new ImageIcon("image/fuzhisucc.png");
+            if (originalSuccessIcon.getIconWidth() > 0) {
+                Image scaledSuccessImage = originalSuccessIcon.getImage().getScaledInstance(32, 32, Image.SCALE_SMOOTH);
+                successIcon = new ImageIcon(scaledSuccessImage);
+            }
+        } catch (Exception ex) {
+            System.err.println("鏃犳硶鍔犺浇鎴愬姛鍥炬爣: " + ex.getMessage());
+        }
+        
+        final ImageIcon finalSuccessIcon = successIcon;
+        final ImageIcon originalCopyIcon = (ImageIcon) copyButton.getIcon();
+        
+        copyButton.addActionListener(e -> {
+            String text = coordinatesArea.getText();
+            if (text != null && !text.trim().isEmpty()) {
+                java.awt.Toolkit.getDefaultToolkit().getSystemClipboard().setContents(
+                    new java.awt.datatransfer.StringSelection(text), null);
+                
+                // 鍒囨崲涓烘垚鍔熷浘鏍�
+                if (finalSuccessIcon != null) {
+                    copyButton.setIcon(finalSuccessIcon);
+                }
+                
+                // 2绉掑悗鎭㈠涓哄師濮嬪浘鏍�
+                Timer restoreTimer = new Timer(2000, evt -> {
+                    if (originalCopyIcon != null) {
+                        copyButton.setIcon(originalCopyIcon);
+                    }
+                });
+                restoreTimer.setRepeats(false);
+                restoreTimer.start();
+            }
+        });
+        
+        // 鍒涘缓鍏抽棴鎸夐挳锛屼娇鐢ㄥ浘鏍�
+        JButton closeButton = new JButton();
+        try {
+            ImageIcon closeIcon = new ImageIcon("image/closepage.png");
+            if (closeIcon.getIconWidth() > 0) {
+                // 璋冩暣鍥炬爣澶у皬
+                Image scaledImage = closeIcon.getImage().getScaledInstance(32, 32, Image.SCALE_SMOOTH);
+                closeButton.setIcon(new ImageIcon(scaledImage));
+            } else {
+                closeButton.setText("鍏抽棴");
+            }
+        } catch (Exception ex) {
+            closeButton.setText("鍏抽棴");
+            System.err.println("鏃犳硶鍔犺浇鍏抽棴鍥炬爣: " + ex.getMessage());
+        }
+        closeButton.setContentAreaFilled(false);
+        closeButton.setBorder(null);
+        closeButton.setFocusPainted(false);
+        closeButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
+        closeButton.setToolTipText("鍏抽棴");
+        closeButton.addActionListener(e -> dialog.dispose());
+        
+        buttonPanel.add(copyButton);
+        buttonPanel.add(closeButton);
+        content.add(buttonPanel);
+        
+        dialog.pack();
+        dialog.setLocationRelativeTo(this);
+        dialog.setVisible(true);
+    }
+    
     private void checkForUpdates() {
         // 妯℃嫙妫�鏌ユ洿鏂拌繃绋�
         checkUpdateBtn.setEnabled(false);
diff --git a/src/set/Setsys.java b/src/set/Setsys.java
index a3d9262..a9ac06d 100644
--- a/src/set/Setsys.java
+++ b/src/set/Setsys.java
@@ -15,6 +15,7 @@
     private int idleTrailDurationSeconds = DEFAULT_IDLE_TRAIL_DURATION_SECONDS;
     private boolean boundaryLengthVisible = false;  // 榛樿鍏抽棴鏄剧ず杈圭晫璺濈
     private boolean measurementModeEnabled = false;  // 榛樿鍏抽棴娴嬮噺妯″紡
+    private boolean manualBoundaryDrawingMode = false;  // 榛樿鍏抽棴鎵嬪姩缁樺埗杈圭晫妯″紡
     
     private static final String PROPERTIES_FILE = "set.properties";
 
@@ -104,6 +105,14 @@
     public void setMeasurementModeEnabled(boolean enabled) {
         this.measurementModeEnabled = enabled;
     }
+    
+    public boolean isManualBoundaryDrawingMode() {
+        return manualBoundaryDrawingMode;
+    }
+    
+    public void setManualBoundaryDrawingMode(boolean enabled) {
+        this.manualBoundaryDrawingMode = enabled;
+    }
 
     /**
      * 鍒濆鍖栨柟娉� - 浠巔roperties鏂囦欢璇诲彇鏁版嵁
@@ -126,6 +135,8 @@
             this.boundaryLengthVisible = "true".equalsIgnoreCase(boundaryLengthVisibleStr);
             String measurementModeEnabledStr = props.getProperty("measurementModeEnabled");
             this.measurementModeEnabled = "true".equalsIgnoreCase(measurementModeEnabledStr);
+            String manualBoundaryDrawingModeStr = props.getProperty("manualBoundaryDrawingMode");
+            this.manualBoundaryDrawingMode = "true".equalsIgnoreCase(manualBoundaryDrawingModeStr);
                         
         } catch (FileNotFoundException e) {           
             // 鏂囦欢涓嶅瓨鍦ㄦ椂锛岃缃墍鏈夊睘鎬т负null
@@ -173,6 +184,9 @@
             case "measurementModeEnabled":
                 this.measurementModeEnabled = "true".equalsIgnoreCase(value);
                 break;
+            case "manualBoundaryDrawingMode":
+                this.manualBoundaryDrawingMode = "true".equalsIgnoreCase(value);
+                break;
             case "mapScale":
                 // mapScale涓嶉渶瑕佸湪鍐呭瓨涓瓨鍌紝鐩存帴鏇存柊鍒版枃浠�
                 break;
@@ -243,6 +257,7 @@
         this.idleTrailDurationSeconds = DEFAULT_IDLE_TRAIL_DURATION_SECONDS;
         this.boundaryLengthVisible = false;  // 榛樿鍏抽棴
         this.measurementModeEnabled = false;  // 榛樿鍏抽棴娴嬮噺妯″紡
+        this.manualBoundaryDrawingMode = false;  // 榛樿鍏抽棴鎵嬪姩缁樺埗杈圭晫妯″紡
     }
 
     /**
diff --git a/src/zhuye/MapRenderer.java b/src/zhuye/MapRenderer.java
index 31b93b8..790f3b9 100644
--- a/src/zhuye/MapRenderer.java
+++ b/src/zhuye/MapRenderer.java
@@ -31,6 +31,7 @@
 import zhangaiwu.Obstacledge;
 import zhangaiwu.yulanzhangaiwu;
 import yaokong.Control03;
+import bianjie.shudongdraw;
 
 /**
  * 鍦板浘娓叉煋鍣� - 璐熻矗鍧愭爣绯荤粯鍒躲�佽鍥惧彉鎹㈢瓑鍔熻兘
@@ -102,6 +103,7 @@
     private boolean measurementModeActive = false;  // 娴嬮噺妯″紡鏄惁婵�娲�
     private boolean handheldBoundaryPreviewActive;
     private boolean pendingTrackBreak = true;
+    private bianjie.shudongdraw manualBoundaryDrawer = new bianjie.shudongdraw();  // 鎵嬪姩缁樺埗杈圭晫缁樺埗鍣�
     private boolean idleTrailSuppressed;
     private Path2D.Double realtimeBoundaryPathCache;
     private String realtimeBoundaryPathLand;
@@ -207,6 +209,14 @@
                 lastDragPoint = null;
                 dragInProgress = false;
             }
+            
+            public void mouseExited(MouseEvent e) {
+                // 榧犳爣绂诲紑闈㈡澘鏃讹紝娓呴櫎榧犳爣浣嶇疆鏄剧ず
+                if (manualBoundaryDrawer.isManualBoundaryDrawingMode()) {
+                    manualBoundaryDrawer.clearMousePosition();
+                    visualizationPanel.repaint();
+                }
+            }
 
             public void mouseClicked(MouseEvent e) {
                 if (dragInProgress) {
@@ -216,6 +226,14 @@
                 if (!SwingUtilities.isLeftMouseButton(e) || e.getClickCount() != 1) {
                     return;
                 }
+                // 浼樺厛澶勭悊鎵嬪姩缁樺埗杈圭晫妯″紡鐐瑰嚮
+                if (manualBoundaryDrawer.isManualBoundaryDrawingMode()) {
+                    Point2D.Double worldPoint = screenToWorld(e.getPoint());
+                    if (manualBoundaryDrawer.handleClick(worldPoint)) {
+                        visualizationPanel.repaint();
+                        return;
+                    }
+                }
                 // 浼樺厛澶勭悊娴嬮噺妯″紡鐐瑰嚮
                 if (measurementModeActive && handleMeasurementClick(e.getPoint())) {
                     return;
@@ -248,6 +266,17 @@
                     visualizationPanel.repaint();
                 }
             }
+            
+            public void mouseMoved(MouseEvent e) {
+                // 鍦ㄦ墜鍔ㄧ粯鍒惰竟鐣屾ā寮忔椂锛屾洿鏂伴紶鏍囦綅缃�
+                if (manualBoundaryDrawer.isManualBoundaryDrawingMode()) {
+                    Point2D.Double worldPoint = screenToWorld(e.getPoint());
+                    manualBoundaryDrawer.updateMousePosition(worldPoint);
+                    visualizationPanel.repaint();
+                } else {
+                    manualBoundaryDrawer.clearMousePosition();
+                }
+            }
         });
     }
 
@@ -378,6 +407,12 @@
 
     adddikuaiyulan.drawPreview(g2d, handheldBoundaryPreview, scale, handheldBoundaryPreviewActive, boundaryPreviewMarkerScale);
 
+        // 缁樺埗鎵嬪姩缁樺埗鐨勮竟鐣�
+        manualBoundaryDrawer.drawBoundary(g2d, scale);
+        
+        // 缁樺埗榧犳爣瀹炴椂浣嶇疆锛堟墜鍔ㄧ粯鍒惰竟鐣屾ā寮忔椂锛�
+        manualBoundaryDrawer.drawMousePosition(g2d, scale);
+
         // 缁樺埗瀵艰埅璺緞锛堜腑灞傦級
         if (hasPlannedPath) {
             drawCurrentPlannedPath(g2d);
@@ -1223,6 +1258,33 @@
     }
     
     /**
+     * 璁剧疆鎵嬪姩缁樺埗杈圭晫妯″紡
+     */
+    public void setManualBoundaryDrawingMode(boolean active) {
+        manualBoundaryDrawer.setManualBoundaryDrawingMode(active);
+        if (visualizationPanel != null) {
+            visualizationPanel.repaint();
+        }
+    }
+    
+    /**
+     * 鑾峰彇鎵嬪姩缁樺埗鐨勮竟鐣岀偣鍒楄〃
+     */
+    public List<Point2D.Double> getManualBoundaryPoints() {
+        return manualBoundaryDrawer.getManualBoundaryPoints();
+    }
+    
+    /**
+     * 娓呯┖鎵嬪姩缁樺埗鐨勮竟鐣岀偣
+     */
+    public void clearManualBoundaryPoints() {
+        manualBoundaryDrawer.clearManualBoundaryPoints();
+        if (visualizationPanel != null) {
+            visualizationPanel.repaint();
+        }
+    }
+    
+    /**
      * 璁剧疆娴嬮噺妯″紡
      */
     public void setMeasurementMode(boolean active) {
diff --git a/src/zhuye/Shouye.java b/src/zhuye/Shouye.java
index b588d83..c661ee5 100644
--- a/src/zhuye/Shouye.java
+++ b/src/zhuye/Shouye.java
@@ -127,6 +127,8 @@
 	private JButton pathPreviewReturnButton;
 	private boolean pathPreviewActive;
 	private Runnable pathPreviewReturnAction;
+	private JButton settingsReturnButton;  // 杩斿洖绯荤粺璁剧疆椤甸潰鐨勬偓娴寜閽�
+	private JButton saveManualBoundaryButton;  // 淇濆瓨鎵嬪姩缁樺埗杈圭晫鐨勬寜閽�
 	private String previewRestoreLandNumber;
 	private String previewRestoreLandName;
 	private boolean drawingPaused;
@@ -467,6 +469,238 @@
 		} else {
 			celiangmoshi.stop();
 		}
+		// 鍒濆鍖栨墜鍔ㄧ粯鍒惰竟鐣屾ā寮�
+		boolean manualBoundaryDrawingEnabled = setsys.isManualBoundaryDrawingMode();
+		if (mapRenderer != null) {
+			mapRenderer.setManualBoundaryDrawingMode(manualBoundaryDrawingEnabled);
+		}
+		// 鏇存柊杩斿洖璁剧疆鎸夐挳鐨勬樉绀虹姸鎬�
+		updateSettingsReturnButtonVisibility();
+	}
+	
+	/**
+	 * 鏇存柊杩斿洖绯荤粺璁剧疆鎸夐挳鐨勬樉绀虹姸鎬�
+	 * 褰撴墜鍔ㄧ粯鍒惰竟鐣屾ā寮忋�佹樉绀鸿竟鐣岃窛绂绘垨寮�鍚祴閲忔ā寮忎换涓�寮�鍚椂鏄剧ず
+	 */
+	public void updateSettingsReturnButtonVisibility() {
+		Setsys setsys = new Setsys();
+		setsys.initializeFromProperties();
+		
+		boolean manualBoundaryDrawingEnabled = setsys.isManualBoundaryDrawingMode();
+		boolean shouldShow = manualBoundaryDrawingEnabled
+			|| setsys.isBoundaryLengthVisible() 
+			|| setsys.isMeasurementModeEnabled();
+		
+		if (shouldShow) {
+			showSettingsReturnButton();
+			// 濡傛灉鎵嬪姩缁樺埗杈圭晫妯″紡寮�鍚紝鏄剧ず淇濆瓨鎸夐挳
+			if (manualBoundaryDrawingEnabled) {
+				showSaveManualBoundaryButton();
+			} else {
+				hideSaveManualBoundaryButton();
+			}
+		} else {
+			hideSettingsReturnButton();
+			hideSaveManualBoundaryButton();
+		}
+	}
+	
+	/**
+	 * 鏄剧ず杩斿洖绯荤粺璁剧疆鎸夐挳
+	 */
+	private void showSettingsReturnButton() {
+		ensureFloatingButtonInfrastructure();
+		if (settingsReturnButton == null) {
+			settingsReturnButton = createFloatingTextButton("杩斿洖");
+			settingsReturnButton.setToolTipText("杩斿洖绯荤粺璁剧疆");
+			settingsReturnButton.addActionListener(e -> {
+				// 鍏抽棴鎵�鏈夌浉鍏虫ā寮�
+				Setsys setsys = new Setsys();
+				setsys.initializeFromProperties();
+				
+				boolean modeChanged = false;
+				
+				// 鍏抽棴鎵嬪姩缁樺埗杈圭晫妯″紡
+				if (setsys.isManualBoundaryDrawingMode()) {
+					setsys.setManualBoundaryDrawingMode(false);
+					setsys.updateProperty("manualBoundaryDrawingMode", "false");
+					// 娓呯┖鎵嬪姩缁樺埗鐨勮竟鐣岀偣
+					if (mapRenderer != null) {
+						mapRenderer.clearManualBoundaryPoints();
+					}
+					modeChanged = true;
+				}
+				
+				// 鍏抽棴鏄剧ず杈圭晫璺濈
+				if (setsys.isBoundaryLengthVisible()) {
+					setsys.setBoundaryLengthVisible(false);
+					setsys.updateProperty("boundaryLengthVisible", "false");
+					if (mapRenderer != null) {
+						mapRenderer.setBoundaryLengthVisible(false);
+					}
+					modeChanged = true;
+				}
+				
+				// 鍏抽棴娴嬮噺妯″紡
+				if (setsys.isMeasurementModeEnabled()) {
+					setsys.setMeasurementModeEnabled(false);
+					setsys.updateProperty("measurementModeEnabled", "false");
+					if (mapRenderer != null) {
+						mapRenderer.setMeasurementMode(false);
+					}
+					celiangmoshi.stop();
+					modeChanged = true;
+				}
+				
+				// 濡傛灉鍏抽棴浜嗕换浣曟ā寮忥紝绔嬪嵆闅愯棌杩斿洖鎸夐挳骞跺埛鏂扮晫闈�
+				if (modeChanged) {
+					// 绔嬪嵆闅愯棌杩斿洖鎸夐挳
+					if (settingsReturnButton != null) {
+						settingsReturnButton.setVisible(false);
+					}
+					// 鏇存柊鎸夐挳鍒楋紙绉婚櫎杩斿洖鎸夐挳锛�
+					rebuildFloatingButtonColumn();
+					// 濡傛灉鎵�鏈夋寜閽兘闅愯棌浜嗭紝闅愯棌鎮诞鎸夐挳闈㈡澘
+					if (floatingButtonPanel != null && floatingButtonColumn != null
+							&& floatingButtonColumn.getComponentCount() == 0) {
+						floatingButtonPanel.setVisible(false);
+					}
+					// 鍒锋柊鐣岄潰
+					if (visualizationPanel != null) {
+						visualizationPanel.revalidate();
+						visualizationPanel.repaint();
+					}
+				}
+				
+				// 鏇存柊杩斿洖鎸夐挳鏄剧ず鐘舵�侊紙纭繚鐘舵�佸悓姝ワ級
+				updateSettingsReturnButtonVisibility();
+				
+				// 鎵撳紑绯荤粺璁剧疆椤甸潰
+				showSettingsDialog();
+			});
+		}
+		settingsReturnButton.setVisible(true);
+		// 闅愯棌缁樺埗鐩稿叧鐨勬寜閽紙鏆傚仠銆佺粨鏉熺粯鍒讹級
+		if (drawingPauseButton != null) {
+			drawingPauseButton.setVisible(false);
+		}
+		if (endDrawingButton != null) {
+			endDrawingButton.setVisible(false);
+		}
+		if (floatingButtonPanel != null) {
+			floatingButtonPanel.setVisible(true);
+			if (floatingButtonPanel.getParent() != visualizationPanel) {
+				visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH);
+			}
+		}
+		rebuildFloatingButtonColumn();
+	}
+	
+	/**
+	 * 闅愯棌杩斿洖绯荤粺璁剧疆鎸夐挳
+	 */
+	private void hideSettingsReturnButton() {
+		if (settingsReturnButton != null) {
+			settingsReturnButton.setVisible(false);
+		}
+		rebuildFloatingButtonColumn();
+		if (floatingButtonPanel != null && floatingButtonColumn != null
+				&& floatingButtonColumn.getComponentCount() == 0) {
+			floatingButtonPanel.setVisible(false);
+		}
+	}
+	
+	/**
+	 * 鏄剧ず淇濆瓨鎵嬪姩缁樺埗杈圭晫鎸夐挳
+	 */
+	private void showSaveManualBoundaryButton() {
+		ensureFloatingButtonInfrastructure();
+		if (saveManualBoundaryButton == null) {
+			saveManualBoundaryButton = createFloatingTextButton("淇濆瓨");
+			saveManualBoundaryButton.setToolTipText("淇濆瓨鎵嬪姩缁樺埗鐨勮竟鐣�");
+			saveManualBoundaryButton.addActionListener(e -> saveManualBoundary());
+		}
+		saveManualBoundaryButton.setVisible(true);
+		if (floatingButtonPanel != null) {
+			floatingButtonPanel.setVisible(true);
+			if (floatingButtonPanel.getParent() != visualizationPanel) {
+				visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH);
+			}
+		}
+		rebuildFloatingButtonColumn();
+	}
+	
+	/**
+	 * 闅愯棌淇濆瓨鎵嬪姩缁樺埗杈圭晫鎸夐挳
+	 */
+	private void hideSaveManualBoundaryButton() {
+		if (saveManualBoundaryButton != null) {
+			saveManualBoundaryButton.setVisible(false);
+		}
+		rebuildFloatingButtonColumn();
+		if (floatingButtonPanel != null && floatingButtonColumn != null
+				&& floatingButtonColumn.getComponentCount() == 0) {
+			floatingButtonPanel.setVisible(false);
+		}
+	}
+	
+	/**
+	 * 淇濆瓨鎵嬪姩缁樺埗鐨勮竟鐣屽埌鏂囦欢
+	 */
+	private void saveManualBoundary() {
+		if (mapRenderer == null) {
+			JOptionPane.showMessageDialog(this, "鍦板浘娓叉煋鍣ㄦ湭鍒濆鍖�", "閿欒", JOptionPane.ERROR_MESSAGE);
+			return;
+		}
+		
+		List<Point2D.Double> points = mapRenderer.getManualBoundaryPoints();
+		if (points == null || points.isEmpty()) {
+			JOptionPane.showMessageDialog(this, "娌℃湁鍙繚瀛樼殑杈圭晫鐐癸紝璇峰厛鍦ㄥ湴鍥句笂鐐瑰嚮缁樺埗杈圭晫", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+			return;
+		}
+		
+		// 鏋勫缓鍧愭爣瀛楃涓诧細x1,y1;x2,y2;...;xn,yn锛堝崟浣嶏細绫筹紝绮剧‘鍒板皬鏁扮偣鍚�2浣嶏級
+		StringBuilder coordinates = new StringBuilder();
+		for (int i = 0; i < points.size(); i++) {
+			Point2D.Double point = points.get(i);
+			if (i > 0) {
+				coordinates.append(";");
+			}
+			coordinates.append(String.format(Locale.US, "%.2f,%.2f", point.x, point.y));
+		}
+		
+		// 淇濆瓨鍒� properties 鏂囦欢
+		try {
+			java.util.Properties props = new java.util.Properties();
+			java.io.File file = new java.io.File("shoudongbianjie.properties");
+			
+			// 濡傛灉鏂囦欢瀛樺湪锛屽厛鍔犺浇鐜版湁鍐呭
+			if (file.exists()) {
+				try (java.io.FileInputStream input = new java.io.FileInputStream(file)) {
+					props.load(input);
+				}
+			}
+			
+			// 淇濆瓨鍧愭爣
+			props.setProperty("boundaryCoordinates", coordinates.toString());
+			props.setProperty("pointCount", String.valueOf(points.size()));
+			
+			// 鍐欏洖鏂囦欢
+			try (java.io.FileOutputStream output = new java.io.FileOutputStream(file)) {
+				props.store(output, "鎵嬪姩缁樺埗杈圭晫鍧愭爣 - 鏍煎紡: x1,y1;x2,y2;...;xn,yn (鍗曚綅:绫�,绮剧‘鍒板皬鏁扮偣鍚�2浣�)");
+			}
+			
+			JOptionPane.showMessageDialog(this, 
+				String.format("杈圭晫宸蹭繚瀛樻垚鍔燂紒\n鍏� %d 涓偣", points.size()), 
+				"淇濆瓨鎴愬姛", 
+				JOptionPane.INFORMATION_MESSAGE);
+		} catch (Exception ex) {
+			ex.printStackTrace();
+			JOptionPane.showMessageDialog(this, 
+				"淇濆瓨澶辫触: " + ex.getMessage(), 
+				"閿欒", 
+				JOptionPane.ERROR_MESSAGE);
+		}
 	}
 
 	private void createHeaderPanel() {
@@ -2810,6 +3044,11 @@
 		hideCircleGuidancePanel();
 		enterDrawingControlMode();
 		
+		// 闅愯棌杩斿洖璁剧疆鎸夐挳锛堝鏋滄樉绀虹粯鍒舵寜閽紝鍒欎笉搴旇鏄剧ず杩斿洖鎸夐挳锛�
+		if (settingsReturnButton != null) {
+			settingsReturnButton.setVisible(false);
+		}
+		
 		// 鏄剧ず"姝e湪缁樺埗杈圭晫"鎻愮ず
 		if (drawingBoundaryLabel != null) {
 			drawingBoundaryLabel.setVisible(true);
@@ -2923,6 +3162,20 @@
 			floatingButtonColumn.add(pathPreviewReturnButton);
 			added = true;
 		}
+		if (saveManualBoundaryButton != null && saveManualBoundaryButton.isVisible()) {
+			if (added) {
+				floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
+			}
+			floatingButtonColumn.add(saveManualBoundaryButton);
+			added = true;
+		}
+		if (settingsReturnButton != null && settingsReturnButton.isVisible()) {
+			if (added) {
+				floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
+			}
+			floatingButtonColumn.add(settingsReturnButton);
+			added = true;
+		}
 		floatingButtonColumn.revalidate();
 		floatingButtonColumn.repaint();
 	}

--
Gitblit v1.10.0