From d709e6dad60398fd599900cf781d0dd1e8c37c1c Mon Sep 17 00:00:00 2001
From: 张世豪 <979909237@qq.com>
Date: 星期一, 01 十二月 2025 19:55:14 +0800
Subject: [PATCH] 20251201

---
 src/gecaoji/Device.java         |    5 
 src/dikuai/addzhangaiwu.java    | 1243 ++++++++++++++++
 image/startzuoye.png            |    0 
 .classpath                      |    8 
 src/zhangaiwu/AddDikuai.java    |  393 +++++
 src/zhuye/MapRenderer.java      |  289 +++
 image/zantingzuoye.png          |    0 
 src/udpdell/UDPServer.java      |   44 
 src/zhuye/Shouye.java           |  419 +++++
 Obstacledge.properties          |   21 
 src/dikuai/Dikuaiguanli.java    |   75 
 src/zhangaiwu/Obstacledraw.java |  635 ++++++++
 src/zhangaiwu/Obstacledge.java  |  827 ++++++++++
 src/lujing/Lunjingguihua.java   |  568 +++++++
 src/ui/UIConfig.java            |    4 
 15 files changed, 4,450 insertions(+), 81 deletions(-)

diff --git a/.classpath b/.classpath
index fb50116..5b68229 100644
--- a/.classpath
+++ b/.classpath
@@ -1,6 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
+		<attributes>
+			<attribute name="module" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="lib" path="lib/jSerialComm-2.10.4.jar"/>
+	<classpathentry kind="lib" path="lib/jts-core-1.19.0.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/Obstacledge.properties b/Obstacledge.properties
new file mode 100644
index 0000000..b45db39
--- /dev/null
+++ b/Obstacledge.properties
@@ -0,0 +1,21 @@
+# 鍓茶崏鏈哄湴鍧楅殰纰嶇墿閰嶇疆鏂囦欢
+# 鐢熸垚鏃堕棿锛�2025-12-01T18:24:39.295276600
+# 鍧愭爣绯伙細WGS84锛堝害鍒嗘牸寮忥級
+
+# ============ 鍦板潡鍩哄噯绔欓厤缃� ============
+# 鏍煎紡锛歱lot.[鍦板潡缂栧彿].baseStation=[缁忓害],[N/S],[绾害],[E/W]
+plot.DK-001.baseStation=2324.200273,N,11330.666730,E
+
+# ============ 闅滅鐗╅厤缃� ============
+# 鏍煎紡锛歱lot.[鍦板潡缂栧彿].obstacle.[闅滅鐗╁悕绉癩.shape=[0|1]
+# 鏍煎紡锛歱lot.[鍦板潡缂栧彿].obstacle.[闅滅鐗╁悕绉癩.originalCoords=[鍧愭爣涓瞉
+# 鏍煎紡锛歱lot.[鍦板潡缂栧彿].obstacle.[闅滅鐗╁悕绉癩.xyCoords=[鍧愭爣涓瞉
+
+# --- 鍦板潡001鐨勯殰纰嶇墿 ---
+plot.DK-001.obstacle.tree1.shape=0
+plot.DK-001.obstacle.tree1.originalCoords=2324.200373,N;11330.666830,E;2324.200400,N;11330.666850,E
+plot.DK-001.obstacle.tree1.xyCoords=5.20,3.80;7.50,6.20
+plot.DK-001.obstacle.pond.shape=1
+plot.DK-001.obstacle.pond.originalCoords=2324.200500,N;11330.666900,E;2324.200550,N;11330.666950,E;2324.200600,N;11330.666900,E;2324.200550,N;11330.666850,E
+plot.DK-001.obstacle.pond.xyCoords=15.30,20.10;18.70,22.50;22.10,20.10;18.70,17.80
+
diff --git a/image/startzuoye.png b/image/startzuoye.png
new file mode 100644
index 0000000..b70648d
--- /dev/null
+++ b/image/startzuoye.png
Binary files differ
diff --git a/image/zantingzuoye.png b/image/zantingzuoye.png
new file mode 100644
index 0000000..e454060
--- /dev/null
+++ b/image/zantingzuoye.png
Binary files differ
diff --git a/src/dikuai/Dikuaiguanli.java b/src/dikuai/Dikuaiguanli.java
index 269d7a3..94a3d1c 100644
--- a/src/dikuai/Dikuaiguanli.java
+++ b/src/dikuai/Dikuaiguanli.java
@@ -5,6 +5,7 @@
 import java.awt.event.*;
 import java.awt.datatransfer.*;
 import java.awt.datatransfer.StringSelection;
+import java.io.File;
 import java.math.BigDecimal;
 import ui.UIConfig;
 import ui.UIUtils;
@@ -14,8 +15,12 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.ArrayList;
+
+import zhangaiwu.AddDikuai;
+import zhangaiwu.Obstacledge;
 import zhuye.MapRenderer;
 import zhuye.Shouye;
+import zhuye.Coordinate;
 
 /**
  * 鍦板潡绠$悊闈㈡澘 - 鍗$墖寮忓竷灞�璁捐
@@ -662,14 +667,20 @@
 			if (renderer != null) {
 				String boundary = (dikuai != null) ? dikuai.getBoundaryCoordinates() : null;
 				String plannedPath = (dikuai != null) ? dikuai.getPlannedPath() : null;
+				String obstacles = (dikuai != null) ? dikuai.getObstacleCoordinates() : null;
 				renderer.setCurrentBoundary(boundary, landNumber, landNumber == null ? null : landName);
 				renderer.setCurrentPlannedPath(plannedPath);
+				renderer.setCurrentObstacles(obstacles, landNumber);
 				boolean showBoundaryPoints = landNumber != null && boundaryPointVisibility.getOrDefault(landNumber, false);
 				renderer.setBoundaryPointsVisible(showBoundaryPoints);
 			}
 		}
 	}
 
+	public static String getCurrentWorkLandNumber() {
+		return currentWorkLandNumber;
+	}
+
 	private ImageIcon loadIcon(String path, int width, int height) {
 		try {
 			ImageIcon rawIcon = new ImageIcon(path);
@@ -693,6 +704,7 @@
 			if (ownerWindow instanceof JDialog) {
 				ownerWindow.dispose();
 			}
+			Coordinate.coordinates.clear();
 			latestInstance = null;
 			SwingUtilities.invokeLater(() -> AddDikuai.showAddDikuaiDialog(parentComponent));
 		});
@@ -708,22 +720,55 @@
 	}
 
 	private void addNewObstacle(Dikuai dikuai) {
-		String obstacleCoords = JOptionPane.showInputDialog(this, 
-				"璇疯緭鍏ラ殰纰嶇墿鍧愭爣(鏍煎紡: 绾害1,缁忓害1;绾害2,缁忓害2;...):");
-
-		if (obstacleCoords != null && !obstacleCoords.trim().isEmpty()) {
-			// 鏇存柊闅滅鐗╁潗鏍�
-			Dikuai.updateField(dikuai.getLandNumber(), "obstacleCoordinates", obstacleCoords.trim());
-			Dikuai.updateField(dikuai.getLandNumber(), "updateTime", getCurrentTime());
-
-			// 淇濆瓨鍒版枃浠�
-			Dikuai.saveToProperties();
-
-			// 鍒锋柊鏄剧ず
-			loadDikuaiData();
-
-			JOptionPane.showMessageDialog(this, "闅滅鐗╁潗鏍囧凡娣诲姞锛�", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+		if (dikuai == null) {
+			JOptionPane.showMessageDialog(this, "鏈壘鍒板綋鍓嶅湴鍧楋紝鏃犳硶鏂板闅滅鐗�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+			return;
 		}
+		Window windowAncestor = SwingUtilities.getWindowAncestor(this);
+		if (windowAncestor instanceof JDialog) {
+			windowAncestor.dispose();
+		}
+		Component parent = windowAncestor != null ? windowAncestor.getOwner() instanceof Component ? (Component) windowAncestor.getOwner() : windowAncestor : null;
+		List<String> existingObstacleNames = loadObstacleNamesForLand(dikuai.getLandNumber());
+		addzhangaiwu.showDialog(parent, dikuai, existingObstacleNames);
+		loadDikuaiData();
+	}
+
+	private List<String> loadObstacleNamesForLand(String landNumber) {
+		List<String> names = new ArrayList<>();
+		if (landNumber == null || landNumber.trim().isEmpty()) {
+			return names;
+		}
+		try {
+			File configFile = new File("Obstacledge.properties");
+			if (!configFile.exists()) {
+				return names;
+			}
+			Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager();
+			if (!manager.loadFromFile(configFile.getAbsolutePath())) {
+				return names;
+			}
+			Obstacledge.Plot plot = manager.getPlotById(landNumber.trim());
+			if (plot == null) {
+				return names;
+			}
+			for (Obstacledge.Obstacle obstacle : plot.getObstacles()) {
+				if (obstacle == null) {
+					continue;
+				}
+				String name = obstacle.getObstacleName();
+				if (name == null) {
+					continue;
+				}
+				String trimmed = name.trim();
+				if (!trimmed.isEmpty() && !names.contains(trimmed)) {
+					names.add(trimmed);
+				}
+			}
+		} catch (Exception ex) {
+			System.err.println("璇诲彇闅滅鐗╅厤缃け璐�: " + ex.getMessage());
+		}
+		return names;
 	}
 
 	private void editMowingWidth(Dikuai dikuai) {
diff --git a/src/dikuai/addzhangaiwu.java b/src/dikuai/addzhangaiwu.java
new file mode 100644
index 0000000..c936a6b
--- /dev/null
+++ b/src/dikuai/addzhangaiwu.java
@@ -0,0 +1,1243 @@
+package dikuai;
+
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.Image;
+import java.awt.Window;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.border.Border;
+
+import baseStation.BaseStation;
+import gecaoji.Device;
+import ui.UIConfig;
+import zhuye.Coordinate;
+import zhuye.Shouye;
+import zhangaiwu.AddDikuai;
+import zhangaiwu.Obstacledge;
+
+/**
+ * 闅滅鐗╂柊澧�/缂栬緫瀵硅瘽妗嗐�傝璁¤瑷�鍙傝�� {@link AddDikuai}锛屾敮鎸侀�氳繃瀹炲湴缁樺埗閲囬泦闅滅鐗╁潗鏍囥��
+ */
+public class addzhangaiwu extends JDialog {
+    private static final long serialVersionUID = 1L;
+    private static final double METERS_PER_DEGREE_LAT = 111320.0d;
+
+    private final Color PRIMARY_COLOR = new Color(46, 139, 87);
+    private final Color PRIMARY_DARK = new Color(30, 107, 69);
+    private final Color PRIMARY_LIGHT = new Color(232, 245, 233);
+    private final Color WHITE = Color.WHITE;
+    private final Color BORDER_COLOR = new Color(222, 226, 230);
+    private final Color MEDIUM_GRAY = new Color(233, 236, 239);
+    private final Color TEXT_COLOR = new Color(33, 37, 41);
+    private final Color LIGHT_TEXT = new Color(108, 117, 125);
+
+    private final Dikuai targetDikuai;
+    private final List<ExistingObstacle> existingObstacles;
+    private final Map<String, String> formData = new HashMap<>();
+
+    private CardLayout cardLayout;
+    private JPanel stepsPanel;
+    private JButton prevButton;
+    private JButton nextButton;
+    private JButton saveButton;
+
+    private JPanel methodOptionsPanel;
+    private JPanel shapeOptionsPanel;
+    private final Map<String, JPanel> methodOptionPanels = new HashMap<>();
+    private final Map<String, JPanel> shapeOptionPanels = new HashMap<>();
+    private JPanel selectedMethodPanel;
+    private JPanel selectedShapePanel;
+    private JButton drawButton;
+    private JLabel drawingStatusLabel;
+
+    private int currentStep = 1;
+    private boolean drawingInProgress;
+    private String activeDrawingShape;
+
+    private static ObstacleDrawingSession activeSession;
+    private static boolean resumeRequested;
+
+    private static final class ObstacleDrawingSession {
+        Dikuai target;
+        Map<String, String> data = new HashMap<>();
+        String baseStation;
+        boolean drawingCompleted;
+        boolean captureSuccessful;
+        String captureMessage;
+    }
+
+    private addzhangaiwu(Window owner, Dikuai target, List<String> obstacleNames) {
+        super(owner, "鏂板闅滅鐗�", ModalityType.APPLICATION_MODAL);
+        if (target == null) {
+            throw new IllegalArgumentException("targetDikuai 涓嶈兘涓虹┖");
+        }
+    this.targetDikuai = target;
+    this.existingObstacles = Collections.unmodifiableList(resolveExistingObstacles(target, obstacleNames));
+        initializeUI();
+        setupEventHandlers();
+        preloadData();
+    }
+
+    private void initializeUI() {
+    Dimension dialogSize = new Dimension(UIConfig.DIALOG_WIDTH, UIConfig.DIALOG_HEIGHT);
+    setSize(dialogSize);
+    setPreferredSize(dialogSize);
+    setMinimumSize(dialogSize);
+    setMaximumSize(dialogSize);
+        setResizable(false);
+        setLocationRelativeTo(getOwner());
+        getContentPane().setLayout(new BorderLayout());
+        getContentPane().setBackground(WHITE);
+
+        JPanel container = new JPanel(new BorderLayout());
+        container.setBackground(WHITE);
+        container.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+    container.setPreferredSize(new Dimension(UIConfig.DIALOG_WIDTH - 40, UIConfig.DIALOG_HEIGHT - 40));
+
+        cardLayout = new CardLayout();
+        stepsPanel = new JPanel(cardLayout);
+        stepsPanel.setBackground(WHITE);
+    stepsPanel.setPreferredSize(new Dimension(UIConfig.DIALOG_WIDTH - 40, UIConfig.DIALOG_HEIGHT - 140));
+        stepsPanel.add(createStep1Panel(), "step1");
+        stepsPanel.add(createStep2Panel(), "step2");
+
+        container.add(stepsPanel, BorderLayout.CENTER);
+        container.add(createButtonPanel(), BorderLayout.SOUTH);
+
+        add(container, BorderLayout.CENTER);
+        showStep(1);
+    }
+
+    private JPanel createStep1Panel() {
+        JPanel stepPanel = new JPanel();
+        stepPanel.setLayout(new BoxLayout(stepPanel, BoxLayout.Y_AXIS));
+        stepPanel.setBackground(WHITE);
+
+        JLabel title = new JLabel("姝ラ1锛氱‘璁ゅ湴鍧椾俊鎭�");
+        title.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 20));
+        title.setForeground(TEXT_COLOR);
+        title.setAlignmentX(Component.LEFT_ALIGNMENT);
+        title.setHorizontalAlignment(SwingConstants.LEFT);
+        stepPanel.add(title);
+        stepPanel.add(Box.createRigidArea(new Dimension(0, 20)));
+
+    JPanel infoContainer = new JPanel();
+    infoContainer.setLayout(new BoxLayout(infoContainer, BoxLayout.Y_AXIS));
+    infoContainer.setOpaque(false);
+    infoContainer.setAlignmentX(Component.LEFT_ALIGNMENT);
+    infoContainer.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
+
+    infoContainer.add(createInfoRow("鍦板潡缂栧彿锛�", safeValue(targetDikuai.getLandNumber(), "鏈缃�")));
+    infoContainer.add(Box.createRigidArea(new Dimension(0, 16)));
+    infoContainer.add(createInfoRow("鍦板潡鍚嶇О锛�", safeValue(targetDikuai.getLandName(), "鏈懡鍚�")));
+    infoContainer.add(Box.createRigidArea(new Dimension(0, 20)));
+    infoContainer.add(buildExistingObstacleSection());
+
+    stepPanel.add(infoContainer);
+        stepPanel.add(Box.createVerticalGlue());
+        return stepPanel;
+    }
+
+    private JPanel buildExistingObstacleSection() {
+        JPanel section = new JPanel();
+        section.setLayout(new BoxLayout(section, BoxLayout.Y_AXIS));
+        section.setOpaque(false);
+        section.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+        JLabel titleLabel = new JLabel("宸叉湁闅滅鐗�");
+        titleLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
+        titleLabel.setForeground(TEXT_COLOR);
+        titleLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+        section.add(titleLabel);
+        section.add(Box.createRigidArea(new Dimension(0, 8)));
+
+        if (existingObstacles.isEmpty()) {
+            JLabel emptyLabel = new JLabel("鏆傛棤");
+            emptyLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
+            emptyLabel.setForeground(LIGHT_TEXT);
+            emptyLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+            section.add(emptyLabel);
+            return section;
+        }
+
+        JPanel listPanel = new JPanel();
+        listPanel.setLayout(new BoxLayout(listPanel, BoxLayout.Y_AXIS));
+        listPanel.setOpaque(false);
+        listPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+        for (int i = 0; i < existingObstacles.size(); i++) {
+            ExistingObstacle obstacle = existingObstacles.get(i);
+            JPanel row = createObstacleSummaryRow(obstacle);
+            row.setAlignmentX(Component.LEFT_ALIGNMENT);
+            listPanel.add(row);
+            if (i < existingObstacles.size() - 1) {
+                listPanel.add(Box.createRigidArea(new Dimension(0, 6)));
+            }
+        }
+
+        section.add(listPanel);
+        return section;
+    }
+
+    private JPanel createObstacleSummaryRow(ExistingObstacle obstacle) {
+        JPanel row = new JPanel();
+        row.setLayout(new BoxLayout(row, BoxLayout.X_AXIS));
+        row.setOpaque(false);
+        row.setAlignmentX(Component.LEFT_ALIGNMENT);
+        row.setMaximumSize(new Dimension(Integer.MAX_VALUE, 32));
+
+        JLabel infoLabel = new JLabel(buildObstacleSummaryText(obstacle));
+        infoLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
+        infoLabel.setForeground(TEXT_COLOR);
+        infoLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+        JButton editButton = createInlineButton("淇敼");
+        editButton.addActionListener(e -> populateObstacleForEditing(obstacle));
+
+        row.add(infoLabel);
+        row.add(Box.createHorizontalGlue());
+        row.add(editButton);
+        return row;
+    }
+
+    private JButton createInlineButton(String text) {
+        JButton button = new JButton(text);
+        button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 12));
+        button.setForeground(WHITE);
+        button.setBackground(PRIMARY_COLOR);
+        button.setBorder(BorderFactory.createEmptyBorder(6, 16, 6, 16));
+        button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+        button.setFocusPainted(false);
+        Dimension size = new Dimension(72, 28);
+        button.setPreferredSize(size);
+        button.setMinimumSize(size);
+        button.setMaximumSize(size);
+        button.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseEntered(MouseEvent e) {
+                button.setBackground(PRIMARY_DARK);
+            }
+
+            @Override
+            public void mouseExited(MouseEvent e) {
+                button.setBackground(PRIMARY_COLOR);
+            }
+        });
+        return button;
+    }
+
+    private String buildObstacleSummaryText(ExistingObstacle obstacle) {
+        String name = obstacle.getName();
+        String shape = obstacle.getShapeDisplay();
+        String coordPreview = buildCoordinatePreview(obstacle.getCoordinates(), 5);
+        return String.format(Locale.CHINA, "%s锛�%s锛屽潗鏍�:%s", name, shape, coordPreview);
+    }
+
+    private String buildCoordinatePreview(String coords, int keepLength) {
+        if (!isMeaningfulValue(coords)) {
+            return "鏃犲潗鏍�";
+        }
+        String sanitized = coords.replaceAll("\\s+", "");
+        if (sanitized.length() <= keepLength) {
+            return sanitized;
+        }
+        if (keepLength <= 0) {
+            return "...";
+        }
+        return sanitized.substring(0, keepLength) + "...";
+    }
+
+    private void populateObstacleForEditing(ExistingObstacle obstacle) {
+        if (obstacle == null) {
+            return;
+        }
+        String coords = obstacle.getCoordinates();
+        if (isMeaningfulValue(coords)) {
+            formData.put("obstacleCoordinates", coords.trim());
+        } else {
+            formData.remove("obstacleCoordinates");
+        }
+        String shapeKey = obstacle.getShapeKey();
+        if (shapeKey != null) {
+            JPanel shapePanel = shapeOptionPanels.get(shapeKey);
+            selectShapeOption(shapePanel, shapeKey, false);
+        }
+        updateDrawingStatus();
+        showStep(2);
+    }
+
+    private JPanel createStep2Panel() {
+        JPanel stepPanel = new JPanel();
+        stepPanel.setLayout(new BoxLayout(stepPanel, BoxLayout.Y_AXIS));
+        stepPanel.setBackground(WHITE);
+
+        JLabel title = new JLabel("姝ラ2锛氱粯鍒堕殰纰嶇墿鍧愭爣");
+        title.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 20));
+        title.setForeground(TEXT_COLOR);
+        title.setAlignmentX(Component.LEFT_ALIGNMENT);
+        stepPanel.add(title);
+        stepPanel.add(Box.createRigidArea(new Dimension(0, 16)));
+
+        stepPanel.add(createSectionHeader("閫夋嫨缁樺埗鏂瑰紡", "鏀寔鍓茶崏鏈烘垨鎵嬫寔璁惧閲囬泦闅滅鐗╄竟鐣�"));
+        stepPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+
+        methodOptionsPanel = new JPanel();
+        methodOptionsPanel.setLayout(new BoxLayout(methodOptionsPanel, BoxLayout.Y_AXIS));
+        methodOptionsPanel.setOpaque(false);
+        methodOptionsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+    methodOptionsPanel.add(createMethodOption("mower", "鍓茶崏鏈虹粯鍒�", "椹鹃┒鍓茶崏鏈烘部闅滅鐗╄竟鐣岃椹堕噰闆嗗潗鏍�", "image/mow.png"));
+        methodOptionsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+    methodOptionsPanel.add(createMethodOption("handheld", "鎵嬫寔璁惧缁樺埗", "浣跨敤鎵嬫寔璁惧娌块殰纰嶇墿杈圭晫琛岃蛋璁板綍鍧愭爣", "image/URT.png"));
+        stepPanel.add(methodOptionsPanel);
+
+        stepPanel.add(Box.createRigidArea(new Dimension(0, 20)));
+        stepPanel.add(createSectionHeader("闅滅鐗╁舰鐘�", "澶氳竟褰㈤渶閲囬泦澶氫釜鐐癸紝鍦嗗舰鍙渶鍦嗗績涓庡渾鍛ㄤ笂涓�鐐�"));
+        stepPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+
+        shapeOptionsPanel = new JPanel();
+        shapeOptionsPanel.setLayout(new BoxLayout(shapeOptionsPanel, BoxLayout.Y_AXIS));
+        shapeOptionsPanel.setOpaque(false);
+        shapeOptionsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+    shapeOptionsPanel.add(createShapeOption("polygon", "澶氳竟褰�", "渚濇閲囬泦杞粨涓婄殑澶氫釜鐐规垨鑰呮部闅滅鐗╄竟缂樿蛋涓�鍦�"));
+        shapeOptionsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+        shapeOptionsPanel.add(createShapeOption("circle", "鍦嗗舰", "鍏堥噰闆嗗渾蹇冨潗鏍囷紝鍐嶉噰闆嗗渾鍛ㄤ笂涓�鐐�"));
+        stepPanel.add(shapeOptionsPanel);
+
+        stepPanel.add(Box.createRigidArea(new Dimension(0, 24)));
+
+        drawButton = createPrimaryButton("寮�濮嬬粯鍒�", 16);
+        drawButton.setAlignmentX(Component.LEFT_ALIGNMENT);
+        drawButton.addActionListener(e -> startDrawingWorkflow());
+        stepPanel.add(drawButton);
+
+        stepPanel.add(Box.createRigidArea(new Dimension(0, 12)));
+
+        drawingStatusLabel = new JLabel("灏氭湭閲囬泦闅滅鐗╁潗鏍�");
+        drawingStatusLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
+        drawingStatusLabel.setForeground(LIGHT_TEXT);
+        drawingStatusLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+        stepPanel.add(drawingStatusLabel);
+
+        stepPanel.add(Box.createVerticalGlue());
+        return stepPanel;
+    }
+
+    private JPanel createButtonPanel() {
+        JPanel buttonPanel = new JPanel();
+        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
+        buttonPanel.setBackground(WHITE);
+        buttonPanel.setBorder(BorderFactory.createEmptyBorder(20, 0, 0, 0));
+
+        prevButton = createSecondaryButton("涓婁竴姝�");
+        nextButton = createPrimaryButton("涓嬩竴姝�", 16);
+        saveButton = createPrimaryButton("淇濆瓨", 16);
+        saveButton.setVisible(false);
+
+        buttonPanel.add(prevButton);
+        buttonPanel.add(Box.createHorizontalGlue());
+        buttonPanel.add(nextButton);
+        buttonPanel.add(Box.createRigidArea(new Dimension(12, 0)));
+        buttonPanel.add(saveButton);
+
+        return buttonPanel;
+    }
+
+    private JPanel createInstructionPanel(String text) {
+        JPanel instruction = new JPanel(new BorderLayout());
+        instruction.setBackground(PRIMARY_LIGHT);
+        instruction.setAlignmentX(Component.LEFT_ALIGNMENT);
+        instruction.setBorder(BorderFactory.createCompoundBorder(
+                BorderFactory.createMatteBorder(0, 5, 0, 0, PRIMARY_COLOR),
+                BorderFactory.createEmptyBorder(12, 14, 12, 14)));
+
+        JLabel icon = new JLabel("馃挕", SwingConstants.CENTER);
+        icon.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 18));
+        icon.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
+        instruction.add(icon, BorderLayout.WEST);
+
+        JLabel content = new JLabel(text);
+        content.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
+        content.setForeground(TEXT_COLOR);
+        instruction.add(content, BorderLayout.CENTER);
+
+        return instruction;
+    }
+
+    private JPanel createSectionHeader(String title, String hint) {
+        JPanel panel = new JPanel();
+        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+        panel.setOpaque(false);
+        panel.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+        JLabel titleLabel = new JLabel(title);
+        titleLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
+        titleLabel.setForeground(TEXT_COLOR);
+        titleLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+        panel.add(titleLabel);
+
+        if (hint != null && !hint.trim().isEmpty()) {
+            panel.add(Box.createRigidArea(new Dimension(0, 4)));
+            JLabel hintLabel = new JLabel(hint);
+            hintLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
+            hintLabel.setForeground(LIGHT_TEXT);
+            hintLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+            panel.add(hintLabel);
+        }
+
+        return panel;
+    }
+
+    private JPanel createMethodOption(String type, String title, String description, String iconPath) {
+        JPanel option = new JPanel(new BorderLayout(15, 0));
+        option.setBackground(WHITE);
+        option.setBorder(BorderFactory.createCompoundBorder(
+                BorderFactory.createLineBorder(BORDER_COLOR, 2),
+                BorderFactory.createEmptyBorder(18, 20, 18, 20)));
+    option.putClientProperty("paddingInsets", new int[]{18, 20, 18, 20});
+        option.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+        option.setAlignmentX(Component.LEFT_ALIGNMENT);
+        option.setMaximumSize(new Dimension(520, 120));
+
+        JLabel iconLabel = new JLabel();
+        iconLabel.setHorizontalAlignment(SwingConstants.CENTER);
+        iconLabel.setPreferredSize(new Dimension(80, 80));
+        if (iconPath != null && !iconPath.trim().isEmpty()) {
+            ImageIcon rawIcon = new ImageIcon(iconPath);
+            if (rawIcon.getIconWidth() > 0 && rawIcon.getIconHeight() > 0) {
+                Image scaled = rawIcon.getImage().getScaledInstance(64, 64, Image.SCALE_SMOOTH);
+                iconLabel.setIcon(new ImageIcon(scaled));
+            }
+        }
+        if (iconLabel.getIcon() == null) {
+            iconLabel.setText("mower".equals(type) ? "馃殰" : "馃摫");
+            iconLabel.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 44));
+        }
+        option.add(iconLabel, BorderLayout.WEST);
+
+        JPanel textPanel = new JPanel();
+        textPanel.setOpaque(false);
+        textPanel.setLayout(new BoxLayout(textPanel, BoxLayout.Y_AXIS));
+
+        JLabel titleLabel = new JLabel(title);
+        titleLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
+        titleLabel.setForeground(TEXT_COLOR);
+        titleLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+        option.putClientProperty("titleLabel", titleLabel);
+        textPanel.add(titleLabel);
+
+        if (description != null && !description.trim().isEmpty()) {
+            textPanel.add(Box.createRigidArea(new Dimension(0, 8)));
+            JLabel descLabel = new JLabel(description);
+            descLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
+            descLabel.setForeground(LIGHT_TEXT);
+            descLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+            textPanel.add(descLabel);
+        }
+
+        option.add(textPanel, BorderLayout.CENTER);
+        option.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                selectMethodOption(option, type, true);
+            }
+
+            @Override
+            public void mouseEntered(MouseEvent e) {
+                if (option != selectedMethodPanel) {
+                    option.setBackground(new Color(245, 245, 245));
+                }
+            }
+
+            @Override
+            public void mouseExited(MouseEvent e) {
+                if (option != selectedMethodPanel) {
+                    option.setBackground(WHITE);
+                }
+            }
+        });
+
+        methodOptionPanels.put(type, option);
+        return option;
+    }
+
+    private JPanel createShapeOption(String type, String title, String description) {
+        JPanel option = new JPanel();
+        option.setLayout(new BoxLayout(option, BoxLayout.Y_AXIS));
+        option.setBackground(WHITE);
+        option.setAlignmentX(Component.LEFT_ALIGNMENT);
+        option.setBorder(BorderFactory.createCompoundBorder(
+                BorderFactory.createLineBorder(BORDER_COLOR, 2),
+                BorderFactory.createEmptyBorder(12, 18, 12, 18)));
+    option.putClientProperty("paddingInsets", new int[]{12, 18, 12, 18});
+        option.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+
+        JLabel titleLabel = new JLabel(title);
+        titleLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 15));
+        titleLabel.setForeground(TEXT_COLOR);
+        titleLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+        option.putClientProperty("titleLabel", titleLabel);
+        option.add(titleLabel);
+
+        if (description != null && !description.trim().isEmpty()) {
+            option.add(Box.createRigidArea(new Dimension(0, 4)));
+            JLabel descLabel = new JLabel(description);
+            descLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+            descLabel.setForeground(LIGHT_TEXT);
+            descLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+            option.add(descLabel);
+        }
+
+        option.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                selectShapeOption(option, type, true);
+            }
+
+            @Override
+            public void mouseEntered(MouseEvent e) {
+                if (option != selectedShapePanel) {
+                    option.setBackground(new Color(245, 245, 245));
+                }
+            }
+
+            @Override
+            public void mouseExited(MouseEvent e) {
+                if (option != selectedShapePanel) {
+                    option.setBackground(WHITE);
+                }
+            }
+        });
+
+        shapeOptionPanels.put(type, option);
+        return option;
+    }
+
+    private void selectMethodOption(JPanel option, String type, boolean userTriggered) {
+        if (option == null) {
+            return;
+        }
+        if (selectedMethodPanel != null && selectedMethodPanel != option) {
+            resetOptionAppearance(selectedMethodPanel);
+        }
+        selectedMethodPanel = option;
+        highlightOption(option);
+        formData.put("drawingMethod", type);
+        if (userTriggered) {
+            updateSaveButtonState();
+        }
+    }
+
+    private void selectShapeOption(JPanel option, String type, boolean userTriggered) {
+        if (option == null) {
+            return;
+        }
+        if (selectedShapePanel != null && selectedShapePanel != option) {
+            resetOptionAppearance(selectedShapePanel);
+        }
+        selectedShapePanel = option;
+        highlightOption(option);
+        formData.put("obstacleShape", type);
+        if (userTriggered) {
+            updateSaveButtonState();
+        }
+    }
+
+    private void highlightOption(JPanel option) {
+        option.setBackground(PRIMARY_LIGHT);
+        option.setBorder(buildOptionBorder(PRIMARY_COLOR, 3, option));
+        Object titleObj = option.getClientProperty("titleLabel");
+        if (titleObj instanceof JLabel) {
+            ((JLabel) titleObj).setForeground(PRIMARY_COLOR);
+        }
+    }
+
+    private void resetOptionAppearance(JPanel option) {
+        if (option == null) {
+            return;
+        }
+        option.setBackground(WHITE);
+        option.setBorder(buildOptionBorder(BORDER_COLOR, 2, option));
+        Object titleObj = option.getClientProperty("titleLabel");
+        if (titleObj instanceof JLabel) {
+            ((JLabel) titleObj).setForeground(TEXT_COLOR);
+        }
+    }
+
+    private Border buildOptionBorder(Color color, int thickness, JPanel option) {
+        Object paddingObj = option != null ? option.getClientProperty("paddingInsets") : null;
+        if (paddingObj instanceof int[]) {
+            int[] padding = (int[]) paddingObj;
+            if (padding.length == 4) {
+                Border line = BorderFactory.createLineBorder(color, thickness);
+                Border paddingBorder = BorderFactory.createEmptyBorder(padding[0], padding[1], padding[2], padding[3]);
+                return BorderFactory.createCompoundBorder(line, paddingBorder);
+            }
+        }
+        return BorderFactory.createLineBorder(color, thickness);
+    }
+
+    private void setupEventHandlers() {
+        prevButton.addActionListener(e -> {
+            if (currentStep > 1) {
+                showStep(currentStep - 1);
+            }
+        });
+
+        nextButton.addActionListener(e -> {
+            if (currentStep == 1) {
+                Coordinate.coordinates.clear();
+                showStep(2);
+            }
+        });
+
+        saveButton.addActionListener(e -> saveObstacleAndClose());
+
+        addWindowListener(new WindowAdapter() {
+            @Override
+            public void windowClosing(WindowEvent e) {
+                dispose();
+            }
+        });
+    }
+
+    private void startDrawingWorkflow() {
+        if (drawingInProgress) {
+            return;
+        }
+
+        activeDrawingShape = null;
+
+        String method = formData.get("drawingMethod");
+        if (!isMeaningfulValue(method)) {
+            JOptionPane.showMessageDialog(this, "璇烽�夋嫨缁樺埗鏂瑰紡", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+
+        String shape = formData.get("obstacleShape");
+        if (!isMeaningfulValue(shape)) {
+            JOptionPane.showMessageDialog(this, "璇烽�夋嫨闅滅鐗╁舰鐘�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+
+        String baseStation = resolveBaseStationCoordinates();
+        if (!isMeaningfulValue(baseStation)) {
+            JOptionPane.showMessageDialog(this, "鏈幏鍙栧埌鏈夋晥鐨勫熀鍑嗙珯鍧愭爣锛岃鍏堝湪鍩哄噯绔欑鐞嗕腑瀹屾垚璁剧疆", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+
+        activeDrawingShape = shape.toLowerCase(Locale.ROOT);
+
+        Coordinate.coordinates.clear();
+        Coordinate.setStartSaveGngga(true);
+        drawingInProgress = true;
+        drawButton.setText("姝e湪缁樺埗...");
+        drawButton.setEnabled(false);
+        drawingStatusLabel.setText("姝e湪閲囬泦闅滅鐗╁潗鏍囷紝璇峰湪涓荤晫闈㈠畬鎴愮粯鍒躲��");
+
+        if (activeSession == null) {
+            activeSession = new ObstacleDrawingSession();
+        }
+        activeSession.target = targetDikuai;
+        activeSession.data.clear();
+        activeSession.data.putAll(formData);
+        activeSession.baseStation = baseStation;
+        activeSession.drawingCompleted = false;
+        activeSession.captureSuccessful = false;
+        activeSession.captureMessage = null;
+
+        resumeRequested = false;
+        closeForDrawingSession();
+    }
+
+    private void closeForDrawingSession() {
+        Shouye shouye = Shouye.getInstance();
+        if (shouye != null) {
+            shouye.showEndDrawingButton(addzhangaiwu::finishDrawingSession, activeDrawingShape);
+        }
+        activeDrawingShape = null;
+        setVisible(false);
+        dispose();
+    }
+
+    public static void finishDrawingSession() {
+        Coordinate.setStartSaveGngga(false);
+
+        Shouye shouye = Shouye.getInstance();
+        if (shouye != null) {
+            shouye.hideEndDrawingButton();
+        }
+
+        if (activeSession == null) {
+            return;
+        }
+
+        processCapturedCoordinates(activeSession);
+        activeSession.drawingCompleted = true;
+        resumeRequested = true;
+
+        Component parent = shouye != null ? shouye : null;
+        showDialog(parent, activeSession.target);
+    }
+
+    /**
+     * Stops an in-progress circle capture and reopens the wizard on step 2.
+     */
+    public static void abortCircleDrawingAndReturn(String message) {
+        Coordinate.setStartSaveGngga(false);
+        Coordinate.coordinates.clear();
+
+        if (activeSession == null || activeSession.target == null) {
+            return;
+        }
+
+        activeSession.captureSuccessful = false;
+        activeSession.drawingCompleted = false;
+        activeSession.captureMessage = (message != null && !message.trim().isEmpty())
+                ? message.trim()
+                : null;
+
+        resumeRequested = true;
+
+        Component parent = Shouye.getInstance();
+        Dikuai target = activeSession.target;
+        SwingUtilities.invokeLater(() -> showDialog(parent, target));
+    }
+
+    private static void processCapturedCoordinates(ObstacleDrawingSession session) {
+        List<Coordinate> captured = new ArrayList<>(Coordinate.coordinates);
+        Coordinate.coordinates.clear();
+
+        if (captured.isEmpty()) {
+            session.captureSuccessful = false;
+            session.captureMessage = "鏈噰闆嗗埌浠讳綍闅滅鐗╁潗鏍囷紝璇烽噸鏂扮粯鍒�";
+            return;
+        }
+
+        if (!isMeaningfulValue(session.baseStation)) {
+            session.captureSuccessful = false;
+            session.captureMessage = "鍩哄噯绔欏潗鏍囨棤鏁堬紝鏃犳硶杞崲闅滅鐗╁潗鏍�";
+            return;
+        }
+
+        List<double[]> xyPoints = convertToLocalXY(captured, session.baseStation);
+        if (xyPoints.isEmpty()) {
+            session.captureSuccessful = false;
+            session.captureMessage = "鍧愭爣杞崲澶辫触锛岃妫�鏌ュ熀鍑嗙珯璁剧疆";
+            return;
+        }
+
+        String shape = session.data.get("obstacleShape");
+        if ("circle".equals(shape)) {
+            if (xyPoints.size() < 2) {
+                session.captureSuccessful = false;
+                session.captureMessage = "鍦嗗舰闅滅鐗╄嚦灏戦渶瑕佷袱涓噰闆嗙偣锛堝渾蹇冨拰鍦嗗懆鐐癸級";
+                return;
+            }
+            double[] center = xyPoints.get(0);
+            double[] radiusPoint = xyPoints.get(xyPoints.size() - 1);
+            String result = String.format(Locale.US, "%.2f,%.2f;%.2f,%.2f",
+                    center[0], center[1], radiusPoint[0], radiusPoint[1]);
+            session.data.put("obstacleCoordinates", result);
+            session.captureSuccessful = true;
+            session.captureMessage = "宸查噰闆嗗渾褰㈤殰纰嶇墿鍧愭爣";
+        } else {
+            if (xyPoints.size() < 3) {
+                session.captureSuccessful = false;
+                session.captureMessage = "澶氳竟褰㈤殰纰嶇墿鑷冲皯闇�瑕佷笁涓噰闆嗙偣";
+                return;
+            }
+            StringBuilder sb = new StringBuilder();
+            for (double[] pt : xyPoints) {
+                if (sb.length() > 0) {
+                    sb.append(";");
+                }
+                sb.append(String.format(Locale.US, "%.2f,%.2f", pt[0], pt[1]));
+            }
+            session.data.put("obstacleCoordinates", sb.toString());
+            session.captureSuccessful = true;
+            session.captureMessage = "宸查噰闆嗗杈瑰舰闅滅鐗╋紝鍏� " + xyPoints.size() + " 涓偣";
+        }
+    }
+
+    private static List<double[]> convertToLocalXY(List<Coordinate> coords, String baseStation) {
+        List<double[]> result = new ArrayList<>();
+        if (!isMeaningfulValue(baseStation)) {
+            return result;
+        }
+        String[] parts = baseStation.split(",");
+        if (parts.length < 4) {
+            return result;
+        }
+        double baseLat = parseDMToDecimal(parts[0], parts[1]);
+        double baseLon = parseDMToDecimal(parts[2], parts[3]);
+        if (!Double.isFinite(baseLat) || !Double.isFinite(baseLon)) {
+            return result;
+        }
+
+        for (Coordinate coord : coords) {
+            double lat = parseDMToDecimal(coord.getLatitude(), coord.getLatDirection());
+            double lon = parseDMToDecimal(coord.getLongitude(), coord.getLonDirection());
+            if (!Double.isFinite(lat) || !Double.isFinite(lon)) {
+                continue;
+            }
+            result.add(convertLatLonToLocal(lat, lon, baseLat, baseLon));
+        }
+        return result;
+    }
+
+    private static double parseDMToDecimal(String dmm, String direction) {
+        if (dmm == null || dmm.trim().isEmpty()) {
+            return Double.NaN;
+        }
+        try {
+            String trimmed = dmm.trim();
+            int dotIndex = trimmed.indexOf('.');
+            if (dotIndex < 2) {
+                return Double.NaN;
+            }
+            int degrees = Integer.parseInt(trimmed.substring(0, dotIndex - 2));
+            double minutes = Double.parseDouble(trimmed.substring(dotIndex - 2));
+            double decimal = degrees + minutes / 60.0;
+            if ("S".equalsIgnoreCase(direction) || "W".equalsIgnoreCase(direction)) {
+                decimal = -decimal;
+            }
+            return decimal;
+        } catch (NumberFormatException ex) {
+            return Double.NaN;
+        }
+    }
+
+    private static double[] convertLatLonToLocal(double lat, double lon, double baseLat, double baseLon) {
+        double deltaLat = lat - baseLat;
+        double deltaLon = lon - baseLon;
+        double meanLatRad = Math.toRadians((baseLat + lat) / 2.0);
+        double eastMeters = deltaLon * METERS_PER_DEGREE_LAT * Math.cos(meanLatRad);
+        double northMeters = deltaLat * METERS_PER_DEGREE_LAT;
+        return new double[]{eastMeters, northMeters};
+    }
+
+    private List<ExistingObstacle> resolveExistingObstacles(Dikuai target, List<String> providedNames) {
+        String landNumber = target != null ? target.getLandNumber() : null;
+        Map<String, ExistingObstacle> configMap = loadObstacleDetailsFromConfig(landNumber);
+        List<ExistingObstacle> result = new ArrayList<>();
+        List<String> sanitized = sanitizeObstacleNames(providedNames);
+        if (!sanitized.isEmpty()) {
+            for (String name : sanitized) {
+                ExistingObstacle info = configMap.remove(name);
+                if (info != null) {
+                    result.add(info);
+                } else {
+                    result.add(ExistingObstacle.placeholder(name));
+                }
+            }
+        }
+        if (!configMap.isEmpty()) {
+            List<ExistingObstacle> remaining = new ArrayList<>(configMap.values());
+            remaining.sort(Comparator.comparing(ExistingObstacle::getName, String.CASE_INSENSITIVE_ORDER));
+            result.addAll(remaining);
+        }
+        if (result.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return result;
+    }
+
+    private List<String> sanitizeObstacleNames(List<String> names) {
+        List<String> sanitized = new ArrayList<>();
+        if (names == null) {
+            return sanitized;
+        }
+        for (String name : names) {
+            if (isMeaningfulValue(name)) {
+                sanitized.add(name.trim());
+            }
+        }
+        return sanitized;
+    }
+
+    private Map<String, ExistingObstacle> loadObstacleDetailsFromConfig(String landNumber) {
+        Map<String, ExistingObstacle> details = new HashMap<>();
+        if (!isMeaningfulValue(landNumber)) {
+            return details;
+        }
+        try {
+            File configFile = new File("Obstacledge.properties");
+            if (!configFile.exists()) {
+                return details;
+            }
+            Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager();
+            if (!manager.loadFromFile(configFile.getAbsolutePath())) {
+                return details;
+            }
+            Obstacledge.Plot plot = manager.getPlotById(landNumber.trim());
+            if (plot == null) {
+                return details;
+            }
+            for (Obstacledge.Obstacle obstacle : plot.getObstacles()) {
+                if (obstacle == null) {
+                    continue;
+                }
+                String name = obstacle.getObstacleName();
+                if (!isMeaningfulValue(name)) {
+                    continue;
+                }
+                String trimmedName = name.trim();
+                Obstacledge.ObstacleShape shape = obstacle.getShape();
+                String xyCoords = obstacle.getXyCoordsString();
+                String original = obstacle.getOriginalCoordsString();
+                String coords = isMeaningfulValue(xyCoords) ? xyCoords.trim()
+                        : (isMeaningfulValue(original) ? original.trim() : "");
+                details.put(trimmedName, new ExistingObstacle(trimmedName, shape, coords));
+            }
+        } catch (Exception ex) {
+            System.err.println("鍔犺浇闅滅鐗╄鎯呭け璐�: " + ex.getMessage());
+        }
+        return details;
+    }
+
+    private void preloadData() {
+        String existing = targetDikuai.getObstacleCoordinates();
+        if (isMeaningfulValue(existing)) {
+            formData.put("obstacleCoordinates", existing.trim());
+        }
+        updateDrawingStatus();
+    }
+
+    private void updateDrawingStatus() {
+        if (drawingStatusLabel == null) {
+            return;
+        }
+        String coords = formData.get("obstacleCoordinates");
+        if (isMeaningfulValue(coords)) {
+            int count = countCoordinatePairs(coords);
+            drawingStatusLabel.setText("宸查噰闆嗛殰纰嶇墿鏁版嵁锛岀偣鏁帮細" + count);
+            if (!drawingInProgress && drawButton != null) {
+                drawButton.setText("閲嶆柊缁樺埗");
+                drawButton.setEnabled(true);
+            }
+        } else {
+            drawingStatusLabel.setText("灏氭湭閲囬泦闅滅鐗╁潗鏍�");
+            if (!drawingInProgress && drawButton != null) {
+                drawButton.setText("寮�濮嬬粯鍒�");
+                drawButton.setEnabled(true);
+            }
+        }
+        updateSaveButtonState();
+    }
+
+    private int countCoordinatePairs(String coords) {
+        if (!isMeaningfulValue(coords)) {
+            return 0;
+        }
+        String[] parts = coords.split(";+");
+        int count = 0;
+        for (String part : parts) {
+            if (part == null) {
+                continue;
+            }
+            String trimmed = part.trim();
+            if (trimmed.isEmpty()) {
+                continue;
+            }
+            if (trimmed.contains(",")) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    private void updateSaveButtonState() {
+        if (saveButton != null) {
+            saveButton.setEnabled(isMeaningfulValue(formData.get("obstacleCoordinates")));
+        }
+    }
+
+    private boolean validateStep2() {
+        if (!isMeaningfulValue(formData.get("obstacleCoordinates"))) {
+            JOptionPane.showMessageDialog(this, "璇峰厛瀹屾垚闅滅鐗╃粯鍒�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return false;
+        }
+        return true;
+    }
+
+    private void saveObstacleAndClose() {
+        if (!validateStep2()) {
+            return;
+        }
+        String coords = formData.get("obstacleCoordinates").trim();
+        String landNumber = targetDikuai.getLandNumber();
+        if (!Dikuai.updateField(landNumber, "obstacleCoordinates", coords)) {
+            JOptionPane.showMessageDialog(this, "鏃犳硶鏇存柊闅滅鐗╁潗鏍�", "閿欒", JOptionPane.ERROR_MESSAGE);
+            return;
+        }
+        Dikuai.updateField(landNumber, "updateTime", currentTime());
+        Dikuai.saveToProperties();
+        Dikuaiguanli.notifyExternalCreation(landNumber);
+
+        JOptionPane.showMessageDialog(this, "闅滅鐗╁潗鏍囧凡淇濆瓨", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+        activeSession = null;
+        dispose();
+    }
+
+    private static final class ExistingObstacle {
+        private final String name;
+        private final Obstacledge.ObstacleShape shape;
+        private final String coordinates;
+
+        ExistingObstacle(String name, Obstacledge.ObstacleShape shape, String coordinates) {
+            this.name = name != null ? name : "";
+            this.shape = shape;
+            this.coordinates = coordinates != null ? coordinates : "";
+        }
+
+        static ExistingObstacle placeholder(String name) {
+            return new ExistingObstacle(name, null, "");
+        }
+
+        String getName() {
+            return name;
+        }
+
+        String getShapeDisplay() {
+            if (shape == null) {
+                return "鏈煡褰㈢姸";
+            }
+            return shape.getDescription();
+        }
+
+        String getShapeKey() {
+            if (shape == Obstacledge.ObstacleShape.CIRCLE) {
+                return "circle";
+            }
+            if (shape == Obstacledge.ObstacleShape.POLYGON) {
+                return "polygon";
+            }
+            return null;
+        }
+
+        String getCoordinates() {
+            return coordinates;
+        }
+    }
+
+    private void showStep(int step) {
+        currentStep = step;
+        cardLayout.show(stepsPanel, "step" + step);
+        prevButton.setVisible(step > 1);
+        if (step < 2) {
+            nextButton.setVisible(true);
+            saveButton.setVisible(false);
+        } else {
+            nextButton.setVisible(false);
+            saveButton.setVisible(true);
+            updateDrawingStatus();
+        }
+        updateSaveButtonState();
+        revalidate();
+        repaint();
+    }
+
+    private void applySessionData(ObstacleDrawingSession session) {
+        drawingInProgress = false;
+        if (drawButton != null) {
+            drawButton.setEnabled(true);
+        }
+        formData.clear();
+        formData.putAll(session.data);
+
+        String method = session.data.get("drawingMethod");
+        if (method != null) {
+            JPanel panel = methodOptionPanels.get(method);
+            if (panel != null) {
+                selectMethodOption(panel, method, false);
+            }
+        }
+
+        String shape = session.data.get("obstacleShape");
+        if (shape != null) {
+            JPanel panel = shapeOptionPanels.get(shape);
+            if (panel != null) {
+                selectShapeOption(panel, shape, false);
+            }
+        }
+
+        if (session.captureMessage != null) {
+            JOptionPane.showMessageDialog(this,
+                    session.captureMessage,
+                    session.captureSuccessful ? "鎴愬姛" : "鎻愮ず",
+                    session.captureSuccessful ? JOptionPane.INFORMATION_MESSAGE : JOptionPane.WARNING_MESSAGE);
+        }
+
+        updateDrawingStatus();
+        currentStep = 2;
+        showStep(2);
+    }
+
+    private JPanel createInfoRow(String label, String value) {
+        return createInfoRow(label, value, null);
+    }
+
+    private JPanel createInfoRow(String label, String value, String tooltip) {
+        JPanel row = new JPanel();
+        row.setLayout(new BoxLayout(row, BoxLayout.X_AXIS));
+        row.setOpaque(false);
+        row.setAlignmentX(Component.LEFT_ALIGNMENT);
+        row.setMaximumSize(new Dimension(Integer.MAX_VALUE, 32));
+
+        JLabel labelComp = new JLabel(label);
+        labelComp.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
+        labelComp.setForeground(TEXT_COLOR);
+        labelComp.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+        JLabel valueComp = new JLabel(value != null ? value : "");
+        valueComp.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+        valueComp.setForeground(TEXT_COLOR);
+        valueComp.setVerticalAlignment(SwingConstants.TOP);
+        valueComp.setAlignmentX(Component.LEFT_ALIGNMENT);
+        if (tooltip != null && !tooltip.trim().isEmpty()) {
+            valueComp.setToolTipText(tooltip);
+        }
+
+        row.add(labelComp);
+        row.add(Box.createRigidArea(new Dimension(6, 0)));
+        row.add(valueComp);
+        return row;
+    }
+
+    private JButton createPrimaryButton(String text, int fontSize) {
+        JButton button = new JButton(text);
+        button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, fontSize));
+        button.setBackground(PRIMARY_COLOR);
+        button.setForeground(WHITE);
+        button.setBorder(BorderFactory.createCompoundBorder(
+                BorderFactory.createLineBorder(PRIMARY_DARK, 2),
+                BorderFactory.createEmptyBorder(10, 22, 10, 22)));
+        button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+        button.setFocusPainted(false);
+        button.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseEntered(MouseEvent e) {
+                if (button.isEnabled()) {
+                    button.setBackground(PRIMARY_DARK);
+                }
+            }
+
+            @Override
+            public void mouseExited(MouseEvent e) {
+                if (button.isEnabled()) {
+                    button.setBackground(PRIMARY_COLOR);
+                }
+            }
+        });
+        return button;
+    }
+
+    private JButton createSecondaryButton(String text) {
+        JButton button = new JButton(text);
+        button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
+        button.setBackground(MEDIUM_GRAY);
+        button.setForeground(TEXT_COLOR);
+        button.setBorder(BorderFactory.createCompoundBorder(
+                BorderFactory.createLineBorder(BORDER_COLOR, 2),
+                BorderFactory.createEmptyBorder(10, 22, 10, 22)));
+        button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+        button.setFocusPainted(false);
+        return button;
+    }
+
+    private static boolean isMeaningfulValue(String value) {
+        if (value == null) {
+            return false;
+        }
+        String trimmed = value.trim();
+        return !trimmed.isEmpty() && !"-1".equals(trimmed);
+    }
+
+    private String resolveBaseStationCoordinates() {
+        String coords = targetDikuai.getBaseStationCoordinates();
+        if (isMeaningfulValue(coords)) {
+            return coords.trim();
+        }
+
+        Device device = new Device();
+        device.initFromProperties();
+        coords = device.getBaseStationCoordinates();
+        if (isMeaningfulValue(coords)) {
+            return coords.trim();
+        }
+
+        BaseStation baseStation = new BaseStation();
+        baseStation.load();
+        coords = baseStation.getInstallationCoordinates();
+        if (isMeaningfulValue(coords)) {
+            return coords.trim();
+        }
+        return null;
+    }
+
+    private String safeValue(String value, String fallback) {
+        if (!isMeaningfulValue(value)) {
+            return fallback;
+        }
+        return value.trim();
+    }
+
+    private String currentTime() {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        return sdf.format(new Date());
+    }
+
+    /**
+     * 鏄剧ず鏂板闅滅鐗╁璇濇銆�
+     */
+    public static void showDialog(Component parent, Dikuai target) {
+        showDialog(parent, target, null);
+    }
+
+    public static void showDialog(Component parent, Dikuai target, List<String> obstacleNames) {
+        Window owner = null;
+        if (parent instanceof Window) {
+            owner = (Window) parent;
+        } else if (parent != null) {
+            owner = SwingUtilities.getWindowAncestor(parent);
+        }
+
+        addzhangaiwu dialog = new addzhangaiwu(owner, target, obstacleNames);
+        if (resumeRequested && activeSession != null && activeSession.target == target) {
+            dialog.applySessionData(activeSession);
+            resumeRequested = false;
+        }
+        dialog.setVisible(true);
+    }
+}
diff --git a/src/gecaoji/Device.java b/src/gecaoji/Device.java
index 0a54671..91d9bcc 100644
--- a/src/gecaoji/Device.java
+++ b/src/gecaoji/Device.java
@@ -297,10 +297,7 @@
                 return;
             }
         }
-        if (hasMeaningfulValue(mowerNumber) && hasMeaningfulValue(incomingDeviceId) && !mowerNumber.equals(incomingDeviceId)) {
-            return;
-        }
-        if (hasMeaningfulValue(incomingDeviceId)) {
+        if (hasMeaningfulValue(incomingDeviceId) && !incomingDeviceId.equals(mowerNumber)) {
             mowerNumber = incomingDeviceId;
         }
 
diff --git a/src/lujing/Lunjingguihua.java b/src/lujing/Lunjingguihua.java
new file mode 100644
index 0000000..45ecc9c
--- /dev/null
+++ b/src/lujing/Lunjingguihua.java
@@ -0,0 +1,568 @@
+package lujing;
+
+import java.awt.geom.Line2D;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Envelope;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.LinearRing;
+import org.locationtech.jts.geom.MultiLineString;
+import org.locationtech.jts.geom.MultiPolygon;
+import org.locationtech.jts.geom.Polygon;
+import org.locationtech.jts.operation.union.CascadedPolygonUnion;
+
+/**
+ * 鍓茶崏璺緞瑙勫垝瀹炵敤绫伙紝渚涘叾浠栭」鐩洿鎺ヨ皟鐢ㄣ��
+ * 鎻愪緵瀛楃涓插叆鍙傜殑鍓茶崏璺緞鐢熸垚鑳藉姏锛屽苟灏佽蹇呰鐨勮В鏋愪笌鍑犱綍澶勭悊閫昏緫銆�
+ */
+public final class Lunjingguihua {
+
+    private Lunjingguihua() {
+        throw new IllegalStateException("Utility class");
+    }
+
+    /**
+     * 鐢熸垚鍓茶崏璺緞娈靛垪琛ㄣ��
+     *
+     * @param polygonCoords 澶氳竟褰㈣竟鐣屽潗鏍囷紝鏍煎紡濡� "x1,y1;x2,y2;..."锛堢背锛�
+     * @param obstaclesCoords 闅滅鐗╁潗鏍囷紝鏀寔澶氫釜鎷彿娈垫垨鍦嗗舰瀹氫箟锛屼緥 "(x1,y1;x2,y2)(cx,cy;px,py)"
+     * @param mowingWidth 鍓茶崏瀹藉害瀛楃涓诧紝绫冲崟浣嶏紝鍏佽淇濈暀涓や綅灏忔暟
+     * @param modeStr 鍓茶崏妯″紡锛�"0"/绌轰负骞宠绾匡紝"1" 鎴� "spiral" 琛ㄧず铻烘棆妯″紡锛堝綋鍓嶄粎骞宠绾垮疄鐜帮級
+     * @return 璺緞娈靛垪琛紝鎸夎椹堕『搴忔帓鍒�
+     */
+    public static List<PathSegment> generatePathSegments(String polygonCoords,
+                                                          String obstaclesCoords,
+                                                          String mowingWidth,
+                                                          String modeStr) {
+        List<Coordinate> polygon = parseCoordinates(polygonCoords);
+        if (polygon.size() < 4) {
+            throw new IllegalArgumentException("澶氳竟褰㈠潗鏍囨暟閲忎笉瓒筹紝鑷冲皯闇�瑕佷笁涓偣");
+        }
+
+        double width = parseDoubleOrDefault(mowingWidth, 2.0);
+        if (width <= 0) {
+            throw new IllegalArgumentException("鍓茶崏瀹藉害蹇呴』澶т簬 0");
+        }
+
+        List<List<Coordinate>> obstacles = parseObstacles(obstaclesCoords);
+        String mode = normalizeMode(modeStr);
+
+        PlannerCore planner = new PlannerCore(polygon, width, mode, obstacles);
+        return planner.generate();
+    }
+
+    /**
+     * 閫氳繃瀛楃涓插弬鏁扮敓鎴愬壊鑽夎矾寰勫潗鏍囥��
+     *
+     * @param polygonCoords 澶氳竟褰㈣竟鐣屽潗鏍囷紝鏍煎紡濡� "x1,y1;x2,y2;..."锛堢背锛�
+     * @param obstaclesCoords 闅滅鐗╁潗鏍囷紝鏀寔澶氫釜鎷彿娈垫垨鍦嗗舰瀹氫箟锛屼緥 "(x1,y1;x2,y2)(cx,cy;px,py)"
+     * @param mowingWidth 鍓茶崏瀹藉害瀛楃涓诧紝绫冲崟浣嶏紝鍏佽淇濈暀涓や綅灏忔暟
+     * @param modeStr 鍓茶崏妯″紡锛�"0"/绌轰负骞宠绾匡紝"1" 鎴� "spiral" 琛ㄧず铻烘棆妯″紡锛堝綋鍓嶄粎骞宠绾垮疄鐜帮級
+     * @return 杩炵画璺緞鍧愭爣瀛楃涓诧紝椤哄簭绱ц窡鍓茶崏鏈鸿杩涜矾绾�
+     */
+    public static String generatePathFromStrings(String polygonCoords,
+                                                 String obstaclesCoords,
+                                                 String mowingWidth,
+                                                 String modeStr) {
+        List<PathSegment> segments = generatePathSegments(polygonCoords, obstaclesCoords, mowingWidth, modeStr);
+        return formatPathSegments(segments);
+    }
+
+    /**
+     * 灏嗚矾寰勬鍒楄〃杞崲涓哄潗鏍囧瓧绗︿覆銆�
+     */
+    public static String formatPathSegments(List<PathSegment> path) {
+        if (path == null || path.isEmpty()) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder();
+        Coordinate last = null;
+        for (PathSegment segment : path) {
+            if (!equals2D(last, segment.start)) {
+                appendPoint(sb, segment.start);
+                last = new Coordinate(segment.start);
+            }
+            if (!equals2D(last, segment.end)) {
+                appendPoint(sb, segment.end);
+                last = new Coordinate(segment.end);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 瑙f瀽鍧愭爣瀛楃涓层��
+     */
+    public static List<Coordinate> parseCoordinates(String s) {
+        List<Coordinate> list = new ArrayList<>();
+        if (s == null || s.trim().isEmpty()) {
+            return list;
+        }
+        String[] pts = s.split("[;\\s]+");
+        for (String p : pts) {
+            String trimmed = p.trim().replace("(", "").replace(")", "");
+            if (trimmed.isEmpty()) {
+                continue;
+            }
+            String[] xy = trimmed.split("[,锛孿\s]+");
+            if (xy.length >= 2) {
+                try {
+                    list.add(new Coordinate(Double.parseDouble(xy[0].trim()),
+                                            Double.parseDouble(xy[1].trim())));
+                } catch (NumberFormatException ex) {
+                    System.err.println("鍧愭爣瑙f瀽澶辫触: " + trimmed);
+                }
+            }
+        }
+        if (list.size() > 2 && !list.get(0).equals2D(list.get(list.size() - 1))) {
+            list.add(new Coordinate(list.get(0)));
+        }
+        return list;
+    }
+
+    /**
+     * 瑙f瀽闅滅鐗╁瓧绗︿覆锛屽吋瀹瑰闅滅鐗╀笌鍦嗗舰瀹氫箟銆�
+     */
+    public static List<List<Coordinate>> parseObstacles(String str) {
+        List<List<Coordinate>> obs = new ArrayList<>();
+        if (str == null || str.trim().isEmpty()) {
+            return obs;
+        }
+        java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\(([^)]+)\\)");
+        java.util.regex.Matcher matcher = pattern.matcher(str);
+        while (matcher.find()) {
+            List<Coordinate> coords = parseCoordinates(matcher.group(1));
+            if (coords.size() >= 3) {
+                obs.add(coords);
+            } else if (coords.size() == 2) {
+                List<Coordinate> circle = approximateCircle(coords.get(0), coords.get(1));
+                if (!circle.isEmpty()) {
+                    obs.add(circle);
+                }
+            }
+        }
+        if (obs.isEmpty()) {
+            List<Coordinate> coords = parseCoordinates(str);
+            if (coords.size() >= 3) {
+                obs.add(coords);
+            } else if (coords.size() == 2) {
+                List<Coordinate> circle = approximateCircle(coords.get(0), coords.get(1));
+                if (!circle.isEmpty()) {
+                    obs.add(circle);
+                }
+            }
+        }
+        return obs;
+    }
+
+    private static double parseDoubleOrDefault(String value, double defaultValue) {
+        if (value == null || value.trim().isEmpty()) {
+            return defaultValue;
+        }
+        try {
+            return Double.parseDouble(value.trim());
+        } catch (NumberFormatException ex) {
+            throw new IllegalArgumentException("鍓茶崏瀹藉害鏍煎紡涓嶆纭�: " + value, ex);
+        }
+    }
+
+    private static String normalizeMode(String modeStr) {
+        if (modeStr == null) {
+            return "parallel";
+        }
+        String trimmed = modeStr.trim().toLowerCase();
+        if ("1".equals(trimmed) || "spiral".equals(trimmed)) {
+            return "spiral";
+        }
+        return "parallel";
+    }
+
+    private static boolean equals2D(Coordinate a, Coordinate b) {
+        if (a == b) {
+            return true;
+        }
+        if (a == null || b == null) {
+            return false;
+        }
+        return a.equals2D(b);
+    }
+
+    private static void appendPoint(StringBuilder sb, Coordinate point) {
+        if (sb.length() > 0) {
+            sb.append(";");
+        }
+        sb.append(String.format("%.2f,%.2f", point.x, point.y));
+    }
+
+    private static List<Coordinate> approximateCircle(Coordinate center, Coordinate edge) {
+        double radius = center.distance(edge);
+        if (radius <= 0) {
+            return Collections.emptyList();
+        }
+        int segments = 36;
+        List<Coordinate> circle = new ArrayList<>(segments + 1);
+        for (int i = 0; i < segments; i++) {
+            double angle = 2 * Math.PI * i / segments;
+            double x = center.x + radius * Math.cos(angle);
+            double y = center.y + radius * Math.sin(angle);
+            circle.add(new Coordinate(x, y));
+        }
+        circle.add(new Coordinate(circle.get(0)));
+        return circle;
+    }
+
+    /**
+     * 璺緞娈垫暟鎹粨鏋勩��
+     */
+    public static final class PathSegment {
+        public Coordinate start;
+        public Coordinate end;
+        public boolean isMowing;
+        public boolean isStartPoint;
+        public boolean isEndPoint;
+
+        PathSegment(Coordinate start, Coordinate end, boolean isMowing) {
+            this.start = start;
+            this.end = end;
+            this.isMowing = isMowing;
+        }
+
+        public void setAsStartPoint() {
+            this.isStartPoint = true;
+        }
+
+        public void setAsEndPoint() {
+            this.isEndPoint = true;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("PathSegment[(%.2f,%.2f)->(%.2f,%.2f) mowing=%b start=%b end=%b]",
+                    start.x, start.y, end.x, end.y, isMowing, isStartPoint, isEndPoint);
+        }
+    }
+
+    /**
+     * 鍐呴儴鏍稿績瑙勫垝鍣紝瀹炵幇涓� MowingPathPlanner 绛夋晥鐨勯�昏緫銆�
+     */
+    private static final class PlannerCore {
+        private final List<Coordinate> polygon;
+        private final double width;
+        private final String mode;
+        private final List<List<Coordinate>> obstacles;
+        private final GeometryFactory gf = new GeometryFactory();
+
+        PlannerCore(List<Coordinate> polygon, double width, String mode, List<List<Coordinate>> obstacles) {
+            this.polygon = polygon;
+            this.width = width;
+            this.mode = mode;
+            this.obstacles = obstacles != null ? obstacles : new ArrayList<>();
+        }
+
+        List<PathSegment> generate() {
+            if (!"spiral".equals(mode)) {
+                return generateParallelPath();
+            }
+            return generateParallelPath();
+        }
+
+        private List<PathSegment> generateParallelPath() {
+            List<PathSegment> path = new ArrayList<>();
+            Geometry safeArea = buildSafeArea();
+            if (safeArea == null || safeArea.isEmpty()) {
+                System.err.println("瀹夊叏鍖哄煙涓虹┖锛屾棤娉曠敓鎴愯矾寰�");
+                return path;
+            }
+
+            LineSegment longest = findLongestEdge(polygon);
+            Vector2D baseDir = new Vector2D(longest.end.x - longest.start.x,
+                                            longest.end.y - longest.start.y).normalize();
+            Vector2D perp = baseDir.rotate90CCW();
+
+            Envelope env = safeArea.getEnvelopeInternal();
+            double minProj = perp.dot(new Vector2D(env.getMinX(), env.getMinY()));
+            double maxProj = perp.dot(new Vector2D(env.getMaxX(), env.getMaxY()));
+            double first = minProj - width / 2.0;
+
+            Geometry originalPoly = createPolygonFromCoordinates(polygon);
+            Coordinate lastEnd = null;
+            int idx = 0;
+
+            for (double offset = first; offset <= maxProj + width / 2.0; offset += width) {
+                Line2D.Double raw = createInfiniteLine(longest, perp, offset);
+                List<Line2D.Double> segs = clipLineToPolygon(raw, safeArea);
+                if (segs.isEmpty()) {
+                    continue;
+                }
+
+                List<Line2D.Double> finalSegs = new ArrayList<>();
+                for (Line2D.Double seg : segs) {
+                    finalSegs.addAll(clipLineToPolygon(seg, originalPoly));
+                }
+                if (finalSegs.isEmpty()) {
+                    continue;
+                }
+
+                finalSegs.sort((a, b) -> Double.compare(baseDir.dot(midV(a)), baseDir.dot(midV(b))));
+                boolean even = (idx % 2 == 0);
+                for (Line2D.Double seg : finalSegs) {
+                    Coordinate entry = even ? new Coordinate(seg.x1, seg.y1)
+                                            : new Coordinate(seg.x2, seg.y2);
+                    Coordinate exit = even ? new Coordinate(seg.x2, seg.y2)
+                                           : new Coordinate(seg.x1, seg.y1);
+
+                    if (lastEnd != null && lastEnd.distance(entry) > 1e-3) {
+                        path.add(new PathSegment(lastEnd, entry, false));
+                    }
+
+                    PathSegment mowingSeg = new PathSegment(entry, exit, true);
+                    if (path.isEmpty()) {
+                        mowingSeg.setAsStartPoint();
+                    }
+                    path.add(mowingSeg);
+                    lastEnd = exit;
+                }
+                idx++;
+            }
+
+            if (!path.isEmpty()) {
+                path.get(path.size() - 1).setAsEndPoint();
+            }
+
+            postProcess(path);
+            return path;
+        }
+
+        private Geometry buildSafeArea() {
+            try {
+                Coordinate[] coords = polygon.toArray(new Coordinate[0]);
+                if (!coords[0].equals2D(coords[coords.length - 1])) {
+                    coords = Arrays.copyOf(coords, coords.length + 1);
+                    coords[coords.length - 1] = coords[0];
+                }
+                Polygon origin = gf.createPolygon(gf.createLinearRing(coords));
+                Geometry result = origin;
+
+                if (!obstacles.isEmpty()) {
+                    List<Geometry> obsGeom = new ArrayList<>();
+                    for (List<Coordinate> obs : obstacles) {
+                        Geometry g = createPolygonFromCoordinates(obs);
+                        if (g != null && !g.isEmpty()) {
+                            obsGeom.add(g);
+                        }
+                    }
+                    if (!obsGeom.isEmpty()) {
+                        Geometry union = CascadedPolygonUnion.union(obsGeom);
+                        result = origin.difference(union);
+                    }
+                }
+
+                Geometry shrunk = shrinkStraight(result, width / 2.0);
+                return shrunk.isEmpty() ? result : shrunk;
+            } catch (Exception ex) {
+                System.err.println("鏋勫缓瀹夊叏鍖哄煙澶辫触: " + ex.getMessage());
+                return gf.createPolygon();
+            }
+        }
+
+        private LineSegment findLongestEdge(List<Coordinate> ring) {
+            double max = -1.0;
+            LineSegment best = null;
+            for (int i = 0; i < ring.size() - 1; i++) {
+                double d = ring.get(i).distance(ring.get(i + 1));
+                if (d > max) {
+                    max = d;
+                    best = new LineSegment(ring.get(i), ring.get(i + 1), i);
+                }
+            }
+            return best;
+        }
+
+        private Line2D.Double createInfiniteLine(LineSegment base, Vector2D perp, double offset) {
+            Vector2D baseStart = new Vector2D(base.start.x, base.start.y);
+            Vector2D baseDir = new Vector2D(base.end.x - base.start.x,
+                                            base.end.y - base.start.y).normalize();
+            Vector2D center = baseStart.add(perp.mul(offset));
+            double ext = 1.5 * diagonalLength();
+            Vector2D p1 = center.sub(baseDir.mul(ext));
+            Vector2D p2 = center.add(baseDir.mul(ext));
+            return new Line2D.Double(p1.x, p1.y, p2.x, p2.y);
+        }
+
+        private List<Line2D.Double> clipLineToPolygon(Line2D.Double line, Geometry poly) {
+            List<Line2D.Double> list = new ArrayList<>();
+            LineString ls = gf.createLineString(new Coordinate[]{
+                    new Coordinate(line.x1, line.y1),
+                    new Coordinate(line.x2, line.y2)
+            });
+            Geometry inter = poly.intersection(ls);
+            if (inter.isEmpty()) {
+                return list;
+            }
+
+            if (inter instanceof LineString) {
+                addCoords((LineString) inter, list);
+            } else if (inter instanceof MultiLineString) {
+                MultiLineString mls = (MultiLineString) inter;
+                for (int i = 0; i < mls.getNumGeometries(); i++) {
+                    addCoords((LineString) mls.getGeometryN(i), list);
+                }
+            }
+            return list;
+        }
+
+        private void addCoords(LineString ls, List<Line2D.Double> bucket) {
+            Coordinate[] cs = ls.getCoordinateSequence().toCoordinateArray();
+            for (int i = 0; i < cs.length - 1; i++) {
+                bucket.add(new Line2D.Double(cs[i].x, cs[i].y, cs[i + 1].x, cs[i + 1].y));
+            }
+        }
+
+        private Geometry createPolygonFromCoordinates(List<Coordinate> coords) {
+            if (coords.size() < 3) {
+                return gf.createPolygon();
+            }
+            List<Coordinate> closed = new ArrayList<>(coords);
+            if (!closed.get(0).equals2D(closed.get(closed.size() - 1))) {
+                closed.add(new Coordinate(closed.get(0)));
+            }
+            LinearRing shell = gf.createLinearRing(closed.toArray(new Coordinate[0]));
+            Polygon polygonGeom = gf.createPolygon(shell);
+            return polygonGeom.isValid() ? polygonGeom : (Polygon) polygonGeom.buffer(0);
+        }
+
+        private double diagonalLength() {
+            double minX = polygon.stream().mapToDouble(c -> c.x).min().orElse(0);
+            double maxX = polygon.stream().mapToDouble(c -> c.x).max().orElse(0);
+            double minY = polygon.stream().mapToDouble(c -> c.y).min().orElse(0);
+            double maxY = polygon.stream().mapToDouble(c -> c.y).max().orElse(0);
+            return Math.hypot(maxX - minX, maxY - minY);
+        }
+
+        private Vector2D midV(Line2D.Double l) {
+            return new Vector2D((l.x1 + l.x2) / 2.0, (l.y1 + l.y2) / 2.0);
+        }
+
+        private void postProcess(List<PathSegment> path) {
+            if (path == null || path.isEmpty()) {
+                return;
+            }
+            List<PathSegment> tmp = new ArrayList<>(path);
+            path.clear();
+            Coordinate prevEnd = null;
+            for (PathSegment seg : tmp) {
+                if (prevEnd != null && seg.start.distance(prevEnd) < 1e-3) {
+                    seg.start = prevEnd;
+                }
+                if (!seg.isMowing && !path.isEmpty()) {
+                    PathSegment last = path.get(path.size() - 1);
+                    if (!last.isMowing && isCollinear(last.start, last.end, seg.end)) {
+                        last.end = seg.end;
+                        prevEnd = seg.end;
+                        continue;
+                    }
+                }
+                path.add(seg);
+                prevEnd = seg.end;
+            }
+        }
+
+        private boolean isCollinear(Coordinate a, Coordinate b, Coordinate c) {
+            double dx1 = b.x - a.x;
+            double dy1 = b.y - a.y;
+            double dx2 = c.x - b.x;
+            double dy2 = c.y - b.y;
+            double cross = dx1 * dy2 - dy1 * dx2;
+            return Math.abs(cross) < 1e-6;
+        }
+
+        private Geometry shrinkStraight(Geometry outer, double dist) {
+            Geometry buf = outer.buffer(-dist);
+            if (buf.isEmpty()) {
+                return buf;
+            }
+
+            Geometry poly = (buf instanceof Polygon) ? buf
+                    : (buf instanceof MultiPolygon) ? ((MultiPolygon) buf).getGeometryN(0) : null;
+            if (!(poly instanceof Polygon)) {
+                return buf;
+            }
+
+            Coordinate[] ring = ((Polygon) poly).getExteriorRing().getCoordinateSequence().toCoordinateArray();
+            List<Coordinate> straight = new ArrayList<>();
+            final double EPS = 1e-3;
+            for (int i = 0; i < ring.length - 1; i++) {
+                Coordinate prev = (i == 0) ? ring[ring.length - 2] : ring[i - 1];
+                Coordinate curr = ring[i];
+                Coordinate next = ring[i + 1];
+                double cross = Math.abs((next.x - curr.x) * (curr.y - prev.y)
+                                      - (curr.x - prev.x) * (next.y - curr.y));
+                if (cross > EPS) {
+                    straight.add(curr);
+                }
+            }
+            if (straight.isEmpty()) {
+                return buf;
+            }
+            straight.add(new Coordinate(straight.get(0)));
+            return straight.size() < 4 ? gf.createPolygon()
+                    : gf.createPolygon(gf.createLinearRing(straight.toArray(new Coordinate[0])));
+        }
+    }
+
+    private static final class Vector2D {
+        final double x;
+        final double y;
+
+        Vector2D(double x, double y) {
+            this.x = x;
+            this.y = y;
+        }
+
+        Vector2D normalize() {
+            double len = Math.hypot(x, y);
+            if (len < 1e-12) {
+                return new Vector2D(1, 0);
+            }
+            return new Vector2D(x / len, y / len);
+        }
+
+        Vector2D rotate90CCW() {
+            return new Vector2D(-y, x);
+        }
+
+        double dot(Vector2D v) {
+            return x * v.x + y * v.y;
+        }
+
+        Vector2D sub(Vector2D v) {
+            return new Vector2D(x - v.x, y - v.y);
+        }
+
+        Vector2D add(Vector2D v) {
+            return new Vector2D(x + v.x, y + v.y);
+        }
+
+        Vector2D mul(double k) {
+            return new Vector2D(x * k, y * k);
+        }
+    }
+
+    private static final class LineSegment {
+        final Coordinate start;
+        final Coordinate end;
+        final int index;
+
+        LineSegment(Coordinate start, Coordinate end, int index) {
+            this.start = start;
+            this.end = end;
+            this.index = index;
+        }
+    }
+}
diff --git a/src/udpdell/UDPServer.java b/src/udpdell/UDPServer.java
index ee7b975..181086a 100644
--- a/src/udpdell/UDPServer.java
+++ b/src/udpdell/UDPServer.java
@@ -110,28 +110,28 @@
 			System.out.println("savenum:"+count);
 
 			Device.updateFromGNGGA(message, fields[15]);            
-			//            // 鎵撳嵃瑙f瀽鍚庣殑鏁版嵁
-			//            System.out.println("UTC鏃堕棿: " + fields[1].toUpperCase());
-			//            System.out.println("绾害: " + fields[2].toUpperCase());
-			//            System.out.println("绾害鍗婄悆: " + fields[3].toUpperCase());
-			//            System.out.println("缁忓害: " + fields[4].toUpperCase());
-			//            System.out.println("缁忓害鍗婄悆: " + fields[5].toUpperCase());
-			//            System.out.println("瀹氫綅璐ㄩ噺鎸囩ず: " + fields[6].toUpperCase());
-			//            System.out.println("鍗槦鏁伴噺: " + fields[7].toUpperCase());
-			//            System.out.println("姘村钩绮惧害鍥犲瓙: " + fields[8].toUpperCase());
-			//            System.out.println("娴锋嫈楂樺害: " + fields[9].toUpperCase());
-			//            System.out.println("娴锋嫈楂樺害鍗曚綅: " + fields[10].toUpperCase());
-			//            System.out.println("澶у湴姘村噯闈㈤珮搴�: " + fields[11].toUpperCase());
-			//            System.out.println("澶у湴姘村噯闈㈤珮搴﹀崟浣�: " + fields[12].toUpperCase());
-			//            System.out.println("宸垎鏃堕棿: " + fields[13].toUpperCase());
-			//            System.out.println("鏍¢獙鍜�: " + fields[14].toUpperCase());
-			//            System.out.println("璁惧缂栧彿: " + fields[15].toUpperCase());
-			//            System.out.println("璁惧鐢甸噺: " + fields[16].toUpperCase());
-			//            System.out.println("鍗槦淇″彿寮哄害: " + fields[17].toUpperCase());
-			//            System.out.println("淇濈暀浣�1: " + fields[18].toUpperCase());
-			//            System.out.println("淇濈暀浣�2: " + fields[19].toUpperCase());
-			//            System.out.println("淇濈暀浣�3: " + fields[20].toUpperCase());
-			//            System.out.println("----------------------------------------");
+			// 鎵撳嵃瑙f瀽鍚庣殑鏁版嵁
+			//System.out.println("UTC鏃堕棿: " + fields[1].toUpperCase());
+			//System.out.println("绾害: " + fields[2].toUpperCase());
+			//System.out.println("绾害鍗婄悆: " + fields[3].toUpperCase());
+			//System.out.println("缁忓害: " + fields[4].toUpperCase());
+			//System.out.println("缁忓害鍗婄悆: " + fields[5].toUpperCase());
+			//System.out.println("瀹氫綅璐ㄩ噺鎸囩ず: " + fields[6].toUpperCase());
+			//System.out.println("鍗槦鏁伴噺: " + fields[7].toUpperCase());
+			//System.out.println("姘村钩绮惧害鍥犲瓙: " + fields[8].toUpperCase());
+			//System.out.println("娴锋嫈楂樺害: " + fields[9].toUpperCase());
+			//System.out.println("娴锋嫈楂樺害鍗曚綅: " + fields[10].toUpperCase());
+			//System.out.println("澶у湴姘村噯闈㈤珮搴�: " + fields[11].toUpperCase());
+			//System.out.println("澶у湴姘村噯闈㈤珮搴﹀崟浣�: " + fields[12].toUpperCase());
+			//System.out.println("宸垎鏃堕棿: " + fields[13].toUpperCase());
+			//System.out.println("鏍¢獙鍜�: " + fields[14].toUpperCase());
+			//System.out.println("璁惧缂栧彿: " + fields[15].toUpperCase());
+			//System.out.println("璁惧鐢甸噺: " + fields[16].toUpperCase());
+			//System.out.println("鍗槦淇″彿寮哄害: " + fields[17].toUpperCase());
+			//System.out.println("淇濈暀浣�1: " + fields[18].toUpperCase());
+			//System.out.println("淇濈暀浣�2: " + fields[19].toUpperCase());
+			//System.out.println("淇濈暀浣�3: " + fields[20].toUpperCase());
+			//System.out.println("----------------------------------------");
 		}
 	}
 }
\ No newline at end of file
diff --git a/src/ui/UIConfig.java b/src/ui/UIConfig.java
index 8f2f173..f0c9920 100644
--- a/src/ui/UIConfig.java
+++ b/src/ui/UIConfig.java
@@ -9,8 +9,8 @@
     private UIConfig() {}
 
     // 6.5 瀵哥珫灞忓父鐢ㄥ璇濇灏哄锛堝 x 楂橈級
-    public static final int DIALOG_WIDTH = 360;
-    public static final int DIALOG_HEIGHT = 640;
+    public static final int DIALOG_WIDTH = 400;
+    public static final int DIALOG_HEIGHT = 800;
 
     // 琛ㄩ」鏈�澶ч珮搴︼紝閬垮厤鍥炬爣鎴栨寜閽瑁佸垏
     public static final int ITEM_ROW_MAX_HEIGHT = 32;
diff --git a/src/dikuai/AddDikuai.java b/src/zhangaiwu/AddDikuai.java
similarity index 78%
rename from src/dikuai/AddDikuai.java
rename to src/zhangaiwu/AddDikuai.java
index 8c59d2a..017bc55 100644
--- a/src/dikuai/AddDikuai.java
+++ b/src/zhangaiwu/AddDikuai.java
@@ -1,4 +1,4 @@
-package dikuai;
+package zhangaiwu;
 
 import javax.swing.*;
 
@@ -14,11 +14,15 @@
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 import bianjie.jisuanmianjie;
+import dikuai.Dikuai;
+import dikuai.Dikuaiguanli;
 import gecaoji.Device;
 import bianjie.bianjieguihua2;
+import lujing.Lunjingguihua;
 import ui.UIConfig;
 import zhuye.MowerLocationData;
 import zhuye.Shouye;
@@ -62,6 +66,7 @@
     private JButton nextButton;
     private JButton createButton;
     private JLabel boundaryCountLabel;
+    private JPanel obstacleListContainer;
     
     // 鍦板潡鏁版嵁
     private Map<String, String> dikuaiData = new HashMap<>();
@@ -218,6 +223,10 @@
         formGroup.add(areaNameField);
         formGroup.add(Box.createRigidArea(new Dimension(0, 8)));
         formGroup.add(hintLabel);
+
+    formGroup.add(Box.createRigidArea(new Dimension(0, 20)));
+    JPanel obstacleSection = createObstacleSummarySection();
+    formGroup.add(obstacleSection);
         
         stepPanel.add(formGroup);
         stepPanel.add(Box.createVerticalGlue());
@@ -226,6 +235,256 @@
         
         return stepPanel;
     }
+
+    private JPanel createObstacleSummarySection() {
+        JPanel section = new JPanel();
+        section.setLayout(new BoxLayout(section, BoxLayout.Y_AXIS));
+        section.setOpaque(false);
+        section.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+        JLabel title = new JLabel("宸叉湁闅滅鐗�");
+        title.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
+        title.setForeground(TEXT_COLOR);
+        title.setAlignmentX(Component.LEFT_ALIGNMENT);
+        section.add(title);
+        section.add(Box.createRigidArea(new Dimension(0, 8)));
+
+        obstacleListContainer = new JPanel();
+        obstacleListContainer.setLayout(new BoxLayout(obstacleListContainer, BoxLayout.Y_AXIS));
+        obstacleListContainer.setOpaque(false);
+        obstacleListContainer.setAlignmentX(Component.LEFT_ALIGNMENT);
+        section.add(obstacleListContainer);
+
+        updateObstacleSummary();
+
+        return section;
+    }
+
+    private void updateObstacleSummary() {
+        if (obstacleListContainer == null) {
+            return;
+        }
+
+        obstacleListContainer.removeAll();
+        List<ObstacleSummary> summaries = loadExistingObstacles();
+
+        if (summaries.isEmpty()) {
+            JLabel emptyLabel = new JLabel("鏆傛棤闅滅鐗�");
+            emptyLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
+            emptyLabel.setForeground(LIGHT_TEXT);
+            emptyLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+            obstacleListContainer.add(emptyLabel);
+        } else {
+            int index = 1;
+            for (ObstacleSummary summary : summaries) {
+                JPanel row = new JPanel(new BorderLayout());
+                row.setOpaque(false);
+                row.setAlignmentX(Component.LEFT_ALIGNMENT);
+                row.setBorder(BorderFactory.createEmptyBorder(6, 0, 6, 0));
+
+                String labelText = String.format(Locale.CHINA, "%02d. %s", index++, summary.getName());
+                JLabel nameLabel = new JLabel(labelText);
+                nameLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
+                nameLabel.setForeground(TEXT_COLOR);
+
+                JLabel coordLabel = new JLabel(summary.getDisplayCoords());
+                coordLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
+                coordLabel.setForeground(LIGHT_TEXT);
+                coordLabel.setToolTipText(summary.getFullCoords());
+
+                row.add(nameLabel, BorderLayout.WEST);
+                row.add(coordLabel, BorderLayout.CENTER);
+                obstacleListContainer.add(row);
+            }
+        }
+
+        obstacleListContainer.revalidate();
+        obstacleListContainer.repaint();
+    }
+
+    private List<ObstacleSummary> loadExistingObstacles() {
+        List<ObstacleSummary> summaries = new ArrayList<>();
+
+        String landNumber = getPendingLandNumber();
+        String raw = null;
+        if (landNumber != null) {
+            Dikuai dikuai = Dikuai.getDikuai(landNumber);
+            if (dikuai != null) {
+                raw = normalizeCoordinateValue(dikuai.getObstacleCoordinates());
+            }
+        }
+        if (raw == null) {
+            raw = normalizeCoordinateValue(dikuaiData.get("obstacleCoordinates"));
+        }
+        if (!isMeaningfulValue(raw)) {
+            return summaries;
+        }
+
+        String normalized = stripInlineComment(raw);
+        if (normalized.isEmpty()) {
+            return summaries;
+        }
+
+        List<String> entries = splitObstacleEntries(normalized);
+        int defaultIndex = 1;
+
+        for (String entry : entries) {
+            String trimmedEntry = stripInlineComment(entry);
+            if (trimmedEntry.isEmpty()) {
+                continue;
+            }
+
+            String nameToken = null;
+            String shapeToken = null;
+            String coordsSection = trimmedEntry;
+
+            if (trimmedEntry.contains("::")) {
+                String[] parts = trimmedEntry.split("::", 3);
+                if (parts.length == 3) {
+                    nameToken = parts[0].trim();
+                    shapeToken = parts[1].trim();
+                    coordsSection = parts[2].trim();
+                }
+            } else if (trimmedEntry.contains("@")) {
+                String[] parts = trimmedEntry.split("@", 3);
+                if (parts.length == 3) {
+                    nameToken = parts[0].trim();
+                    shapeToken = parts[1].trim();
+                    coordsSection = parts[2].trim();
+                } else if (parts.length == 2) {
+                    shapeToken = parts[0].trim();
+                    coordsSection = parts[1].trim();
+                }
+            } else if (trimmedEntry.contains(":")) {
+                String[] parts = trimmedEntry.split(":", 3);
+                if (parts.length == 3) {
+                    nameToken = parts[0].trim();
+                    shapeToken = parts[1].trim();
+                    coordsSection = parts[2].trim();
+                } else if (parts.length == 2) {
+                    if (looksLikeShapeToken(parts[0])) {
+                        shapeToken = parts[0].trim();
+                        coordsSection = parts[1].trim();
+                    } else {
+                        nameToken = parts[0].trim();
+                        coordsSection = parts[1].trim();
+                    }
+                }
+            }
+
+            String sanitizedCoords = sanitizeCoordinateString(coordsSection);
+            if (!isMeaningfulValue(sanitizedCoords)) {
+                continue;
+            }
+
+            String resolvedName;
+            if (nameToken != null && !nameToken.isEmpty()) {
+                resolvedName = nameToken;
+            } else {
+                resolvedName = "闅滅鐗�" + defaultIndex++;
+            }
+
+            String displayCoords = truncateCoordinateForDisplay(sanitizedCoords, 12);
+            summaries.add(new ObstacleSummary(resolvedName, sanitizedCoords, displayCoords));
+        }
+
+        return summaries;
+    }
+
+    private List<String> splitObstacleEntries(String data) {
+        List<String> entries = new ArrayList<>();
+        if (data.indexOf('|') >= 0) {
+            String[] parts = data.split("\\|");
+            for (String part : parts) {
+                if (part != null && !part.trim().isEmpty()) {
+                    entries.add(part.trim());
+                }
+            }
+        } else if (data.contains("\n")) {
+            String[] lines = data.split("\r?\n");
+            for (String line : lines) {
+                if (line != null && !line.trim().isEmpty()) {
+                    entries.add(line.trim());
+                }
+            }
+        } else {
+            entries.add(data);
+        }
+        return entries;
+    }
+
+    private String stripInlineComment(String text) {
+        if (text == null) {
+            return "";
+        }
+        int hashIndex = text.indexOf('#');
+        if (hashIndex >= 0) {
+            return text.substring(0, hashIndex).trim();
+        }
+        return text.trim();
+    }
+
+    private boolean looksLikeShapeToken(String token) {
+        if (token == null) {
+            return false;
+        }
+        String normalized = token.trim().toLowerCase(Locale.ROOT);
+        return "circle".equals(normalized)
+                || "polygon".equals(normalized)
+                || "鍦嗗舰".equals(normalized)
+                || "澶氳竟褰�".equals(normalized)
+                || "0".equals(normalized)
+                || "1".equals(normalized);
+    }
+
+    private String sanitizeCoordinateString(String value) {
+        if (value == null) {
+            return "";
+        }
+        String stripped = stripInlineComment(value);
+        if (stripped.isEmpty()) {
+            return "";
+        }
+        return stripped.replace("\r", "").replace("\n", "").replace("\t", "").replace(" ", "");
+    }
+
+    private String truncateCoordinateForDisplay(String coords, int maxLength) {
+        if (coords == null) {
+            return "";
+        }
+        String trimmed = coords.trim();
+        if (trimmed.length() <= maxLength) {
+            return trimmed;
+        }
+        if (maxLength <= 3) {
+            return trimmed.substring(0, maxLength);
+        }
+        return trimmed.substring(0, maxLength - 3) + "...";
+    }
+
+    private static final class ObstacleSummary {
+        private final String name;
+        private final String fullCoords;
+        private final String displayCoords;
+
+        ObstacleSummary(String name, String fullCoords, String displayCoords) {
+            this.name = name;
+            this.fullCoords = fullCoords;
+            this.displayCoords = displayCoords;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getFullCoords() {
+            return fullCoords;
+        }
+
+        public String getDisplayCoords() {
+            return displayCoords;
+        }
+    }
     
     private JPanel createStep2Panel() {
         JPanel stepPanel = new JPanel();
@@ -675,9 +934,89 @@
             showStep(2);
             return;
         }
-        dikuaiData.put("mowingPattern", (String) mowingPatternCombo.getSelectedItem());
-        dikuaiData.put("mowingWidth", mowingWidthSpinner.getValue().toString());
-        JOptionPane.showMessageDialog(this, "宸叉牴鎹綋鍓嶈缃敓鎴愬壊鑽夎矾寰�", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+        Dikuai dikuai = getOrCreatePendingDikuai();
+        String boundaryCoords = null;
+        if (dikuai != null) {
+            boundaryCoords = normalizeCoordinateValue(dikuai.getBoundaryCoordinates());
+        }
+        if (boundaryCoords == null) {
+            boundaryCoords = normalizeCoordinateValue(dikuaiData.get("boundaryCoordinates"));
+        }
+        if (boundaryCoords == null) {
+            JOptionPane.showMessageDialog(this, "鏈壘鍒版湁鏁堢殑鍦板潡杈圭晫鍧愭爣锛屾棤娉曠敓鎴愯矾寰�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            if (createButton != null) {
+                createButton.setEnabled(false);
+            }
+            return;
+        }
+
+        String obstacleCoords = null;
+        if (dikuai != null) {
+            obstacleCoords = normalizeCoordinateValue(dikuai.getObstacleCoordinates());
+        }
+        if (obstacleCoords == null) {
+            obstacleCoords = normalizeCoordinateValue(dikuaiData.get("obstacleCoordinates"));
+        }
+
+        String patternDisplay = (String) mowingPatternCombo.getSelectedItem();
+        dikuaiData.put("mowingPattern", patternDisplay);
+
+        Object widthObj = mowingWidthSpinner.getValue();
+        if (!(widthObj instanceof Number)) {
+            JOptionPane.showMessageDialog(this, "鍓茶崏瀹藉害杈撳叆鏃犳晥", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            if (createButton != null) {
+                createButton.setEnabled(false);
+            }
+            return;
+        }
+        double widthCm = ((Number) widthObj).doubleValue();
+        if (widthCm <= 0) {
+            JOptionPane.showMessageDialog(this, "鍓茶崏瀹藉害蹇呴』澶т簬0", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            if (createButton != null) {
+                createButton.setEnabled(false);
+            }
+            return;
+        }
+        dikuaiData.put("mowingWidth", widthObj.toString());
+
+        String widthMeters = String.format(Locale.US, "%.2f", widthCm / 100.0);
+        String plannerMode = resolvePlannerMode(patternDisplay);
+
+        try {
+            List<Lunjingguihua.PathSegment> segments = Lunjingguihua.generatePathSegments(
+                boundaryCoords,
+                obstacleCoords,
+                widthMeters,
+                plannerMode
+            );
+            String plannedPath = Lunjingguihua.formatPathSegments(segments);
+            if (!isMeaningfulValue(plannedPath)) {
+                JOptionPane.showMessageDialog(this, "鐢熸垚鍓茶崏璺緞澶辫触: 鐢熸垚缁撴灉涓虹┖", "閿欒", JOptionPane.ERROR_MESSAGE);
+                if (createButton != null) {
+                    createButton.setEnabled(false);
+                }
+                return;
+            }
+            dikuaiData.put("plannedPath", plannedPath);
+            if (createButton != null) {
+                createButton.setEnabled(true);
+            }
+            JOptionPane.showMessageDialog(this,
+                "宸叉牴鎹綋鍓嶈缃敓鎴愬壊鑽夎矾寰勶紝鍏辩敓鎴� " + segments.size() + " 娈点��",
+                "鎴愬姛",
+                JOptionPane.INFORMATION_MESSAGE);
+        } catch (IllegalArgumentException ex) {
+            JOptionPane.showMessageDialog(this, "鐢熸垚鍓茶崏璺緞澶辫触: " + ex.getMessage(), "閿欒", JOptionPane.ERROR_MESSAGE);
+            if (createButton != null) {
+                createButton.setEnabled(false);
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            JOptionPane.showMessageDialog(this, "鐢熸垚鍓茶崏璺緞鏃跺彂鐢熷紓甯�: " + ex.getMessage(), "閿欒", JOptionPane.ERROR_MESSAGE);
+            if (createButton != null) {
+                createButton.setEnabled(false);
+            }
+        }
     }
     
     private JButton createPrimaryButton(String text, int fontSize) {
@@ -732,6 +1071,7 @@
         nextButton = createPrimaryButton("涓嬩竴姝�", 16);
         createButton = createPrimaryButton("淇濆瓨", 16);
         createButton.setVisible(false);
+    createButton.setEnabled(false);
 
         buttonPanel.add(prevButton);
         buttonPanel.add(Box.createHorizontalGlue());
@@ -880,6 +1220,36 @@
             boundaryCountLabel.setVisible(false);
         }
     }
+
+    private boolean hasGeneratedPath() {
+        return isMeaningfulValue(dikuaiData.get("plannedPath"));
+    }
+
+    private String normalizeCoordinateValue(String value) {
+        return isMeaningfulValue(value) ? value.trim() : null;
+    }
+
+    private boolean isMeaningfulValue(String value) {
+        if (value == null) {
+            return false;
+        }
+        String trimmed = value.trim();
+        return !trimmed.isEmpty() && !"-1".equals(trimmed);
+    }
+
+    private String resolvePlannerMode(String patternDisplay) {
+        if (patternDisplay == null) {
+            return "parallel";
+        }
+        String trimmed = patternDisplay.trim();
+        if (trimmed.isEmpty()) {
+            return "parallel";
+        }
+        if ("铻烘棆寮�".equals(trimmed) || "spiral".equalsIgnoreCase(trimmed) || "1".equals(trimmed)) {
+            return "spiral";
+        }
+        return "parallel";
+    }
     
     private void setupEventHandlers() {
         // 涓婁竴姝ユ寜閽�
@@ -922,6 +1292,10 @@
         currentStep = step;
         cardLayout.show(stepsPanel, "step" + step);
         
+        if (step == 1) {
+            updateObstacleSummary();
+        }
+
         // 鏇存柊鎸夐挳鐘舵��
         updateButtonState(step);
     }
@@ -932,9 +1306,11 @@
         if (step < 3) {
             nextButton.setVisible(true);
             createButton.setVisible(false);
+            createButton.setEnabled(false);
         } else {
             nextButton.setVisible(false);
             createButton.setVisible(true);
+            createButton.setEnabled(hasGeneratedPath());
         }
 
         Container parent = prevButton.getParent();
@@ -977,6 +1353,10 @@
             case 3:
                 dikuaiData.put("mowingPattern", (String) mowingPatternCombo.getSelectedItem());
                 dikuaiData.put("mowingWidth", mowingWidthSpinner.getValue().toString());
+                if (!hasGeneratedPath()) {
+                    JOptionPane.showMessageDialog(this, "璇峰厛鐢熸垚鍓茶崏璺緞", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+                    return false;
+                }
                 break;
         }
         return true;
@@ -1271,6 +1651,11 @@
             dikuai.setMowingWidth(dikuaiData.get("mowingWidth"));
         }
 
+        String plannedPath = dikuaiData.get("plannedPath");
+        if (isMeaningfulValue(plannedPath)) {
+            dikuai.setPlannedPath(plannedPath);
+        }
+
         Dikuai.putDikuai(landNumber, dikuai);
         Dikuai.saveToProperties();
         
diff --git a/src/zhangaiwu/Obstacledge.java b/src/zhangaiwu/Obstacledge.java
new file mode 100644
index 0000000..2bd39e4
--- /dev/null
+++ b/src/zhangaiwu/Obstacledge.java
@@ -0,0 +1,827 @@
+package zhangaiwu;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 鍓茶崏鏈哄湴鍧楅殰纰嶇墿绫�
+ * 鐢ㄤ簬淇濆瓨鍜岀鐞嗗湴鍧楅殰纰嶇墿鐨勭浉鍏充俊鎭�
+ */
+public class Obstacledge {
+    
+    /**
+     * 闅滅鐗╁舰鐘舵灇涓�
+     */
+    public enum ObstacleShape {
+        CIRCLE(0, "鍦嗗舰"),
+        POLYGON(1, "澶氳竟褰�");
+        
+        private final int code;
+        private final String description;
+        
+        ObstacleShape(int code, String description) {
+            this.code = code;
+            this.description = description;
+        }
+        
+        public int getCode() {
+            return code;
+        }
+        
+        public String getDescription() {
+            return description;
+        }
+        
+        /**
+         * 鏍规嵁浠g爜鑾峰彇褰㈢姸鏋氫妇
+         * @param code 褰㈢姸浠g爜
+         * @return 瀵瑰簲鐨勫舰鐘舵灇涓撅紝濡傛灉鎵句笉鍒拌繑鍥瀗ull
+         */
+        public static ObstacleShape getByCode(int code) {
+            for (ObstacleShape shape : values()) {
+                if (shape.getCode() == code) {
+                    return shape;
+                }
+            }
+            return null;
+        }
+    }
+    
+    /**
+     * 鍧愭爣鐐圭被 - 鐢ㄤ簬瀛樺偍搴﹀垎鏍煎紡鐨勫潗鏍�
+     */
+    public static class DMCoordinate {
+        private double degreeMinute; // 搴﹀垎鍊硷紝濡�2324.200273
+        private char direction;      // 鏂瑰悜锛孨/S/E/W
+        
+        public DMCoordinate() {
+        }
+        
+        public DMCoordinate(double degreeMinute, char direction) {
+            this.degreeMinute = degreeMinute;
+            this.direction = direction;
+        }
+        
+        public double getDegreeMinute() {
+            return degreeMinute;
+        }
+        
+        public void setDegreeMinute(double degreeMinute) {
+            this.degreeMinute = degreeMinute;
+        }
+        
+        public char getDirection() {
+            return direction;
+        }
+        
+        public void setDirection(char direction) {
+            this.direction = direction;
+        }
+        
+        /**
+         * 灏嗗害鍒嗘牸寮忚浆鎹负搴︽牸寮�
+         * @return 鍗佽繘鍒跺害鏁�
+         */
+        public double toDecimalDegree() {
+            // 鍒嗙搴﹀拰鍒�
+            double dm = Math.abs(degreeMinute);
+            int degrees = (int)(dm / 100); // 搴﹀湪鐧句綅浠ヤ笂
+            double minutes = dm - degrees * 100;
+            
+            // 璁$畻鍗佽繘鍒跺害鏁�
+            double decimalDegrees = degrees + minutes / 60.0;
+            
+            // 鏍规嵁鏂瑰悜纭畾姝h礋
+            if (direction == 'S' || direction == 'W') {
+                decimalDegrees = -decimalDegrees;
+            }
+            
+            return decimalDegrees;
+        }
+        
+        /**
+         * 灏嗗潗鏍囩偣杞崲涓哄瓧绗︿覆鏍煎紡
+         * @return 鏍煎紡濡� "2324.200273,N"
+         */
+        @Override
+        public String toString() {
+            return String.format("%.6f,%c", degreeMinute, direction);
+        }
+        
+        /**
+         * 浠庡瓧绗︿覆瑙f瀽鍧愭爣鐐�
+         * @param coordStr 鏍煎紡濡� "2324.200273,N"
+         * @return DMCoordinate瀵硅薄
+         */
+        public static DMCoordinate fromString(String coordStr) {
+            String[] parts = coordStr.split(",");
+            if (parts.length != 2) {
+                throw new IllegalArgumentException("Invalid coordinate format: " + coordStr);
+            }
+            
+            DMCoordinate coord = new DMCoordinate();
+            coord.setDegreeMinute(Double.parseDouble(parts[0]));
+            coord.setDirection(parts[1].charAt(0));
+            
+            return coord;
+        }
+    }
+    
+    /**
+     * XY鍧愭爣鐐圭被 - 鐢ㄤ簬瀛樺偍鐩稿浜庡熀鍑嗙珯鐨勫钩闈㈠潗鏍�
+     */
+    public static class XYCoordinate {
+        private double x; // X鍧愭爣锛堜笢鏂瑰悜锛�
+        private double y; // Y鍧愭爣锛堝寳鏂瑰悜锛�
+        
+        public XYCoordinate() {
+        }
+        
+        public XYCoordinate(double x, double y) {
+            this.x = x;
+            this.y = y;
+        }
+        
+        public double getX() {
+            return x;
+        }
+        
+        public void setX(double x) {
+            this.x = x;
+        }
+        
+        public double getY() {
+            return y;
+        }
+        
+        public void setY(double y) {
+            this.y = y;
+        }
+        
+        @Override
+        public String toString() {
+            return String.format("%.2f,%.2f", x, y);
+        }
+        
+        /**
+         * 浠庡瓧绗︿覆瑙f瀽XY鍧愭爣鐐�
+         * @param xyStr 鏍煎紡濡� "5.2,3.8"
+         * @return XYCoordinate瀵硅薄
+         */
+        public static XYCoordinate fromString(String xyStr) {
+            String[] parts = xyStr.split(",");
+            if (parts.length != 2) {
+                throw new IllegalArgumentException("Invalid XY coordinate format: " + xyStr);
+            }
+            
+            XYCoordinate coord = new XYCoordinate();
+            coord.setX(Double.parseDouble(parts[0]));
+            coord.setY(Double.parseDouble(parts[1]));
+            
+            return coord;
+        }
+    }
+    
+    /**
+     * 闅滅鐗╃被
+     */
+    public static class Obstacle {
+        private String plotId;           // 鍦板潡缂栧彿
+        private String obstacleName;     // 闅滅鐗╁悕绉帮紙鍞竴锛�
+        private ObstacleShape shape;     // 闅滅鐗╁舰鐘�
+        private List<DMCoordinate> originalCoordinates = new ArrayList<>(); // 鍘熷鍧愭爣鍒楄〃
+        private List<XYCoordinate> xyCoordinates = new ArrayList<>();      // XY鍧愭爣鍒楄〃
+        
+        public Obstacle() {
+        }
+        
+        public Obstacle(String plotId, String obstacleName, ObstacleShape shape) {
+            this.plotId = plotId;
+            this.obstacleName = obstacleName;
+            this.shape = shape;
+        }
+        
+        // Getter鍜孲etter鏂规硶
+        
+        public String getPlotId() {
+            return plotId;
+        }
+        
+        public void setPlotId(String plotId) {
+            this.plotId = plotId;
+        }
+        
+        public String getObstacleName() {
+            return obstacleName;
+        }
+        
+        public void setObstacleName(String obstacleName) {
+            this.obstacleName = obstacleName;
+        }
+        
+        public ObstacleShape getShape() {
+            return shape;
+        }
+        
+        public void setShape(ObstacleShape shape) {
+            this.shape = shape;
+        }
+        
+        /**
+         * 璁剧疆褰㈢姸锛堥�氳繃浠g爜锛�
+         * @param shapeCode 褰㈢姸浠g爜锛�0=鍦嗗舰锛�1=澶氳竟褰級
+         */
+        public void setShapeByCode(int shapeCode) {
+            this.shape = ObstacleShape.getByCode(shapeCode);
+        }
+        
+        public List<DMCoordinate> getOriginalCoordinates() {
+            return originalCoordinates;
+        }
+        
+        public void setOriginalCoordinates(List<DMCoordinate> originalCoordinates) {
+            this.originalCoordinates = originalCoordinates;
+        }
+        
+        /**
+         * 娣诲姞鍘熷鍧愭爣鐐�
+         * @param coordinate 搴﹀垎鏍煎紡鍧愭爣鐐�
+         */
+        public void addOriginalCoordinate(DMCoordinate coordinate) {
+            this.originalCoordinates.add(coordinate);
+        }
+        
+        /**
+         * 娣诲姞鍘熷鍧愭爣鐐�
+         * @param degreeMinute 搴﹀垎鍊�
+         * @param direction 鏂瑰悜
+         */
+        public void addOriginalCoordinate(double degreeMinute, char direction) {
+            this.originalCoordinates.add(new DMCoordinate(degreeMinute, direction));
+        }
+        
+        public List<XYCoordinate> getXyCoordinates() {
+            return xyCoordinates;
+        }
+        
+        public void setXyCoordinates(List<XYCoordinate> xyCoordinates) {
+            this.xyCoordinates = xyCoordinates;
+        }
+        
+        /**
+         * 娣诲姞XY鍧愭爣鐐�
+         * @param coordinate XY鍧愭爣鐐�
+         */
+        public void addXyCoordinate(XYCoordinate coordinate) {
+            this.xyCoordinates.add(coordinate);
+        }
+        
+        /**
+         * 娣诲姞XY鍧愭爣鐐�
+         * @param x X鍧愭爣
+         * @param y Y鍧愭爣
+         */
+        public void addXyCoordinate(double x, double y) {
+            this.xyCoordinates.add(new XYCoordinate(x, y));
+        }
+        
+        /**
+         * 鑾峰彇鍘熷鍧愭爣瀛楃涓�
+         * @return 鏍煎紡濡� "2324.200273,N,11330.666730,E;2324.200400,N,11330.666850,E"
+         */
+        public String getOriginalCoordsString() {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < originalCoordinates.size(); i++) {
+                if (i > 0) {
+                    sb.append(";");
+                }
+                sb.append(originalCoordinates.get(i).toString());
+            }
+            return sb.toString();
+        }
+        
+        /**
+         * 璁剧疆鍘熷鍧愭爣瀛楃涓�
+         * @param coordsStr 鏍煎紡濡� "2324.200273,N,11330.666730,E;2324.200400,N,11330.666850,E"
+         */
+        public void setOriginalCoordsString(String coordsStr) {
+            originalCoordinates.clear();
+            String[] points = coordsStr.split(";");
+            for (String point : points) {
+                // 姣忎釜鐐瑰彲鑳芥槸涓や釜DMCoordinate鐨勭粍鍚堬紙绾害+缁忓害锛�
+                String[] coords = point.split(",");
+                if (coords.length == 4) {
+                    // 鏍煎紡锛氱含搴﹀害鍒�,绾害鏂瑰悜,缁忓害搴﹀垎,缁忓害鏂瑰悜
+                    double latDM = Double.parseDouble(coords[0]);
+                    char latDir = coords[1].charAt(0);
+                    double lonDM = Double.parseDouble(coords[2]);
+                    char lonDir = coords[3].charAt(0);
+                    
+                    // 娉ㄦ剰锛氬疄闄呭瓨鍌ㄦ椂闇�瑕佸垎鍒瓨鍌ㄧ含搴﹀拰缁忓害鍧愭爣鐐�
+                    // 杩欓噷绠�鍖栧鐞嗭紝瀛樺偍涓轰袱涓狣MCoordinate瀵硅薄
+                    addOriginalCoordinate(latDM, latDir);
+                    addOriginalCoordinate(lonDM, lonDir);
+                } else if (coords.length == 2) {
+                    // 鏍煎紡锛氬害鍒�,鏂瑰悜
+                    originalCoordinates.add(DMCoordinate.fromString(point));
+                }
+            }
+        }
+        
+        /**
+         * 鑾峰彇XY鍧愭爣瀛楃涓�
+         * @return 鏍煎紡濡� "5.2,3.8;7.5,6.2"
+         */
+        public String getXyCoordsString() {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < xyCoordinates.size(); i++) {
+                if (i > 0) {
+                    sb.append(";");
+                }
+                sb.append(xyCoordinates.get(i).toString());
+            }
+            return sb.toString();
+        }
+        
+        /**
+         * 璁剧疆XY鍧愭爣瀛楃涓�
+         * @param xyCoordsStr 鏍煎紡濡� "5.2,3.8;7.5,6.2"
+         */
+        public void setXyCoordsString(String xyCoordsStr) {
+            xyCoordinates.clear();
+            String[] points = xyCoordsStr.split(";");
+            for (String point : points) {
+                xyCoordinates.add(XYCoordinate.fromString(point));
+            }
+        }
+        
+        /**
+         * 楠岃瘉闅滅鐗╂暟鎹殑鏈夋晥鎬�
+         * @return 鏄惁鏈夋晥
+         */
+        public boolean isValid() {
+            if (plotId == null || plotId.isEmpty()) {
+                return false;
+            }
+            
+            if (obstacleName == null || obstacleName.isEmpty()) {
+                return false;
+            }
+            
+            if (shape == null) {
+                return false;
+            }
+            
+            // 鏍规嵁褰㈢姸楠岃瘉鍧愭爣鐐规暟閲�
+            if (shape == ObstacleShape.CIRCLE) {
+                // 鍦嗗舰闇�瑕佽嚦灏�2涓潗鏍囩偣锛堝渾蹇冨拰鍦嗕笂涓�鐐癸級
+                return originalCoordinates.size() >= 2 && xyCoordinates.size() >= 2;
+            } else if (shape == ObstacleShape.POLYGON) {
+                // 澶氳竟褰㈤渶瑕佽嚦灏�3涓潗鏍囩偣
+                return originalCoordinates.size() >= 6 && xyCoordinates.size() >= 3; // 鍘熷鍧愭爣姣忎釜鐐规湁绾害鍜岀粡搴︼紝鎵�浠ユ槸2鍊�
+            }
+            
+            return false;
+        }
+        
+        /**
+         * 璁$畻鍦嗗舰闅滅鐗╃殑鍗婂緞锛堝鏋滃舰鐘舵槸鍦嗗舰锛�
+         * @return 鍗婂緞锛堢背锛夛紝濡傛灉涓嶆槸鍦嗗舰杩斿洖-1
+         */
+        public double calculateRadius() {
+            if (shape != ObstacleShape.CIRCLE || xyCoordinates.size() < 2) {
+                return -1;
+            }
+            
+            XYCoordinate center = xyCoordinates.get(0);
+            XYCoordinate pointOnCircle = xyCoordinates.get(1);
+            
+            double dx = pointOnCircle.getX() - center.getX();
+            double dy = pointOnCircle.getY() - center.getY();
+            
+            return Math.sqrt(dx * dx + dy * dy);
+        }
+        
+        /**
+         * 璁$畻澶氳竟褰㈤殰纰嶇墿鐨勯潰绉紙濡傛灉褰㈢姸鏄杈瑰舰锛�
+         * @return 闈㈢Н锛堝钩鏂圭背锛夛紝濡傛灉涓嶆槸澶氳竟褰㈣繑鍥�-1
+         */
+        public double calculateArea() {
+            if (shape != ObstacleShape.POLYGON || xyCoordinates.size() < 3) {
+                return -1;
+            }
+            
+            double area = 0;
+            int n = xyCoordinates.size();
+            
+            for (int i = 0; i < n; i++) {
+                XYCoordinate current = xyCoordinates.get(i);
+                XYCoordinate next = xyCoordinates.get((i + 1) % n);
+                
+                area += current.getX() * next.getY();
+                area -= current.getY() * next.getX();
+            }
+            
+            return Math.abs(area) / 2.0;
+        }
+        
+        @Override
+        public String toString() {
+            return String.format("Obstacle{plotId='%s', name='%s', shape=%s, points=%d}",
+                    plotId, obstacleName, shape.getDescription(), originalCoordinates.size() / 2);
+        }
+    }
+    
+    /**
+     * 鍦板潡绫�
+     */
+    public static class Plot {
+        private String plotId;                    // 鍦板潡缂栧彿锛堝敮涓�锛�
+        private DMCoordinate baseStationLat;      // 鍩哄噯绔欑含搴�
+        private DMCoordinate baseStationLon;      // 鍩哄噯绔欑粡搴�
+        private List<Obstacle> obstacles = new ArrayList<>(); // 鍦板潡鍐呯殑闅滅鐗╁垪琛�
+        
+        public Plot() {
+        }
+        
+        public Plot(String plotId) {
+            this.plotId = plotId;
+        }
+        
+        // Getter鍜孲etter鏂规硶
+        
+        public String getPlotId() {
+            return plotId;
+        }
+        
+        public void setPlotId(String plotId) {
+            this.plotId = plotId;
+        }
+        
+        public DMCoordinate getBaseStationLat() {
+            return baseStationLat;
+        }
+        
+        public void setBaseStationLat(DMCoordinate baseStationLat) {
+            this.baseStationLat = baseStationLat;
+        }
+        
+        public DMCoordinate getBaseStationLon() {
+            return baseStationLon;
+        }
+        
+        public void setBaseStationLon(DMCoordinate baseStationLon) {
+            this.baseStationLon = baseStationLon;
+        }
+        
+        /**
+         * 璁剧疆鍩哄噯绔欏潗鏍�
+         * @param lat 绾害搴﹀垎鍧愭爣
+         * @param lon 缁忓害搴﹀垎鍧愭爣
+         */
+        public void setBaseStation(DMCoordinate lat, DMCoordinate lon) {
+            this.baseStationLat = lat;
+            this.baseStationLon = lon;
+        }
+        
+        /**
+         * 璁剧疆鍩哄噯绔欏潗鏍囧瓧绗︿覆
+         * @param baseStationStr 鏍煎紡濡� "2324.200273,N,11330.666730,E"
+         */
+        public void setBaseStationString(String baseStationStr) {
+            String[] parts = baseStationStr.split(",");
+            if (parts.length != 4) {
+                throw new IllegalArgumentException("Invalid base station format: " + baseStationStr);
+            }
+            
+            double latDM = Double.parseDouble(parts[0]);
+            char latDir = parts[1].charAt(0);
+            double lonDM = Double.parseDouble(parts[2]);
+            char lonDir = parts[3].charAt(0);
+            
+            this.baseStationLat = new DMCoordinate(latDM, latDir);
+            this.baseStationLon = new DMCoordinate(lonDM, lonDir);
+        }
+        
+        /**
+         * 鑾峰彇鍩哄噯绔欏潗鏍囧瓧绗︿覆
+         * @return 鏍煎紡濡� "2324.200273,N,11330.666730,E"
+         */
+        public String getBaseStationString() {
+            if (baseStationLat == null || baseStationLon == null) {
+                return "";
+            }
+            return String.format("%s,%s", baseStationLat.toString(), baseStationLon.toString());
+        }
+        
+        public List<Obstacle> getObstacles() {
+            return obstacles;
+        }
+        
+        public void setObstacles(List<Obstacle> obstacles) {
+            this.obstacles = obstacles;
+        }
+        
+        /**
+         * 娣诲姞闅滅鐗�
+         * @param obstacle 闅滅鐗╁璞�
+         */
+        public void addObstacle(Obstacle obstacle) {
+            this.obstacles.add(obstacle);
+        }
+        
+        /**
+         * 鏍规嵁闅滅鐗╁悕绉拌幏鍙栭殰纰嶇墿
+         * @param obstacleName 闅滅鐗╁悕绉�
+         * @return 闅滅鐗╁璞★紝濡傛灉鎵句笉鍒拌繑鍥瀗ull
+         */
+        public Obstacle getObstacleByName(String obstacleName) {
+            for (Obstacle obstacle : obstacles) {
+                if (obstacle.getObstacleName().equals(obstacleName)) {
+                    return obstacle;
+                }
+            }
+            return null;
+        }
+        
+        /**
+         * 鏍规嵁闅滅鐗╁悕绉扮Щ闄ら殰纰嶇墿
+         * @param obstacleName 闅滅鐗╁悕绉�
+         * @return 鏄惁鎴愬姛绉婚櫎
+         */
+        public boolean removeObstacleByName(String obstacleName) {
+            return obstacles.removeIf(obstacle -> obstacle.getObstacleName().equals(obstacleName));
+        }
+        
+        /**
+         * 楠岃瘉鍦板潡鏁版嵁鐨勬湁鏁堟��
+         * @return 鏄惁鏈夋晥
+         */
+        public boolean isValid() {
+            if (plotId == null || plotId.isEmpty()) {
+                return false;
+            }
+            
+            if (baseStationLat == null || baseStationLon == null) {
+                return false;
+            }
+            
+            // 楠岃瘉鎵�鏈夐殰纰嶇墿
+            for (Obstacle obstacle : obstacles) {
+                if (!obstacle.isValid()) {
+                    return false;
+                }
+            }
+            
+            return true;
+        }
+        
+        /**
+         * 鑾峰彇鍦板潡涓殰纰嶇墿鐨勬暟閲�
+         * @return 闅滅鐗╂暟閲�
+         */
+        public int getObstacleCount() {
+            return obstacles.size();
+        }
+        
+        @Override
+        public String toString() {
+            return String.format("Plot{id='%s', baseStation=%s, obstacles=%d}",
+                    plotId, getBaseStationString(), obstacles.size());
+        }
+    }
+    
+    /**
+     * 閰嶇疆鏂囦欢绠$悊鍣ㄧ被
+     */
+    public static class ConfigManager {
+        private List<Plot> plots = new ArrayList<>();
+        
+        public ConfigManager() {
+        }
+        
+        // Getter鍜孲etter鏂规硶
+        
+        public List<Plot> getPlots() {
+            return plots;
+        }
+        
+        public void setPlots(List<Plot> plots) {
+            this.plots = plots;
+        }
+        
+        /**
+         * 娣诲姞鍦板潡
+         * @param plot 鍦板潡瀵硅薄
+         */
+        public void addPlot(Plot plot) {
+            this.plots.add(plot);
+        }
+        
+        /**
+         * 鏍规嵁鍦板潡缂栧彿鑾峰彇鍦板潡
+         * @param plotId 鍦板潡缂栧彿
+         * @return 鍦板潡瀵硅薄锛屽鏋滄壘涓嶅埌杩斿洖null
+         */
+        public Plot getPlotById(String plotId) {
+            for (Plot plot : plots) {
+                if (plot.getPlotId().equals(plotId)) {
+                    return plot;
+                }
+            }
+            return null;
+        }
+        
+        /**
+         * 鏍规嵁鍦板潡缂栧彿绉婚櫎鍦板潡
+         * @param plotId 鍦板潡缂栧彿
+         * @return 鏄惁鎴愬姛绉婚櫎
+         */
+        public boolean removePlotById(String plotId) {
+            return plots.removeIf(plot -> plot.getPlotId().equals(plotId));
+        }
+        
+        /**
+         * 鑾峰彇鎵�鏈夊湴鍧椾腑鐨勯殰纰嶇墿鎬绘暟
+         * @return 闅滅鐗╂�绘暟
+         */
+        public int getTotalObstacleCount() {
+            int count = 0;
+            for (Plot plot : plots) {
+                count += plot.getObstacleCount();
+            }
+            return count;
+        }
+        
+        /**
+         * 楠岃瘉鎵�鏈夊湴鍧楁暟鎹殑鏈夋晥鎬�
+         * @return 鏄惁鎵�鏈夋暟鎹兘鏈夋晥
+         */
+        public boolean validateAll() {
+            for (Plot plot : plots) {
+                if (!plot.isValid()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        
+        /**
+         * 杞崲涓簆roperties鏂囦欢鏍煎紡瀛楃涓�
+         * @return properties鏍煎紡瀛楃涓�
+         */
+        public String toPropertiesString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("# 鍓茶崏鏈哄湴鍧楅殰纰嶇墿閰嶇疆鏂囦欢\n");
+            sb.append("# 鐢熸垚鏃堕棿锛�").append(java.time.LocalDateTime.now()).append("\n");
+            sb.append("# 鍧愭爣绯伙細WGS84锛堝害鍒嗘牸寮忥級\n\n");
+            
+            sb.append("# ============ 鍦板潡鍩哄噯绔欓厤缃� ============\n");
+            sb.append("# 鏍煎紡锛歱lot.[鍦板潡缂栧彿].baseStation=[缁忓害],[N/S],[绾害],[E/W]\n");
+            
+            // 娣诲姞鍩哄噯绔欓厤缃�
+            for (Plot plot : plots) {
+                sb.append("plot.").append(plot.getPlotId())
+                  .append(".baseStation=").append(plot.getBaseStationString())
+                  .append("\n");
+            }
+            
+            sb.append("\n# ============ 闅滅鐗╅厤缃� ============\n");
+            sb.append("# 鏍煎紡锛歱lot.[鍦板潡缂栧彿].obstacle.[闅滅鐗╁悕绉癩.shape=[0|1]\n");
+            sb.append("# 鏍煎紡锛歱lot.[鍦板潡缂栧彿].obstacle.[闅滅鐗╁悕绉癩.originalCoords=[鍧愭爣涓瞉\n");
+            sb.append("# 鏍煎紡锛歱lot.[鍦板潡缂栧彿].obstacle.[闅滅鐗╁悕绉癩.xyCoords=[鍧愭爣涓瞉\n\n");
+            
+            // 娣诲姞闅滅鐗╅厤缃�
+            for (Plot plot : plots) {
+                if (plot.getObstacleCount() > 0) {
+                    sb.append("# --- 鍦板潡").append(plot.getPlotId()).append("鐨勯殰纰嶇墿 ---\n");
+                    
+                    for (Obstacle obstacle : plot.getObstacles()) {
+                        sb.append("plot.").append(plot.getPlotId())
+                          .append(".obstacle.").append(obstacle.getObstacleName())
+                          .append(".shape=").append(obstacle.getShape().getCode())
+                          .append("\n");
+                        
+                        sb.append("plot.").append(plot.getPlotId())
+                          .append(".obstacle.").append(obstacle.getObstacleName())
+                          .append(".originalCoords=").append(obstacle.getOriginalCoordsString())
+                          .append("\n");
+                        
+                        sb.append("plot.").append(plot.getPlotId())
+                          .append(".obstacle.").append(obstacle.getObstacleName())
+                          .append(".xyCoords=").append(obstacle.getXyCoordsString())
+                          .append("\n");
+                    }
+                    sb.append("\n");
+                }
+            }
+            
+            return sb.toString();
+        }
+        
+        /**
+         * 淇濆瓨閰嶇疆鍒版枃浠�
+         * @param filePath 鏂囦欢璺緞
+         * @return 鏄惁淇濆瓨鎴愬姛
+         */
+        public boolean saveToFile(String filePath) {
+            try {
+                String propertiesContent = toPropertiesString();
+                java.nio.file.Files.write(
+                    java.nio.file.Paths.get(filePath),
+                    propertiesContent.getBytes(java.nio.charset.StandardCharsets.UTF_8)
+                );
+                return true;
+            } catch (Exception e) {
+                e.printStackTrace();
+                return false;
+            }
+        }
+        
+        /**
+         * 浠巔roperties鏂囦欢鍔犺浇閰嶇疆
+         * @param filePath 鏂囦欢璺緞
+         * @return 鏄惁鍔犺浇鎴愬姛
+         */
+        public boolean loadFromFile(String filePath) {
+            try {
+                List<String> lines = java.nio.file.Files.readAllLines(
+                    java.nio.file.Paths.get(filePath),
+                    java.nio.charset.StandardCharsets.UTF_8
+                );
+                
+                plots.clear();
+                Plot currentPlot = null;
+                Obstacle currentObstacle = null;
+                
+                for (String line : lines) {
+                    line = line.trim();
+                    
+                    // 璺宠繃绌鸿鍜屾敞閲�
+                    if (line.isEmpty() || line.startsWith("#")) {
+                        continue;
+                    }
+                    
+                    // 瑙f瀽閿�煎
+                    if (line.contains("=")) {
+                        String[] parts = line.split("=", 2);
+                        String key = parts[0].trim();
+                        String value = parts[1].trim();
+                        
+                        // 瑙f瀽閿殑缁撴瀯
+                        String[] keyParts = key.split("\\.");
+                        if (keyParts.length >= 3) {
+                            String plotId = keyParts[1];
+                            
+                            // 鏌ユ壘鎴栧垱寤哄湴鍧�
+                            if (currentPlot == null || !currentPlot.getPlotId().equals(plotId)) {
+                                currentPlot = getPlotById(plotId);
+                                if (currentPlot == null) {
+                                    currentPlot = new Plot(plotId);
+                                    addPlot(currentPlot);
+                                }
+                            }
+                            
+                            if ("baseStation".equals(keyParts[2])) {
+                                // 鍩哄噯绔欏潗鏍�
+                                currentPlot.setBaseStationString(value);
+                            } else if ("obstacle".equals(keyParts[2]) && keyParts.length >= 5) {
+                                String obstacleName = keyParts[3];
+                                String property = keyParts[4];
+                                
+                                // 鏌ユ壘鎴栧垱寤洪殰纰嶇墿
+                                if (currentObstacle == null || !currentObstacle.getObstacleName().equals(obstacleName)) {
+                                    currentObstacle = currentPlot.getObstacleByName(obstacleName);
+                                    if (currentObstacle == null) {
+                                        currentObstacle = new Obstacle();
+                                        currentObstacle.setPlotId(plotId);
+                                        currentObstacle.setObstacleName(obstacleName);
+                                        currentPlot.addObstacle(currentObstacle);
+                                    }
+                                }
+                                
+                                // 璁剧疆闅滅鐗╁睘鎬�
+                                switch (property) {
+                                    case "shape":
+                                        currentObstacle.setShapeByCode(Integer.parseInt(value));
+                                        break;
+                                    case "originalCoords":
+                                        currentObstacle.setOriginalCoordsString(value);
+                                        break;
+                                    case "xyCoords":
+                                        currentObstacle.setXyCoordsString(value);
+                                        break;
+                                }
+                            }
+                        }
+                    }
+                }
+                
+                return true;
+            } catch (Exception e) {
+                e.printStackTrace();
+                return false;
+            }
+        }
+    }    
+   
+}
\ No newline at end of file
diff --git a/src/zhangaiwu/Obstacledraw.java b/src/zhangaiwu/Obstacledraw.java
new file mode 100644
index 0000000..a01e0e0
--- /dev/null
+++ b/src/zhangaiwu/Obstacledraw.java
@@ -0,0 +1,635 @@
+package zhangaiwu;
+import javax.swing.*;
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * 闅滅鐗╃粯鍒剁被 - 璐熻矗缁樺埗鍦板潡涓殑闅滅鐗�
+ */
+public class Obstacledraw {
+    
+    // 棰滆壊瀹氫箟
+    private static final Color CIRCLE_FILL_COLOR = new Color(255, 182, 193, 120); // 鍦嗗舰濉厖鑹� - 娴呯矇绾�
+    private static final Color CIRCLE_BORDER_COLOR = new Color(199, 21, 133);    // 鍦嗗舰杈规鑹� - 娣辩矇绾�
+    private static final Color POLYGON_FILL_COLOR = new Color(173, 216, 230, 120); // 澶氳竟褰㈠~鍏呰壊 - 娴呰摑
+    private static final Color POLYGON_BORDER_COLOR = new Color(25, 25, 112);    // 澶氳竟褰㈣竟妗嗚壊 - 娣辫摑
+    private static final Color OBSTACLE_LABEL_COLOR = Color.BLACK;
+    private static final Color OBSTACLE_POINT_COLOR = Color.RED;
+    
+    // 灏哄瀹氫箟
+    private static final double OBSTACLE_POINT_SIZE = 0.1; // 闅滅鐗╂帶鍒剁偣澶у皬锛堢背锛�
+    private static final float DEFAULT_BORDER_WIDTH = 1.0f;
+    private static final float SELECTED_BORDER_WIDTH = 2.5f;
+    
+    /**
+     * 缁樺埗鍦板潡鐨勬墍鏈夐殰纰嶇墿
+     * 
+     * @param g2d 鍥惧舰涓婁笅鏂�
+     * @param obstacles 闅滅鐗╁垪琛�
+     * @param scale 缂╂斁姣斾緥
+     * @param selectedObstacleName 閫変腑鐨勯殰纰嶇墿鍚嶇О锛堝彲涓簄ull锛�
+     */
+    public static void drawObstacles(Graphics2D g2d, List<Obstacledge.Obstacle> obstacles, 
+                                     double scale, String selectedObstacleName) {
+        if (obstacles == null || obstacles.isEmpty()) {
+            return;
+        }
+        
+        // 淇濆瓨鍘熷鍙樻崲
+        AffineTransform originalTransform = g2d.getTransform();
+        
+        // 搴旂敤鍙嶉敮榻�
+        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        
+        // 缁樺埗姣忎釜闅滅鐗�
+        for (Obstacledge.Obstacle obstacle : obstacles) {
+            if (obstacle == null || !obstacle.isValid()) {
+                continue;
+            }
+            
+            // 妫�鏌ユ槸鍚﹁閫変腑
+            boolean isSelected = selectedObstacleName != null && 
+                                selectedObstacleName.equals(obstacle.getObstacleName());
+            
+            // 鏍规嵁褰㈢姸缁樺埗
+            switch (obstacle.getShape()) {
+                case CIRCLE:
+                    drawCircleObstacle(g2d, obstacle, scale, isSelected);
+                    break;
+                case POLYGON:
+                    drawPolygonObstacle(g2d, obstacle, scale, isSelected);
+                    break;
+            }
+            
+            // 缁樺埗闅滅鐗╂爣绛�
+            drawObstacleLabel(g2d, obstacle, scale);
+        }
+        
+        // 鎭㈠鍘熷鍙樻崲
+        g2d.setTransform(originalTransform);
+    }
+    
+    /**
+     * 缁樺埗鍦嗗舰闅滅鐗�
+     */
+    private static void drawCircleObstacle(Graphics2D g2d, Obstacledge.Obstacle obstacle, 
+                                           double scale, boolean isSelected) {
+        List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
+        if (xyCoords.size() < 2) {
+            return;
+        }
+        
+        // 鑾峰彇鍦嗗績鍜屽渾涓婁竴鐐�
+        Obstacledge.XYCoordinate center = xyCoords.get(0);
+        Obstacledge.XYCoordinate pointOnCircle = xyCoords.get(1);
+        
+        // 璁$畻鍗婂緞
+        double dx = pointOnCircle.getX() - center.getX();
+        double dy = pointOnCircle.getY() - center.getY();
+        double radius = Math.sqrt(dx * dx + dy * dy);
+        
+        if (radius <= 0) {
+            return;
+        }
+        
+        // 璁$畻缁樺埗浣嶇疆
+        double x = center.getX() - radius;
+        double y = center.getY() - radius;
+        double diameter = radius * 2;
+        
+        // 缁樺埗鍦嗗舰
+        Ellipse2D.Double circle = new Ellipse2D.Double(x, y, diameter, diameter);
+        
+        // 璁剧疆濉厖棰滆壊
+        g2d.setColor(CIRCLE_FILL_COLOR);
+        g2d.fill(circle);
+        
+        // 璁剧疆杈规棰滆壊鍜屽搴�
+        if (isSelected) {
+            g2d.setColor(CIRCLE_BORDER_COLOR.darker());
+            g2d.setStroke(new BasicStroke(SELECTED_BORDER_WIDTH / (float)scale));
+        } else {
+            g2d.setColor(CIRCLE_BORDER_COLOR);
+            g2d.setStroke(new BasicStroke(DEFAULT_BORDER_WIDTH / (float)scale));
+        }
+        g2d.draw(circle);
+        
+        // 缁樺埗鍦嗗績鍜屾帶鍒剁偣
+        drawControlPoints(g2d, obstacle, scale, isSelected);
+    }
+    
+    /**
+     * 缁樺埗澶氳竟褰㈤殰纰嶇墿
+     */
+    private static void drawPolygonObstacle(Graphics2D g2d, Obstacledge.Obstacle obstacle, 
+                                            double scale, boolean isSelected) {
+        List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
+        if (xyCoords.size() < 3) {
+            return;
+        }
+        
+        // 鍒涘缓澶氳竟褰㈣矾寰�
+        Path2D.Double polygon = new Path2D.Double();
+        
+        // 绉诲姩鍒扮涓�涓偣
+        Obstacledge.XYCoordinate firstCoord = xyCoords.get(0);
+        polygon.moveTo(firstCoord.getX(), firstCoord.getY());
+        
+        // 娣诲姞鍏朵粬鐐�
+        for (int i = 1; i < xyCoords.size(); i++) {
+            Obstacledge.XYCoordinate coord = xyCoords.get(i);
+            polygon.lineTo(coord.getX(), coord.getY());
+        }
+        
+        // 闂悎澶氳竟褰�
+        polygon.closePath();
+        
+        // 璁剧疆濉厖棰滆壊
+        g2d.setColor(POLYGON_FILL_COLOR);
+        g2d.fill(polygon);
+        
+        // 璁剧疆杈规棰滆壊鍜屽搴�
+        if (isSelected) {
+            g2d.setColor(POLYGON_BORDER_COLOR.darker());
+            g2d.setStroke(new BasicStroke(SELECTED_BORDER_WIDTH / (float)scale));
+        } else {
+            g2d.setColor(POLYGON_BORDER_COLOR);
+            g2d.setStroke(new BasicStroke(DEFAULT_BORDER_WIDTH / (float)scale));
+        }
+        g2d.draw(polygon);
+        
+        // 缁樺埗鎺у埗鐐�
+        drawControlPoints(g2d, obstacle, scale, isSelected);
+    }
+    
+    /**
+     * 缁樺埗闅滅鐗╂帶鍒剁偣
+     */
+    private static void drawControlPoints(Graphics2D g2d, Obstacledge.Obstacle obstacle, 
+                                          double scale, boolean isSelected) {
+        List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
+        if (xyCoords.isEmpty()) {
+            return;
+        }
+        
+        // 璁剧疆鎺у埗鐐归鑹插拰澶у皬
+        g2d.setColor(OBSTACLE_POINT_COLOR);
+        double pointSize = OBSTACLE_POINT_SIZE;
+        
+        // 濡傛灉鏄�変腑鐨勯殰纰嶇墿锛岀粯鍒舵墍鏈夋帶鍒剁偣
+        if (isSelected) {
+            for (Obstacledge.XYCoordinate coord : xyCoords) {
+                double x = coord.getX() - pointSize / 2;
+                double y = coord.getY() - pointSize / 2;
+                Ellipse2D.Double point = new Ellipse2D.Double(x, y, pointSize, pointSize);
+                g2d.fill(point);
+            }
+        } 
+        // 鍚﹀垯锛屽彧缁樺埗绗竴涓帶鍒剁偣锛堝渾褰㈤殰纰嶇墿涓哄渾蹇冿紝澶氳竟褰㈤殰纰嶇墿涓虹涓�涓《鐐癸級
+        else {
+            Obstacledge.XYCoordinate firstCoord = xyCoords.get(0);
+            double x = firstCoord.getX() - pointSize / 2;
+            double y = firstCoord.getY() - pointSize / 2;
+            Ellipse2D.Double point = new Ellipse2D.Double(x, y, pointSize, pointSize);
+            g2d.fill(point);
+        }
+    }
+    
+    /**
+     * 缁樺埗闅滅鐗╂爣绛�
+     */
+    private static void drawObstacleLabel(Graphics2D g2d, Obstacledge.Obstacle obstacle, 
+                                          double scale) {
+        List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
+        if (xyCoords.isEmpty()) {
+            return;
+        }
+        
+        // 璁$畻鏍囩浣嶇疆锛堜腑蹇冪偣锛�
+        double centerX = 0;
+        double centerY = 0;
+        
+        for (Obstacledge.XYCoordinate coord : xyCoords) {
+            centerX += coord.getX();
+            centerY += coord.getY();
+        }
+        
+        centerX /= xyCoords.size();
+        centerY /= xyCoords.size();
+        
+        // 鑾峰彇闅滅鐗╁悕绉板拰褰㈢姸
+        String obstacleName = obstacle.getObstacleName();
+        String shapeDesc = obstacle.getShape().getDescription();
+        
+        // 璁剧疆瀛椾綋鍜岄鑹�
+        g2d.setColor(OBSTACLE_LABEL_COLOR);
+        
+        // 鏍规嵁缂╂斁姣斾緥璋冩暣瀛椾綋澶у皬
+        int fontSize = (int)(10 / scale);
+        fontSize = Math.max(8, Math.min(fontSize, 14)); // 闄愬埗瀛椾綋澶у皬鑼冨洿
+        
+        g2d.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, fontSize));
+        
+        // 缁樺埗鏍囩
+        String label = String.format("%s(%s)", obstacleName, shapeDesc);
+        FontMetrics metrics = g2d.getFontMetrics();
+        int textWidth = metrics.stringWidth(label);
+        int textHeight = metrics.getHeight();
+        
+        // 鍦ㄤ腑蹇冪偣缁樺埗鏍囩
+        int textX = (int)(centerX - textWidth / 2.0);
+        int textY = (int)(centerY + textHeight / 4.0); // 绋嶅井鍚戜笅鍋忕Щ
+        
+        g2d.drawString(label, textX, textY);
+    }
+    
+    /**
+     * 妫�鏌ョ偣鏄惁鍦ㄩ殰纰嶇墿鍐�
+     * 
+     * @param worldPoint 涓栫晫鍧愭爣鐐�
+     * @param obstacle 闅滅鐗�
+     * @return 濡傛灉鐐瑰湪闅滅鐗╁唴杩斿洖true锛屽惁鍒欒繑鍥瀎alse
+     */
+    public static boolean isPointInObstacle(Point2D.Double worldPoint, Obstacledge.Obstacle obstacle) {
+        if (worldPoint == null || obstacle == null || !obstacle.isValid()) {
+            return false;
+        }
+        
+        List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
+        if (xyCoords.isEmpty()) {
+            return false;
+        }
+        
+        // 鏍规嵁闅滅鐗╁舰鐘惰繘琛屽垽鏂�
+        switch (obstacle.getShape()) {
+            case CIRCLE:
+                return isPointInCircleObstacle(worldPoint, xyCoords);
+            case POLYGON:
+                return isPointInPolygonObstacle(worldPoint, xyCoords);
+            default:
+                return false;
+        }
+    }
+    
+    /**
+     * 妫�鏌ョ偣鏄惁鍦ㄥ渾褰㈤殰纰嶇墿鍐�
+     */
+    private static boolean isPointInCircleObstacle(Point2D.Double worldPoint, 
+                                                   List<Obstacledge.XYCoordinate> xyCoords) {
+        if (xyCoords.size() < 2) {
+            return false;
+        }
+        
+        // 鍦嗗績
+        Obstacledge.XYCoordinate center = xyCoords.get(0);
+        
+        // 鍦嗕笂涓�鐐�
+        Obstacledge.XYCoordinate pointOnCircle = xyCoords.get(1);
+        
+        // 璁$畻鍗婂緞
+        double dx = pointOnCircle.getX() - center.getX();
+        double dy = pointOnCircle.getY() - center.getY();
+        double radius = Math.sqrt(dx * dx + dy * dy);
+        
+        // 璁$畻鐐瑰埌鍦嗗績鐨勮窛绂�
+        double pointDx = worldPoint.x - center.getX();
+        double pointDy = worldPoint.y - center.getY();
+        double distance = Math.sqrt(pointDx * pointDx + pointDy * pointDy);
+        
+        return distance <= radius;
+    }
+    
+    /**
+     * 妫�鏌ョ偣鏄惁鍦ㄥ杈瑰舰闅滅鐗╁唴
+     */
+    private static boolean isPointInPolygonObstacle(Point2D.Double worldPoint, 
+                                                    List<Obstacledge.XYCoordinate> xyCoords) {
+        if (xyCoords.size() < 3) {
+            return false;
+        }
+        
+        // 浣跨敤灏勭嚎娉曞垽鏂偣鏄惁鍦ㄥ杈瑰舰鍐�
+        boolean inside = false;
+        int n = xyCoords.size();
+        
+        for (int i = 0, j = n - 1; i < n; j = i++) {
+            double xi = xyCoords.get(i).getX();
+            double yi = xyCoords.get(i).getY();
+            double xj = xyCoords.get(j).getX();
+            double yj = xyCoords.get(j).getY();
+            
+            boolean intersect = ((yi > worldPoint.y) != (yj > worldPoint.y)) &&
+                               (worldPoint.x < (xj - xi) * (worldPoint.y - yi) / (yj - yi) + xi);
+            
+            if (intersect) {
+                inside = !inside;
+            }
+        }
+        
+        return inside;
+    }
+    
+    /**
+     * 妫�鏌ョ偣鏄惁鍦ㄩ殰纰嶇墿鐨勬帶鍒剁偣涓�
+     * 
+     * @param worldPoint 涓栫晫鍧愭爣鐐�
+     * @param obstacle 闅滅鐗�
+     * @param scale 缂╂斁姣斾緥
+     * @return 鎺у埗鐐圭储寮曪紙浠�0寮�濮嬶級锛屽鏋滀笉鍦ㄦ帶鍒剁偣涓婅繑鍥�-1
+     */
+    public static int getControlPointIndex(Point2D.Double worldPoint, 
+                                           Obstacledge.Obstacle obstacle, 
+                                           double scale) {
+        if (worldPoint == null || obstacle == null || !obstacle.isValid()) {
+            return -1;
+        }
+        
+        List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
+        if (xyCoords.isEmpty()) {
+            return -1;
+        }
+        
+        // 鏍规嵁闅滅鐗╁舰鐘惰绠楅�夋嫨闃堝��
+        double selectionThreshold = OBSTACLE_POINT_SIZE * 2 / scale;
+        
+        // 妫�鏌ユ瘡涓帶鍒剁偣
+        for (int i = 0; i < xyCoords.size(); i++) {
+            Obstacledge.XYCoordinate coord = xyCoords.get(i);
+            
+            double dx = worldPoint.x - coord.getX();
+            double dy = worldPoint.y - coord.getY();
+            double distance = Math.sqrt(dx * dx + dy * dy);
+            
+            if (distance <= selectionThreshold) {
+                return i;
+            }
+        }
+        
+        return -1;
+    }
+    
+    /**
+     * 鑾峰彇闅滅鐗╃殑杈圭晫妗�
+     * 
+     * @param obstacle 闅滅鐗�
+     * @return 杈圭晫妗嗭紙minX, minY, maxX, maxY锛夛紝濡傛灉鏃犳晥杩斿洖null
+     */
+    public static double[] getObstacleBounds(Obstacledge.Obstacle obstacle) {
+        if (obstacle == null || !obstacle.isValid()) {
+            return null;
+        }
+        
+        List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
+        if (xyCoords.isEmpty()) {
+            return null;
+        }
+        
+        double minX = Double.MAX_VALUE;
+        double minY = Double.MAX_VALUE;
+        double maxX = -Double.MAX_VALUE;
+        double maxY = -Double.MAX_VALUE;
+        
+        // 鏍规嵁闅滅鐗╁舰鐘惰绠楄竟鐣�
+        switch (obstacle.getShape()) {
+            case CIRCLE:
+                return getCircleObstacleBounds(xyCoords);
+            case POLYGON:
+                return getPolygonObstacleBounds(xyCoords);
+            default:
+                return null;
+        }
+    }
+    
+    /**
+     * 鑾峰彇鍦嗗舰闅滅鐗╃殑杈圭晫妗�
+     */
+    private static double[] getCircleObstacleBounds(List<Obstacledge.XYCoordinate> xyCoords) {
+        if (xyCoords.size() < 2) {
+            return null;
+        }
+        
+        // 鍦嗗績
+        Obstacledge.XYCoordinate center = xyCoords.get(0);
+        
+        // 鍦嗕笂涓�鐐�
+        Obstacledge.XYCoordinate pointOnCircle = xyCoords.get(1);
+        
+        // 璁$畻鍗婂緞
+        double dx = pointOnCircle.getX() - center.getX();
+        double dy = pointOnCircle.getY() - center.getY();
+        double radius = Math.sqrt(dx * dx + dy * dy);
+        
+        // 璁$畻杈圭晫
+        double minX = center.getX() - radius;
+        double minY = center.getY() - radius;
+        double maxX = center.getX() + radius;
+        double maxY = center.getY() + radius;
+        
+        return new double[]{minX, minY, maxX, maxY};
+    }
+    
+    /**
+     * 鑾峰彇澶氳竟褰㈤殰纰嶇墿鐨勮竟鐣屾
+     */
+    private static double[] getPolygonObstacleBounds(List<Obstacledge.XYCoordinate> xyCoords) {
+        if (xyCoords.size() < 3) {
+            return null;
+        }
+        
+        double minX = Double.MAX_VALUE;
+        double minY = Double.MAX_VALUE;
+        double maxX = -Double.MAX_VALUE;
+        double maxY = -Double.MAX_VALUE;
+        
+        for (Obstacledge.XYCoordinate coord : xyCoords) {
+            minX = Math.min(minX, coord.getX());
+            minY = Math.min(minY, coord.getY());
+            maxX = Math.max(maxX, coord.getX());
+            maxY = Math.max(maxY, coord.getY());
+        }
+        
+        return new double[]{minX, minY, maxX, maxY};
+    }
+    
+    /**
+     * 灏嗛殰纰嶇墿鍒楄〃杞崲涓虹粯鍥剧敤鐨勭偣鍒楄〃
+     * 
+     * @param obstacles 闅滅鐗╁垪琛�
+     * @return 鎵�鏈夐殰纰嶇墿鎺у埗鐐圭殑鍒楄〃
+     */
+    public static List<Point2D.Double> getObstaclePoints(List<Obstacledge.Obstacle> obstacles) {
+        List<Point2D.Double> points = new ArrayList<>();
+        
+        if (obstacles == null || obstacles.isEmpty()) {
+            return points;
+        }
+        
+        for (Obstacledge.Obstacle obstacle : obstacles) {
+            if (obstacle == null || !obstacle.isValid()) {
+                continue;
+            }
+            
+            List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
+            for (Obstacledge.XYCoordinate coord : xyCoords) {
+                points.add(new Point2D.Double(coord.getX(), coord.getY()));
+            }
+        }
+        
+        return points;
+    }
+    
+    /**
+     * 璁$畻鎵�鏈夐殰纰嶇墿鐨勬�昏竟鐣屾
+     * 
+     * @param obstacles 闅滅鐗╁垪琛�
+     * @return 杈圭晫妗嗭紙minX, minY, maxX, maxY锛夛紝濡傛灉娌℃湁闅滅鐗╄繑鍥瀗ull
+     */
+    public static double[] getAllObstaclesBounds(List<Obstacledge.Obstacle> obstacles) {
+        if (obstacles == null || obstacles.isEmpty()) {
+            return null;
+        }
+        
+        double minX = Double.MAX_VALUE;
+        double minY = Double.MAX_VALUE;
+        double maxX = -Double.MAX_VALUE;
+        double maxY = -Double.MAX_VALUE;
+        boolean hasBounds = false;
+        
+        for (Obstacledge.Obstacle obstacle : obstacles) {
+            double[] bounds = getObstacleBounds(obstacle);
+            if (bounds != null) {
+                minX = Math.min(minX, bounds[0]);
+                minY = Math.min(minY, bounds[1]);
+                maxX = Math.max(maxX, bounds[2]);
+                maxY = Math.max(maxY, bounds[3]);
+                hasBounds = true;
+            }
+        }
+        
+        if (!hasBounds) {
+            return null;
+        }
+        
+        return new double[]{minX, minY, maxX, maxY};
+    }
+    
+    /**
+     * 缁樺埗闅滅鐗╃紪杈戞ā寮忎笅鐨勮緟鍔╃嚎
+     * 
+     * @param g2d 鍥惧舰涓婁笅鏂�
+     * @param obstacle 闅滅鐗�
+     * @param scale 缂╂斁姣斾緥
+     * @param isDragging 鏄惁姝e湪鎷栨嫿
+     * @param dragPointIndex 鎷栨嫿鐨勬帶鍒剁偣绱㈠紩
+     */
+    public static void drawEditingGuide(Graphics2D g2d, Obstacledge.Obstacle obstacle, 
+                                        double scale, boolean isDragging, int dragPointIndex) {
+        if (obstacle == null || !obstacle.isValid()) {
+            return;
+        }
+        
+        List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
+        if (xyCoords.isEmpty()) {
+            return;
+        }
+        
+        // 璁剧疆杈呭姪绾挎牱寮�
+        g2d.setColor(new Color(0, 100, 0, 150)); // 鍗婇�忔槑鐨勬繁缁胯壊
+        g2d.setStroke(new BasicStroke(1.0f / (float)scale, 
+                                      BasicStroke.CAP_ROUND, 
+                                      BasicStroke.JOIN_ROUND, 
+                                      1.0f, 
+                                      new float[]{5.0f / (float)scale, 5.0f / (float)scale}, 
+                                      0.0f));
+        
+        // 鏍规嵁闅滅鐗╁舰鐘剁粯鍒惰緟鍔╃嚎
+        switch (obstacle.getShape()) {
+            case CIRCLE:
+                drawCircleEditingGuide(g2d, xyCoords, isDragging, dragPointIndex);
+                break;
+            case POLYGON:
+                drawPolygonEditingGuide(g2d, xyCoords, isDragging, dragPointIndex);
+                break;
+        }
+    }
+    
+    /**
+     * 缁樺埗鍦嗗舰闅滅鐗╃紪杈戞ā寮忎笅鐨勮緟鍔╃嚎
+     */
+    private static void drawCircleEditingGuide(Graphics2D g2d, 
+                                               List<Obstacledge.XYCoordinate> xyCoords,
+                                               boolean isDragging, int dragPointIndex) {
+        if (xyCoords.size() < 2) {
+            return;
+        }
+        
+        Obstacledge.XYCoordinate center = xyCoords.get(0);
+        Obstacledge.XYCoordinate pointOnCircle = xyCoords.get(1);
+        
+        // 缁樺埗鍗婂緞绾�
+        g2d.drawLine((int)center.getX(), (int)center.getY(), 
+                     (int)pointOnCircle.getX(), (int)pointOnCircle.getY());
+        
+        // 缁樺埗涓績鍗佸瓧绾�
+        int crossSize = 5;
+        g2d.drawLine((int)center.getX() - crossSize, (int)center.getY(), 
+                     (int)center.getX() + crossSize, (int)center.getY());
+        g2d.drawLine((int)center.getX(), (int)center.getY() - crossSize, 
+                     (int)center.getX(), (int)center.getY() + crossSize);
+        
+        // 濡傛灉姝e湪鎷栨嫿鎺у埗鐐癸紝缁樺埗鎷栨嫿绾�
+        if (isDragging && dragPointIndex >= 0 && dragPointIndex < xyCoords.size()) {
+            g2d.setColor(Color.RED);
+            g2d.setStroke(new BasicStroke(2.0f));
+            
+            Obstacledge.XYCoordinate draggedPoint = xyCoords.get(dragPointIndex);
+            
+            // 缁樺埗浠庢帶鍒剁偣鍒伴紶鏍囩殑杩炵嚎
+            // 杩欓噷闇�瑕佸閮ㄤ紶鍏ラ紶鏍囦綅缃紝鏆傛椂涓嶅疄鐜�
+        }
+    }
+    
+    /**
+     * 缁樺埗澶氳竟褰㈤殰纰嶇墿缂栬緫妯″紡涓嬬殑杈呭姪绾�
+     */
+    private static void drawPolygonEditingGuide(Graphics2D g2d, 
+                                                List<Obstacledge.XYCoordinate> xyCoords,
+                                                boolean isDragging, int dragPointIndex) {
+        if (xyCoords.size() < 3) {
+            return;
+        }
+        
+        // 缁樺埗椤剁偣涔嬮棿鐨勮緟鍔╃嚎
+        int n = xyCoords.size();
+        for (int i = 0; i < n; i++) {
+            Obstacledge.XYCoordinate current = xyCoords.get(i);
+            Obstacledge.XYCoordinate next = xyCoords.get((i + 1) % n);
+            
+            g2d.drawLine((int)current.getX(), (int)current.getY(), 
+                         (int)next.getX(), (int)next.getY());
+            
+            // 缁樺埗姣忎釜椤剁偣鐨勫崄瀛楃嚎
+            int crossSize = 3;
+            g2d.drawLine((int)current.getX() - crossSize, (int)current.getY(), 
+                         (int)current.getX() + crossSize, (int)current.getY());
+            g2d.drawLine((int)current.getX(), (int)current.getY() - crossSize, 
+                         (int)current.getX(), (int)current.getY() + crossSize);
+        }
+        
+        // 濡傛灉姝e湪鎷栨嫿鎺у埗鐐癸紝缁樺埗鎷栨嫿绾�
+        if (isDragging && dragPointIndex >= 0 && dragPointIndex < xyCoords.size()) {
+            g2d.setColor(Color.RED);
+            g2d.setStroke(new BasicStroke(2.0f));
+            
+            Obstacledge.XYCoordinate draggedPoint = xyCoords.get(dragPointIndex);
+            
+            // 缁樺埗浠庢帶鍒剁偣鍒伴紶鏍囩殑杩炵嚎
+            // 杩欓噷闇�瑕佸閮ㄤ紶鍏ラ紶鏍囦綅缃紝鏆傛椂涓嶅疄鐜�
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/zhuye/MapRenderer.java b/src/zhuye/MapRenderer.java
index 32049b4..aebc291 100644
--- a/src/zhuye/MapRenderer.java
+++ b/src/zhuye/MapRenderer.java
@@ -11,10 +11,13 @@
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.Locale;
 import gecaoji.Device;
 import gecaoji.Gecaoji;
 import dikuai.Dikuaiguanli;
 import dikuai.Dikuai;
+import zhangaiwu.Obstacledraw;
+import zhangaiwu.Obstacledge;
 
 /**
  * 鍦板浘娓叉煋鍣� - 璐熻矗鍧愭爣绯荤粯鍒躲�佽鍥惧彉鎹㈢瓑鍔熻兘
@@ -43,6 +46,9 @@
     private Rectangle2D.Double boundaryBounds;
     private List<Point2D.Double> currentPlannedPath;
     private Rectangle2D.Double plannedPathBounds;
+    private List<Obstacledge.Obstacle> currentObstacles;
+    private Rectangle2D.Double obstacleBounds;
+    private String selectedObstacleName;
     private String boundaryName;
     private boolean boundaryPointsVisible;
     private String currentBoundaryLandNumber;
@@ -202,11 +208,16 @@
 
         boolean hasBoundary = currentBoundary != null && currentBoundary.size() >= 2;
         boolean hasPlannedPath = currentPlannedPath != null && currentPlannedPath.size() >= 2;
+        boolean hasObstacles = currentObstacles != null && !currentObstacles.isEmpty();
 
         if (hasBoundary) {
             drawCurrentBoundary(g2d);
         }
 
+        if (hasObstacles) {
+            Obstacledraw.drawObstacles(g2d, currentObstacles, scale, selectedObstacleName);
+        }
+
         if (hasPlannedPath) {
             drawCurrentPlannedPath(g2d);
         }
@@ -759,6 +770,264 @@
         currentBoundaryLandNumber = null;
     }
 
+    public void setCurrentObstacles(String obstaclesData, String landNumber) {
+        if (landNumber == null) {
+            clearObstacleData();
+            if (!hasRenderableBoundary() && !hasRenderablePlannedPath()) {
+                resetView();
+            } else {
+                visualizationPanel.repaint();
+            }
+            return;
+        }
+
+        List<Obstacledge.Obstacle> parsed = parseObstacles(obstaclesData, landNumber);
+        if (parsed.isEmpty()) {
+            clearObstacleData();
+            if (!hasRenderableBoundary() && !hasRenderablePlannedPath()) {
+                resetView();
+            } else {
+                visualizationPanel.repaint();
+            }
+            return;
+        }
+
+        currentObstacles = parsed;
+        obstacleBounds = convertObstacleBounds(Obstacledraw.getAllObstaclesBounds(parsed));
+        selectedObstacleName = null;
+
+        if (!hasRenderableBoundary() && !hasRenderablePlannedPath() && obstacleBounds != null) {
+            Rectangle2D.Double bounds = obstacleBounds;
+            SwingUtilities.invokeLater(() -> {
+                fitBoundsToView(bounds);
+                visualizationPanel.repaint();
+            });
+        } else {
+            visualizationPanel.repaint();
+        }
+    }
+
+    private void clearObstacleData() {
+        currentObstacles = null;
+        obstacleBounds = null;
+        selectedObstacleName = null;
+    }
+
+    private List<Obstacledge.Obstacle> parseObstacles(String obstaclesData, String landNumber) {
+        List<Obstacledge.Obstacle> obstacles = new ArrayList<>();
+        if (obstaclesData == null) {
+            return obstacles;
+        }
+
+        String normalized = stripInlineComment(obstaclesData.trim());
+        if (normalized.isEmpty() || "-1".equals(normalized)) {
+            return obstacles;
+        }
+
+        List<String> entries = splitObstacleEntries(normalized);
+        int defaultIndex = 1;
+
+        for (String entry : entries) {
+            String trimmedEntry = stripInlineComment(entry);
+            if (trimmedEntry.isEmpty()) {
+                continue;
+            }
+
+            String nameToken = null;
+            String shapeToken = null;
+            String coordsSection = trimmedEntry;
+
+            if (trimmedEntry.contains("::")) {
+                String[] parts = trimmedEntry.split("::", 3);
+                if (parts.length == 3) {
+                    nameToken = parts[0].trim();
+                    shapeToken = parts[1].trim();
+                    coordsSection = parts[2].trim();
+                }
+            } else if (trimmedEntry.contains("@")) {
+                String[] parts = trimmedEntry.split("@", 3);
+                if (parts.length == 3) {
+                    nameToken = parts[0].trim();
+                    shapeToken = parts[1].trim();
+                    coordsSection = parts[2].trim();
+                } else if (parts.length == 2) {
+                    shapeToken = parts[0].trim();
+                    coordsSection = parts[1].trim();
+                }
+            } else if (trimmedEntry.contains(":")) {
+                String[] parts = trimmedEntry.split(":", 3);
+                if (parts.length == 3) {
+                    nameToken = parts[0].trim();
+                    shapeToken = parts[1].trim();
+                    coordsSection = parts[2].trim();
+                } else if (parts.length == 2) {
+                    if (looksLikeShapeToken(parts[0])) {
+                        shapeToken = parts[0].trim();
+                        coordsSection = parts[1].trim();
+                    } else {
+                        nameToken = parts[0].trim();
+                        coordsSection = parts[1].trim();
+                    }
+                }
+            }
+
+            List<Obstacledge.XYCoordinate> xyCoordinates = parseObstacleCoordinates(coordsSection);
+            if (xyCoordinates.size() < 2) {
+                continue;
+            }
+
+            Obstacledge.ObstacleShape shape = resolveObstacleShape(shapeToken, xyCoordinates.size());
+            if (shape == null) {
+                continue;
+            }
+
+            String obstacleName = (nameToken != null && !nameToken.isEmpty())
+                    ? nameToken
+                    : "闅滅鐗�" + defaultIndex++;
+
+            Obstacledge.Obstacle obstacle = new Obstacledge.Obstacle(landNumber, obstacleName, shape);
+            obstacle.setXyCoordinates(new ArrayList<>(xyCoordinates));
+            populateDummyOriginalCoordinates(obstacle, xyCoordinates.size());
+
+            if (obstacle.isValid()) {
+                obstacles.add(obstacle);
+            }
+        }
+
+        return obstacles;
+    }
+
+    private boolean looksLikeShapeToken(String token) {
+        if (token == null) {
+            return false;
+        }
+        String normalized = token.trim().toLowerCase(Locale.ROOT);
+        return "circle".equals(normalized)
+                || "polygon".equals(normalized)
+                || "鍦嗗舰".equals(normalized)
+                || "澶氳竟褰�".equals(normalized)
+                || "0".equals(normalized)
+                || "1".equals(normalized);
+    }
+
+    private List<Obstacledge.XYCoordinate> parseObstacleCoordinates(String coordsSection) {
+        List<Obstacledge.XYCoordinate> coords = new ArrayList<>();
+        if (coordsSection == null) {
+            return coords;
+        }
+
+        String sanitized = stripInlineComment(coordsSection.trim());
+        if (sanitized.isEmpty() || "-1".equals(sanitized)) {
+            return coords;
+        }
+
+        String[] pairs = sanitized.split(";");
+        for (String pair : pairs) {
+            if (pair == null) {
+                continue;
+            }
+            String trimmed = stripInlineComment(pair.trim());
+            if (trimmed.isEmpty()) {
+                continue;
+            }
+            String[] parts = trimmed.split(",");
+            if (parts.length < 2) {
+                continue;
+            }
+            try {
+                double x = Double.parseDouble(parts[0].trim());
+                double y = Double.parseDouble(parts[1].trim());
+                coords.add(new Obstacledge.XYCoordinate(x, y));
+            } catch (NumberFormatException ignored) {
+                // Skip malformed coordinate pair
+            }
+        }
+
+        return coords;
+    }
+
+    private Obstacledge.ObstacleShape resolveObstacleShape(String shapeToken, int coordinateCount) {
+        if (shapeToken != null && !shapeToken.trim().isEmpty()) {
+            String normalized = shapeToken.trim().toLowerCase(Locale.ROOT);
+            if ("circle".equals(normalized) || "鍦嗗舰".equals(normalized) || "0".equals(normalized)) {
+                return Obstacledge.ObstacleShape.CIRCLE;
+            }
+            if ("polygon".equals(normalized) || "澶氳竟褰�".equals(normalized) || "1".equals(normalized)) {
+                return Obstacledge.ObstacleShape.POLYGON;
+            }
+        }
+
+        if (coordinateCount == 2) {
+            return Obstacledge.ObstacleShape.CIRCLE;
+        }
+        if (coordinateCount >= 3) {
+            return Obstacledge.ObstacleShape.POLYGON;
+        }
+        return null;
+    }
+
+    private void populateDummyOriginalCoordinates(Obstacledge.Obstacle obstacle, int xyCount) {
+        List<Obstacledge.DMCoordinate> dmCoordinates = new ArrayList<>();
+        int points = Math.max(1, xyCount);
+        for (int i = 0; i < points; i++) {
+            dmCoordinates.add(new Obstacledge.DMCoordinate(0.0, 'N'));
+            dmCoordinates.add(new Obstacledge.DMCoordinate(0.0, 'E'));
+        }
+        obstacle.setOriginalCoordinates(dmCoordinates);
+    }
+
+    private List<String> splitObstacleEntries(String data) {
+        List<String> entries = new ArrayList<>();
+        if (data.indexOf('|') >= 0) {
+            String[] parts = data.split("\\|");
+            for (String part : parts) {
+                if (part != null && !part.trim().isEmpty()) {
+                    entries.add(part.trim());
+                }
+            }
+        } else if (data.contains("\n")) {
+            String[] lines = data.split("\r?\n");
+            for (String line : lines) {
+                if (line != null && !line.trim().isEmpty()) {
+                    entries.add(line.trim());
+                }
+            }
+        } else {
+            entries.add(data);
+        }
+        return entries;
+    }
+
+    private String stripInlineComment(String text) {
+        if (text == null) {
+            return "";
+        }
+        int hashIndex = text.indexOf('#');
+        if (hashIndex >= 0) {
+            return text.substring(0, hashIndex).trim();
+        }
+        return text.trim();
+    }
+
+    private Rectangle2D.Double convertObstacleBounds(double[] bounds) {
+        if (bounds == null || bounds.length < 4) {
+            return null;
+        }
+        double minX = bounds[0];
+        double minY = bounds[1];
+        double maxX = bounds[2];
+        double maxY = bounds[3];
+        return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
+    }
+
+    private boolean hasRenderableBoundary() {
+        return currentBoundary != null && currentBoundary.size() >= 2;
+    }
+
+    private boolean hasRenderablePlannedPath() {
+        return currentPlannedPath != null && currentPlannedPath.size() >= 2;
+    }
+
     private void adjustViewAfterBoundaryReset() {
         if (plannedPathBounds != null) {
             Rectangle2D.Double bounds = plannedPathBounds;
@@ -766,16 +1035,26 @@
                 fitBoundsToView(bounds);
                 visualizationPanel.repaint();
             });
-        } else {
-            resetView();
+            return;
         }
+
+        if (obstacleBounds != null) {
+            Rectangle2D.Double bounds = obstacleBounds;
+            SwingUtilities.invokeLater(() -> {
+                fitBoundsToView(bounds);
+                visualizationPanel.repaint();
+            });
+            return;
+        }
+
+        resetView();
     }
 
     public void setCurrentPlannedPath(String plannedPath) {
         if (plannedPath == null) {
             currentPlannedPath = null;
             plannedPathBounds = null;
-            if (currentBoundary == null || currentBoundary.size() < 2) {
+            if (!hasRenderableBoundary()) {
                 resetView();
             } else {
                 visualizationPanel.repaint();
@@ -787,7 +1066,7 @@
         if (parsed.size() < 2) {
             currentPlannedPath = null;
             plannedPathBounds = null;
-            if (currentBoundary == null || currentBoundary.size() < 2) {
+            if (!hasRenderableBoundary()) {
                 resetView();
             } else {
                 visualizationPanel.repaint();
@@ -800,7 +1079,7 @@
 
         Rectangle2D.Double bounds = plannedPathBounds;
         SwingUtilities.invokeLater(() -> {
-            if (currentBoundary == null || currentBoundary.size() < 2) {
+            if (!hasRenderableBoundary()) {
                 fitBoundsToView(bounds);
             }
             visualizationPanel.repaint();
diff --git a/src/zhuye/Shouye.java b/src/zhuye/Shouye.java
index 5a0d4be..14565cb 100644
--- a/src/zhuye/Shouye.java
+++ b/src/zhuye/Shouye.java
@@ -10,6 +10,7 @@
 
 import dikuai.Dikuai;
 import dikuai.Dikuaiguanli;
+import dikuai.addzhangaiwu;
 import gecaoji.Device;
 import set.Sets;
 import udpdell.UDPServer;
@@ -75,6 +76,14 @@
     private ImageIcon pauseIcon;
     private ImageIcon pauseActiveIcon;
     private ImageIcon endIcon;
+    private JPanel circleGuidancePanel;
+    private JLabel circleGuidanceLabel;
+    private JButton circleGuidancePrimaryButton;
+    private JButton circleGuidanceSecondaryButton;
+    private int circleGuidanceStep;
+    private JDialog circleGuidanceDialog;
+    private boolean circleDialogMode;
+    private ComponentAdapter circleDialogOwnerAdapter;
     
     public Shouye() {
         instance = this;
@@ -117,6 +126,7 @@
         setNavigationActive(homeNavBtn);
 
         initializeDefaultAreaSelection();
+        refreshMapForSelectedArea();
     }
     
     private void createHeaderPanel() {
@@ -250,8 +260,10 @@
         JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 20, 0));
         buttonPanel.setBackground(PANEL_BACKGROUND);
         
-        startBtn = createControlButton("寮�濮�", THEME_COLOR);
-        pauseBtn = createControlButton("鏆傚仠", Color.ORANGE);
+    startBtn = createControlButton("寮�濮�", THEME_COLOR);
+    applyButtonIcon(startBtn, "image/startzuoye.png");
+    pauseBtn = createControlButton("鏆傚仠", Color.ORANGE);
+    applyButtonIcon(pauseBtn, "image/zantingzuoye.png");
         pauseBtn.setEnabled(false);
         
         buttonPanel.add(startBtn);
@@ -349,6 +361,20 @@
         
         return button;
     }
+
+    private void applyButtonIcon(JButton button, String imagePath) {
+        try {
+            ImageIcon icon = new ImageIcon(imagePath);
+            Image scaledImage = icon.getImage().getScaledInstance(28, 28, Image.SCALE_SMOOTH);
+            button.setIcon(new ImageIcon(scaledImage));
+            button.setHorizontalAlignment(SwingConstants.CENTER);
+            button.setIconTextGap(10);
+            button.setHorizontalTextPosition(SwingConstants.RIGHT);
+            button.setVerticalTextPosition(SwingConstants.CENTER);
+        } catch (Exception e) {
+            System.err.println("鏃犳硶鍔犺浇鎸夐挳鍥炬爣: " + imagePath + " -> " + e.getMessage());
+        }
+    }
     
     private JButton createNavButton(String text, String icon) {
         JButton button = new JButton("<html><center>" + icon + "<br>" + text + "</center></html>");
@@ -625,8 +651,45 @@
     }
 
     public void showEndDrawingButton(Runnable callback) {
-        ensureFloatingIconsLoaded();
+        showEndDrawingButton(callback, null);
+    }
 
+    public void showEndDrawingButton(Runnable callback, String drawingShape) {
+        endDrawingCallback = callback;
+        applyDrawingPauseState(false, false);
+        circleDialogMode = drawingShape != null && "circle".equalsIgnoreCase(drawingShape);
+
+        if (circleDialogMode) {
+            hideFloatingDrawingControls();
+            ensureCircleGuidancePanel();
+            showCircleGuidanceStep(1);
+            visualizationPanel.revalidate();
+            visualizationPanel.repaint();
+            return;
+        }
+
+        circleDialogMode = false;
+        hideCircleGuidancePanel();
+
+        ensureFloatingIconsLoaded();
+        ensureFloatingButtonInfrastructure();
+
+        endDrawingButton.setVisible(true);
+        if (drawingPauseButton != null) {
+            drawingPauseButton.setVisible(true);
+        }
+
+        floatingButtonPanel.setVisible(true);
+        if (floatingButtonPanel.getParent() != visualizationPanel) {
+            visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH);
+        }
+
+        rebuildFloatingButtonColumn();
+        visualizationPanel.revalidate();
+        visualizationPanel.repaint();
+    }
+
+    private void ensureFloatingButtonInfrastructure() {
         if (endDrawingButton == null) {
             endDrawingButton = createFloatingIconButton();
             endDrawingButton.addActionListener(e -> {
@@ -657,41 +720,288 @@
             floatingButtonColumn = new JPanel();
             floatingButtonColumn.setOpaque(false);
             floatingButtonColumn.setLayout(new BoxLayout(floatingButtonColumn, BoxLayout.Y_AXIS));
-            floatingButtonColumn.add(drawingPauseButton);
-            floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
-            floatingButtonColumn.add(endDrawingButton);
             floatingButtonPanel.add(floatingButtonColumn, BorderLayout.EAST);
-        } else if (floatingButtonColumn != null) {
-            floatingButtonColumn.removeAll();
-            floatingButtonColumn.add(drawingPauseButton);
-            floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
-            floatingButtonColumn.add(endDrawingButton);
         }
-
-        endDrawingCallback = callback;
-        endDrawingButton.setVisible(true);
-        if (drawingPauseButton != null) {
-            drawingPauseButton.setVisible(true);
-        }
-        applyDrawingPauseState(false, false);
-        floatingButtonPanel.setVisible(true);
-        if (floatingButtonPanel.getParent() != visualizationPanel) {
-            visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH);
-        }
-        visualizationPanel.revalidate();
-        visualizationPanel.repaint();
     }
 
-    public void hideEndDrawingButton() {
-        if (endDrawingButton != null) {
-            endDrawingButton.setVisible(false);
-        }
+    private void hideFloatingDrawingControls() {
         if (drawingPauseButton != null) {
             drawingPauseButton.setVisible(false);
         }
+        if (endDrawingButton != null) {
+            endDrawingButton.setVisible(false);
+        }
         if (floatingButtonPanel != null) {
             floatingButtonPanel.setVisible(false);
         }
+        if (!circleDialogMode) {
+            rebuildFloatingButtonColumn();
+        }
+    }
+
+    private void rebuildFloatingButtonColumn() {
+        if (floatingButtonColumn == null) {
+            return;
+        }
+        floatingButtonColumn.removeAll();
+        boolean added = false;
+        if (!circleDialogMode && circleGuidancePanel != null && circleGuidancePanel.isVisible()) {
+            floatingButtonColumn.add(circleGuidancePanel);
+            added = true;
+        }
+        if (!circleDialogMode && drawingPauseButton != null && drawingPauseButton.isVisible()) {
+            if (added) {
+                floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
+            }
+            floatingButtonColumn.add(drawingPauseButton);
+            added = true;
+        }
+        if (!circleDialogMode && endDrawingButton != null && endDrawingButton.isVisible()) {
+            if (added) {
+                floatingButtonColumn.add(Box.createRigidArea(new Dimension(0, 10)));
+            }
+            floatingButtonColumn.add(endDrawingButton);
+        }
+        floatingButtonColumn.revalidate();
+        floatingButtonColumn.repaint();
+    }
+
+    private void showCircleGuidanceStep(int step) {
+        ensureCircleGuidancePanel();
+        if (circleGuidancePanel == null) {
+            return;
+        }
+        circleGuidanceStep = step;
+        if (step == 1) {
+            circleGuidanceLabel.setText("鏄惁灏嗗綋鍓嶇偣璁剧疆涓哄渾蹇冿紵");
+            circleGuidancePrimaryButton.setText("鏄�");
+            circleGuidanceSecondaryButton.setText("杩斿洖");
+            circleGuidanceSecondaryButton.setVisible(true);
+        } else if (step == 2) {
+            circleGuidanceLabel.setText("鏄惁灏嗗綋鍓嶇偣璁剧疆涓哄崐寰勭偣锛�");
+            circleGuidancePrimaryButton.setText("鏄�");
+            circleGuidanceSecondaryButton.setVisible(false);
+        } else {
+            hideCircleGuidancePanel();
+            return;
+        }
+        circleGuidancePanel.setVisible(true);
+
+        if (circleDialogMode) {
+            ensureCircleGuidanceDialog();
+            if (circleGuidanceDialog != null) {
+                circleGuidanceDialog.pack();
+                positionCircleGuidanceDialog();
+                circleGuidanceDialog.setVisible(true);
+                circleGuidanceDialog.toFront();
+            }
+        } else {
+            rebuildFloatingButtonColumn();
+        }
+    }
+
+    private void ensureCircleGuidancePanel() {
+        if (circleGuidancePanel != null) {
+            return;
+        }
+        circleGuidancePanel = new JPanel();
+        circleGuidancePanel.setLayout(new BoxLayout(circleGuidancePanel, BoxLayout.Y_AXIS));
+        circleGuidancePanel.setOpaque(true);
+        circleGuidancePanel.setBackground(new Color(255, 255, 255, 235));
+        circleGuidancePanel.setBorder(BorderFactory.createCompoundBorder(
+                BorderFactory.createLineBorder(THEME_COLOR, 1),
+                BorderFactory.createEmptyBorder(10, 12, 10, 12)));
+        circleGuidancePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+        circleGuidanceLabel = new JLabel();
+        circleGuidanceLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 13));
+        circleGuidanceLabel.setForeground(new Color(33, 37, 41));
+        circleGuidanceLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+        JPanel buttonRow = new JPanel();
+        buttonRow.setLayout(new BoxLayout(buttonRow, BoxLayout.X_AXIS));
+        buttonRow.setOpaque(false);
+        buttonRow.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+        circleGuidancePrimaryButton = createGuidanceButton("鏄�", THEME_COLOR, Color.WHITE, true);
+        circleGuidancePrimaryButton.addActionListener(e -> onCircleGuidancePrimary());
+
+        circleGuidanceSecondaryButton = createGuidanceButton("鍚�", Color.WHITE, THEME_COLOR, false);
+        circleGuidanceSecondaryButton.addActionListener(e -> onCircleGuidanceSecondary());
+
+        buttonRow.add(circleGuidancePrimaryButton);
+        buttonRow.add(Box.createRigidArea(new Dimension(8, 0)));
+        buttonRow.add(circleGuidanceSecondaryButton);
+
+        circleGuidancePanel.add(circleGuidanceLabel);
+        circleGuidancePanel.add(Box.createRigidArea(new Dimension(0, 8)));
+        circleGuidancePanel.add(buttonRow);
+        circleGuidancePanel.setVisible(false);
+    }
+
+    private void ensureCircleGuidanceDialog() {
+        if (circleGuidancePanel == null) {
+            return;
+        }
+        if (circleGuidanceDialog != null) {
+            if (circleGuidancePanel.getParent() != circleGuidanceDialog.getContentPane()) {
+                detachCircleGuidancePanel();
+                circleGuidanceDialog.getContentPane().removeAll();
+                circleGuidanceDialog.getContentPane().add(circleGuidancePanel, BorderLayout.CENTER);
+                circleGuidanceDialog.pack();
+            }
+            return;
+        }
+
+        Window owner = SwingUtilities.getWindowAncestor(this);
+        circleGuidanceDialog = new JDialog(owner, "缁樺埗鎻愮ず", Dialog.ModalityType.MODELESS);
+        circleGuidanceDialog.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
+        circleGuidanceDialog.setResizable(false);
+        circleGuidanceDialog.setAlwaysOnTop(true);
+
+        if (owner != null && circleDialogOwnerAdapter == null) {
+            circleDialogOwnerAdapter = new ComponentAdapter() {
+                @Override
+                public void componentMoved(ComponentEvent e) {
+                    positionCircleGuidanceDialog();
+                }
+
+                @Override
+                public void componentResized(ComponentEvent e) {
+                    positionCircleGuidanceDialog();
+                }
+            };
+            owner.addComponentListener(circleDialogOwnerAdapter);
+        }
+
+        detachCircleGuidancePanel();
+        circleGuidanceDialog.getContentPane().setLayout(new BorderLayout());
+        circleGuidanceDialog.getContentPane().add(circleGuidancePanel, BorderLayout.CENTER);
+        circleGuidanceDialog.pack();
+    }
+
+    private void detachCircleGuidancePanel() {
+        if (circleGuidancePanel == null) {
+            return;
+        }
+        Container parent = circleGuidancePanel.getParent();
+        if (parent != null) {
+            parent.remove(circleGuidancePanel);
+            parent.revalidate();
+            parent.repaint();
+        }
+    }
+
+    private void positionCircleGuidanceDialog() {
+        if (circleGuidanceDialog == null) {
+            return;
+        }
+        Window owner = SwingUtilities.getWindowAncestor(this);
+        if (owner == null || !owner.isShowing()) {
+            return;
+        }
+        try {
+            Point ownerLocation = owner.getLocationOnScreen();
+            int x = ownerLocation.x + owner.getWidth() - circleGuidanceDialog.getWidth() - 30;
+            int y = ownerLocation.y + owner.getHeight() - circleGuidanceDialog.getHeight() - 40;
+            x = Math.max(ownerLocation.x, x);
+            y = Math.max(ownerLocation.y, y);
+            circleGuidanceDialog.setLocation(x, y);
+        } catch (IllegalComponentStateException ex) {
+            // Owner not yet displayable; skip positioning
+        }
+    }
+
+    private JButton createGuidanceButton(String text, Color bg, Color fg, boolean filled) {
+        JButton button = new JButton(text);
+        button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 12));
+        button.setForeground(fg);
+        button.setBackground(bg);
+    button.setOpaque(true);
+        button.setFocusPainted(false);
+        button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+        button.setAlignmentX(Component.LEFT_ALIGNMENT);
+        if (filled) {
+            button.setBorder(BorderFactory.createEmptyBorder(6, 14, 6, 14));
+        } else {
+            button.setBackground(Color.WHITE);
+            button.setOpaque(true);
+            button.setBorder(BorderFactory.createCompoundBorder(
+                    BorderFactory.createLineBorder(THEME_COLOR, 1),
+                    BorderFactory.createEmptyBorder(5, 12, 5, 12)));
+        }
+        button.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseEntered(MouseEvent e) {
+                if (filled) {
+                    button.setBackground(THEME_HOVER_COLOR);
+                } else {
+                    button.setBackground(new Color(245, 245, 245));
+                }
+            }
+
+            @Override
+            public void mouseExited(MouseEvent e) {
+                if (filled) {
+                    button.setBackground(bg);
+                } else {
+                    button.setBackground(Color.WHITE);
+                }
+            }
+        });
+        return button;
+    }
+
+    private void onCircleGuidancePrimary() {
+        if (circleGuidanceStep == 1) {
+            showCircleGuidanceStep(2);
+        } else {
+            handleCircleCompletion();
+        }
+    }
+
+    private void onCircleGuidanceSecondary() {
+        if (circleGuidanceStep == 1) {
+            handleCircleAbort(null);
+        } else if (circleGuidanceStep == 2) {
+            handleCircleAbort(null);
+        } else {
+            hideCircleGuidancePanel();
+        }
+    }
+
+    private void hideCircleGuidancePanel() {
+        circleGuidanceStep = 0;
+        if (circleGuidancePanel != null) {
+            circleGuidancePanel.setVisible(false);
+        }
+        if (circleGuidanceDialog != null) {
+            circleGuidanceDialog.setVisible(false);
+        }
+        if (!circleDialogMode) {
+            rebuildFloatingButtonColumn();
+        }
+    }
+
+    private void handleCircleAbort(String message) {
+        // Return the wizard to step 2 without committing any captured points
+        hideEndDrawingButton();
+        addzhangaiwu.abortCircleDrawingAndReturn(message);
+    }
+
+    private void handleCircleCompletion() {
+        hideCircleGuidancePanel();
+        if (endDrawingCallback != null) {
+            endDrawingCallback.run();
+        } else {
+            addzhangaiwu.finishDrawingSession();
+        }
+    }
+
+    public void hideEndDrawingButton() {
+        hideCircleGuidancePanel();
+        hideFloatingDrawingControls();
+        circleDialogMode = false;
         applyDrawingPauseState(false, false);
         endDrawingCallback = null;
         visualizationPanel.revalidate();
@@ -738,6 +1048,59 @@
         }
     }
 
+    private void refreshMapForSelectedArea() {
+        if (mapRenderer == null) {
+            return;
+        }
+
+        String currentLandNumber = Dikuaiguanli.getCurrentWorkLandNumber();
+        if (isMeaningfulValue(currentLandNumber)) {
+            Dikuai current = Dikuai.getDikuai(currentLandNumber);
+            String landName = current != null ? current.getLandName() : null;
+            Dikuaiguanli.setCurrentWorkLand(currentLandNumber, landName);
+            return;
+        }
+
+        String labelName = areaNameLabel != null ? areaNameLabel.getText() : null;
+        if (!isMeaningfulAreaName(labelName)) {
+            Dikuaiguanli.setCurrentWorkLand(null, null);
+            return;
+        }
+
+        Map<String, Dikuai> all = Dikuai.getAllDikuai();
+        for (Dikuai dikuai : all.values()) {
+            if (dikuai == null) {
+                continue;
+            }
+            String candidateName = dikuai.getLandName();
+            if (candidateName != null && candidateName.trim().equals(labelName.trim())) {
+                Dikuaiguanli.setCurrentWorkLand(dikuai.getLandNumber(), candidateName);
+                return;
+            }
+        }
+
+        Dikuaiguanli.setCurrentWorkLand(null, null);
+    }
+
+    private boolean isMeaningfulValue(String value) {
+        if (value == null) {
+            return false;
+        }
+        String trimmed = value.trim();
+        return !trimmed.isEmpty() && !"-1".equals(trimmed);
+    }
+
+    private boolean isMeaningfulAreaName(String value) {
+        if (value == null) {
+            return false;
+        }
+        String trimmed = value.trim();
+        if (trimmed.isEmpty()) {
+            return false;
+        }
+        return !"鏈�夋嫨鍦板潡".equals(trimmed);
+    }
+
     // 娴嬭瘯鏂规硶
     public static void main(String[] args) {
         JFrame frame = new JFrame("AutoMow - 棣栭〉");

--
Gitblit v1.10.0