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

---
 src/zhuye/celiangmoshi.java            |   83 +++++
 src/dikuai/ObstacleManagementPage.java |   16 
 dikuai.properties                      |    4 
 src/bianjie/ThreePointCircle.java      |   78 ++++
 src/dikuai/addzhangaiwu.java           |  270 +++++++++++++++-
 src/zhuye/MapRenderer.java             |  185 +++++++++++
 set.properties                         |    9 
 src/zhuye/Shouye.java                  |   39 ++
 src/zhuye/Coordinate.java              |   19 +
 Obstacledge.properties                 |    8 
 src/dikuai/Dikuaiguanli.java           |   28 +
 src/set/Sets.java                      |  151 +++++++++
 src/set/Setsys.java                    |   15 
 src/zhangaiwu/yulanzhangaiwu.java      |   11 
 src/bianjie/BoundaryLengthDrawer.java  |    1 
 15 files changed, 861 insertions(+), 56 deletions(-)

diff --git a/Obstacledge.properties b/Obstacledge.properties
index ba3a24f..cf3e2ba 100644
--- a/Obstacledge.properties
+++ b/Obstacledge.properties
@@ -1,5 +1,5 @@
 # 鍓茶崏鏈哄湴鍧楅殰纰嶇墿閰嶇疆鏂囦欢
-# 鐢熸垚鏃堕棿锛�2025-12-17T16:39:03.715543100
+# 鐢熸垚鏃堕棿锛�2025-12-17T19:15:02.630670200
 # 鍧愭爣绯伙細WGS84锛堝害鍒嗘牸寮忥級
 
 # ============ 鍦板潡鍩哄噯绔欓厤缃� ============
@@ -13,7 +13,7 @@
 # 鏍煎紡锛歱lot.[鍦板潡缂栧彿].obstacle.[闅滅鐗╁悕绉癩.xyCoords=[鍧愭爣涓瞉
 
 # --- 鍦板潡LAND1鐨勯殰纰嶇墿 ---
-plot.LAND1.obstacle.姘寸數璐笹V.shape=1
-plot.LAND1.obstacle.姘寸數璐笹V.originalCoords=3949.899067,N;11616.760758,E;3949.899164,N;11616.760704,E;3949.899295,N;11616.760673,E;3949.899424,N;11616.760648,E;3949.899535,N;11616.760607,E;3949.899673,N;11616.760584,E;3949.899800,N;11616.760543,E;3949.899910,N;11616.760515,E;3949.900044,N;11616.760481,E;3949.900174,N;11616.760435,E;3949.900305,N;11616.760412,E;3949.900433,N;11616.760361,E;3949.900555,N;11616.760306,E;3949.900651,N;11616.760245,E;3949.900732,N;11616.760161,E;3949.900806,N;11616.760057,E;3949.900889,N;11616.759945,E;3949.900967,N;11616.759856,E;3949.901066,N;11616.759724,E;3949.901142,N;11616.759616,E;3949.901243,N;11616.759498,E;3949.901321,N;11616.759402,E;3949.901443,N;11616.759329,E;3949.901554,N;11616.759243,E;3949.901652,N;11616.759203,E;3949.901783,N;11616.759170,E;3949.901921,N;11616.759142,E;3949.902031,N;11616.759109,E;3949.902162,N;11616.759081,E;3949.902289,N;11616.759050,E;3949.902394,N;11616.759018,E;3949.902532,N;11616.758989,E;3949.902654,N;11616.758975,E;3949.902768,N;11616.758947,E;3949.902759,N;11616.758921,E;3949.902765,N;11616.758830,E;3949.902775,N;11616.758725,E;3949.902781,N;11616.758571,E;3949.902764,N;11616.758404,E;3949.902711,N;11616.758280,E;3949.902679,N;11616.758163,E;3949.902639,N;11616.758002,E;3949.902610,N;11616.757869,E;3949.902580,N;11616.757678,E;3949.902542,N;11616.757505,E;3949.902533,N;11616.757411,E;3949.902502,N;11616.757243,E;3949.902473,N;11616.757063,E;3949.902460,N;11616.756947,E;3949.902438,N;11616.756821,E;3949.902430,N;11616.756804,E;3949.902408,N;11616.756828,E;3949.902392,N;11616.756884,E;3949.902388,N;11616.756920,E;3949.902389,N;11616.756920,E;3949.902387,N;11616.756920,E;3949.902388,N;11616.756919,E;3949.902388,N;11616.756922,E;3949.902388,N;11616.756922,E;3949.902388,N;11616.756922,E;3949.902389,N;11616.756923,E;3949.902389,N;11616.756920,E;3949.901649,N;11616.757013,E;3949.901650,N;11616.757016,E;3949.901650,N;11616.757014,E;3949.901650,N;11616.757013,E;3949.901650,N;11616.757014,E;3949.901650,N;11616.757013,E;3949.901651,N;11616.757015,E;3949.901649,N;11616.757012,E;3949.901650,N;11616.757012,E;3949.901650,N;11616.757012,E;3949.901651,N;11616.757011,E;3949.901652,N;11616.757010,E
-plot.LAND1.obstacle.姘寸數璐笹V.xyCoords=5.46,-6.16;2.88,0.70;-0.16,0.08;0.13,-1.37;1.46,-2.56;2.79,-3.76;4.13,-4.96;5.46,-6.16
+plot.LAND1.obstacle.1234.shape=0
+plot.LAND1.obstacle.1234.originalCoords=3949.902533,N;11616.757411,E;3949.902502,N;11616.757243,E;3949.902473,N;11616.757063,E;3949.902460,N;11616.756947,E;3949.902438,N;11616.756821,E;3949.902430,N;11616.756804,E;3949.902408,N;11616.756828,E;3949.902392,N;11616.756884,E;3949.902388,N;11616.756920,E;3949.902389,N;11616.756920,E;3949.902387,N;11616.756920,E;3949.902388,N;11616.756919,E;3949.902388,N;11616.756922,E;3949.902388,N;11616.756922,E;3949.902388,N;11616.756922,E;3949.902389,N;11616.756923,E;3949.902389,N;11616.756920,E;3949.901649,N;11616.757013,E;3949.901650,N;11616.757016,E;3949.901650,N;11616.757014,E;3949.901650,N;11616.757013,E
+plot.LAND1.obstacle.1234.xyCoords=0.43,-0.56;1.30,-0.56
 
diff --git a/dikuai.properties b/dikuai.properties
index 83b0ec9..35bdcf1 100644
--- a/dikuai.properties
+++ b/dikuai.properties
@@ -1,5 +1,5 @@
 #Dikuai Properties
-#Wed Dec 17 16:39:03 CST 2025
+#Wed Dec 17 19:15:02 CST 2025
 LAND1.angleThreshold=-1
 LAND1.baseStationCoordinates=3949.90238860,N,11616.75692000,E
 LAND1.boundaryCoordinates=77.19,-32.68;80.71,-54.97;80.99,-55.90;83.54,-56.46;85.04,-55.55;85.94,-53.74;83.24,-35.82;84.55,-34.54;94.02,-31.92;94.10,-31.11;90.88,-20.39;90.35,-19.53;88.33,-19.00;84.12,-19.47;78.92,-22.36;76.63,-25.55;76.93,-29.84;77.06,-31.26;77.19,-32.68
@@ -15,5 +15,5 @@
 LAND1.mowingWidth=40
 LAND1.plannedPath=77.45,-31.44;81.28,-55.71;81.70,-55.80;77.91,-31.78;78.05,-31.91;78.17,-31.98;78.35,-32.01;82.12,-55.89;82.54,-55.98;78.77,-32.09;78.95,-32.12;79.17,-32.09;82.96,-56.08;83.38,-56.17;79.57,-32.03;79.96,-31.95;83.76,-56.03;84.13,-55.81;80.35,-31.86;80.50,-31.80;80.72,-31.63;84.50,-55.58;84.87,-55.33;81.08,-31.34;81.41,-30.87;85.18,-54.72;85.48,-54.10;81.75,-30.45;81.89,-30.27;81.94,-30.19;82.05,-29.82;83.00,-35.83;83.34,-35.38;81.43,-23.31;81.47,-22.18;81.43,-22.04;81.32,-21.94;81.21,-21.92;81.13,-21.42;81.50,-21.21;83.69,-35.03;84.04,-34.69;81.88,-21.00;82.25,-20.80;84.39,-34.35;84.77,-34.22;82.62,-20.59;82.99,-20.38;85.16,-34.11;85.55,-34.00;83.37,-20.18;83.74,-19.97;85.94,-33.90;86.32,-33.79;84.11,-19.76;84.50,-19.68;86.71,-33.68;87.10,-33.57;84.90,-19.63;85.30,-19.59;87.49,-33.47;87.88,-33.36;85.70,-19.55;86.09,-19.50;88.26,-33.25;88.65,-33.15;86.49,-19.46;86.89,-19.41;89.04,-33.04;89.43,-32.93;87.29,-19.37;87.69,-19.32;89.82,-32.82;90.20,-32.72;88.08,-19.28;88.49,-19.30;90.59,-32.61;90.98,-32.50;88.91,-19.41;89.34,-19.52;91.37,-32.39;91.76,-32.29;89.76,-19.63;90.18,-19.74;92.14,-32.18;92.53,-32.07;90.76,-20.88;91.62,-23.72;92.92,-31.96;93.31,-31.86;92.47,-26.56;93.33,-29.40;93.70,-31.75;81.40,-22.00;81.27,-21.93;81.21,-21.92;81.08,-21.96;80.98,-22.05;79.85,-23.55;79.64,-22.24;80.01,-22.04;80.22,-23.37;80.58,-23.05;80.39,-21.83;80.76,-21.62;80.87,-22.34;77.96,-24.59;77.63,-24.92;77.59,-24.64;77.92,-24.18;77.99,-24.57;78.36,-24.38;78.25,-23.72;78.59,-23.25;78.73,-24.19;79.11,-23.99;78.92,-22.79;79.27,-22.45;79.48,-23.76;77.99,-24.57;77.93,-24.62;77.51,-25.05;77.47,-25.11;77.20,-25.66;77.01,-26.12;76.93,-25.57;77.26,-25.10;77.31,-25.43
 LAND1.returnPointCoordinates=-1
-LAND1.updateTime=2025-12-17 16\:39\:03
+LAND1.updateTime=2025-12-17 19\:15\:02
 LAND1.userId=-1
diff --git a/set.properties b/set.properties
index d6499ab..1e8efa5 100644
--- a/set.properties
+++ b/set.properties
@@ -1,5 +1,5 @@
 #Mower Configuration Properties - Updated
-#Wed Dec 17 17:45:29 CST 2025
+#Wed Dec 17 19:35:27 CST 2025
 appVersion=-1
 boundaryLengthVisible=false
 currentWorkLandNumber=LAND1
@@ -7,11 +7,12 @@
 firmwareVersion=-1
 handheldMarkerId=
 idleTrailDurationSeconds=60
-mapScale=6.72
+mapScale=28.94
+measurementModeEnabled=false
 mowerId=1872
 serialAutoConnect=true
 serialBaudRate=115200
 serialPortName=COM15
 simCardNumber=-1
-viewCenterX=-124.59
-viewCenterY=52.20
+viewCenterX=-3.17
+viewCenterY=0.87
diff --git a/src/bianjie/BoundaryLengthDrawer.java b/src/bianjie/BoundaryLengthDrawer.java
index 9aa308b..125209e 100644
--- a/src/bianjie/BoundaryLengthDrawer.java
+++ b/src/bianjie/BoundaryLengthDrawer.java
@@ -133,3 +133,4 @@
         return Math.hypot(dx, dy) <= threshold;
     }
 }
+
diff --git a/src/bianjie/ThreePointCircle.java b/src/bianjie/ThreePointCircle.java
new file mode 100644
index 0000000..991fb3d
--- /dev/null
+++ b/src/bianjie/ThreePointCircle.java
@@ -0,0 +1,78 @@
+package bianjie;
+
+import java.text.DecimalFormat;
+
+public class ThreePointCircle {
+
+    /**
+     * 鏍规嵁鍦嗕笂涓変釜鐐硅绠楀渾蹇冨拰鍗婂緞
+     *
+     * @param point1 绗竴涓偣鍧愭爣锛屾牸寮� "x,y"
+     * @param point2 绗簩涓偣鍧愭爣锛屾牸寮� "x,y"
+     * @param point3 绗笁涓偣鍧愭爣锛屾牸寮� "x,y"
+     * @return 鍖呭惈鍦嗗績鍜屽崐寰勭殑瀛楃涓诧紝渚嬪 "鍦嗗績: 10.00,10.00; 鍗婂緞: 5.00"
+     */
+    public static String getCircleFromPoints(String point1, String point2, String point3) {
+        try {
+            // 1. 瑙f瀽鍧愭爣鐐�
+            double[] p1 = parsePoint(point1);
+            double[] p2 = parsePoint(point2);
+            double[] p3 = parsePoint(point3);
+
+            double x1 = p1[0], y1 = p1[1];
+            double x2 = p2[0], y2 = p2[1];
+            double x3 = p3[0], y3 = p3[1];
+
+            // 2. 璁$畻鍒嗘瘝 D
+            // 濡傛灉 D 涓� 0锛岃鏄庝笁鐐瑰叡绾匡紝鏃犳硶鏋勬垚鍦�
+            double D = 2 * (x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2));
+
+            if (Math.abs(D) < 1e-9) {
+                return "閿欒: 杈撳叆鐨勪笁涓偣鍏辩嚎锛屾棤娉曠‘瀹氫竴涓渾銆�";
+            }
+
+            // 3. 璁$畻鍦嗗績鍧愭爣 (centerX, centerY)
+            // 鍏紡锛�
+            // Ux = x^2 + y^2
+            // centerX = (Ux1*(y2-y3) + Ux2*(y3-y1) + Ux3*(y1-y2)) / D
+            // centerY = (Ux1*(x3-x2) + Ux2*(x1-x3) + Ux3*(x2-x1)) / D
+            
+            double sumSq1 = x1 * x1 + y1 * y1;
+            double sumSq2 = x2 * x2 + y2 * y2;
+            double sumSq3 = x3 * x3 + y3 * y3;
+
+            double centerX = (sumSq1 * (y2 - y3) + sumSq2 * (y3 - y1) + sumSq3 * (y1 - y2)) / D;
+            double centerY = (sumSq1 * (x3 - x2) + sumSq2 * (x1 - x3) + sumSq3 * (x2 - x1)) / D;
+
+            // 4. 璁$畻鍗婂緞 (鍦嗗績鍒颁换鎰忎竴鐐圭殑璺濈)
+            double radius = Math.sqrt(Math.pow(centerX - x1, 2) + Math.pow(centerY - y1, 2));
+
+            // 5. 鏍煎紡鍖栬緭鍑� (淇濈暀涓や綅灏忔暟锛屽彲鏍规嵁闇�姹傝皟鏁�)
+            DecimalFormat df = new DecimalFormat("#.##");
+            
+            return String.format("鍦嗗績: %s,%s; 鍗婂緞: %s", 
+                    df.format(centerX), 
+                    df.format(centerY), 
+                    df.format(radius));
+
+        } catch (NumberFormatException e) {
+            return "閿欒: 鍧愭爣鏍煎紡涓嶆纭紝璇蜂娇鐢� 'x,y' 鏍煎紡 (渚嬪: '1.5,2.5')";
+        } catch (Exception e) {
+            return "閿欒: " + e.getMessage();
+        }
+    }
+
+    /**
+     * 杈呭姪鏂规硶锛氬皢瀛楃涓� "x,y" 瑙f瀽涓� double 鏁扮粍 [x, y]
+     */
+    private static double[] parsePoint(String pointStr) {
+        String[] parts = pointStr.split(",");
+        if (parts.length != 2) {
+            throw new IllegalArgumentException("鍧愭爣鏍煎紡鏃犳晥: " + pointStr);
+        }
+        double x = Double.parseDouble(parts[0].trim());
+        double y = Double.parseDouble(parts[1].trim());
+        return new double[]{x, y};
+    }
+    
+}
\ No newline at end of file
diff --git a/src/dikuai/Dikuaiguanli.java b/src/dikuai/Dikuaiguanli.java
index 5adb9c5..c84eecb 100644
--- a/src/dikuai/Dikuaiguanli.java
+++ b/src/dikuai/Dikuaiguanli.java
@@ -213,8 +213,27 @@
 
 		headerPanel.add(nameLabel, BorderLayout.WEST);
 
+		// 鍙充晶鍖哄煙锛氱姸鎬佹枃瀛� + 鎸夐挳
+		JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0));
+		rightPanel.setBackground(CARD_BACKGROUND);
+		rightPanel.setOpaque(false);
+		
+		// 鐘舵�佹枃瀛楁爣绛撅紙鏍规嵁鏄惁閫変腑鏄剧ず/闅愯棌锛�
+		JLabel statusLabel = new JLabel("宸茶缃负褰撳墠鍦板潡");
+		statusLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
+		statusLabel.setForeground(PRIMARY_COLOR);
+		boolean isCurrent = dikuai.getLandNumber() != null && dikuai.getLandNumber().equals(currentWorkLandNumber);
+		statusLabel.setVisible(isCurrent);
+		
 		JButton workToggleBtn = createWorkToggleButton(dikuai);
-		headerPanel.add(workToggleBtn, BorderLayout.EAST);
+		
+		// 灏嗙姸鎬佹爣绛惧拰鎸夐挳鍏宠仈锛屼互渚垮湪鎸夐挳鐘舵�佸彉鍖栨椂鏇存柊鏍囩
+		workToggleBtn.putClientProperty("statusLabel", statusLabel);
+		
+		rightPanel.add(statusLabel);
+		rightPanel.add(workToggleBtn);
+		
+		headerPanel.add(rightPanel, BorderLayout.EAST);
 		
 		card.add(headerPanel, BorderLayout.NORTH);
 		
@@ -1440,6 +1459,13 @@
 			button.setText(isCurrent ? "褰撳墠鍦板潡" : "璁句负褰撳墠");
 		}
 		button.setToolTipText(isCurrent ? "鍙栨秷褰撳墠浣滀笟鍦板潡" : "璁句负褰撳墠浣滀笟鍦板潡");
+		
+		// 鏇存柊鐘舵�佹枃瀛楁爣绛剧殑鏄剧ず/闅愯棌
+		Object statusLabelObj = button.getClientProperty("statusLabel");
+		if (statusLabelObj instanceof JLabel) {
+			JLabel statusLabel = (JLabel) statusLabelObj;
+			statusLabel.setVisible(isCurrent);
+		}
 	}
 
 	private void ensureWorkIconsLoaded() {
diff --git a/src/dikuai/ObstacleManagementPage.java b/src/dikuai/ObstacleManagementPage.java
index 963b0c7..9dc7c5d 100644
--- a/src/dikuai/ObstacleManagementPage.java
+++ b/src/dikuai/ObstacleManagementPage.java
@@ -309,15 +309,21 @@
         actionPanel.setLayout(new BoxLayout(actionPanel, BoxLayout.X_AXIS));
         actionPanel.setOpaque(false);
         
-        JButton generateBtn = createStyledButton("閲嶆柊鐢熸垚鍧愭爣", PRIMARY_COLOR, true);
-        generateBtn.addActionListener(e -> generateObstacleCoordinates(obstacle, xyArea));
+        // 瀵逛簬鍦嗗舰闅滅鐗╋紝涓嶆樉绀�"閲嶆柊鐢熸垚鍧愭爣"鎸夐挳锛屽彧鏄剧ず棰勮鎸夐挳
+        Obstacledge.ObstacleShape shape = obstacle.getShape();
+        boolean isCircle = (shape == Obstacledge.ObstacleShape.CIRCLE);
+        
+        if (!isCircle) {
+            // 鍙湁闈炲渾褰㈤殰纰嶇墿鎵嶆樉绀�"閲嶆柊鐢熸垚鍧愭爣"鎸夐挳
+            JButton generateBtn = createStyledButton("閲嶆柊鐢熸垚鍧愭爣", PRIMARY_COLOR, true);
+            generateBtn.addActionListener(e -> generateObstacleCoordinates(obstacle, xyArea));
+            actionPanel.add(generateBtn);
+            actionPanel.add(Box.createHorizontalStrut(10));
+        }
         
         JButton previewBtn = createStyledButton("棰勮", TEXT_SECONDARY, false);
         previewBtn.setPreferredSize(new Dimension(70, 36)); // 绋嶅井绐勪竴鐐�
         previewBtn.addActionListener(e -> previewObstacle(obstacle));
-        
-        actionPanel.add(generateBtn);
-        actionPanel.add(Box.createHorizontalStrut(10));
         actionPanel.add(previewBtn);
         
         card.add(actionPanel);
diff --git a/src/dikuai/addzhangaiwu.java b/src/dikuai/addzhangaiwu.java
index 098de63..2952d93 100644
--- a/src/dikuai/addzhangaiwu.java
+++ b/src/dikuai/addzhangaiwu.java
@@ -56,6 +56,7 @@
 import zhangaiwu.yulanzhangaiwu;
 import zhuye.buttonset;
 import bianjie.bianjieguihua2;
+import bianjie.ThreePointCircle;
 
 /**
  * 闅滅鐗╂柊澧�/缂栬緫瀵硅瘽妗嗐�傝璁¤瑷�鍙傝�� {@link AddDikuai}锛屾敮鎸侀�氳繃瀹炲湴缁樺埗閲囬泦闅滅鐗╁潗鏍囥��
@@ -921,6 +922,12 @@
             boundaryStatusLabel.setVisible(false);
             boundaryStatusLabel.setText("");
         }
+        // 瀵逛簬鍦嗗舰闅滅鐗╋紝闅愯棌鐢熸垚杈圭晫鎸夐挳
+        String shapeKey = formData.get("obstacleShape");
+        boolean isCircle = "circle".equals(shapeKey);
+        if (isCircle && generateBoundaryButton != null) {
+            generateBoundaryButton.setVisible(false);
+        }
 
         String method = formData.get("drawingMethod");
         if (!isMeaningfulValue(method)) {
@@ -1095,14 +1102,11 @@
                 session.captureMessage = "鏃犳硶鏍规嵁閲囬泦鐨勭偣鐢熸垚鍦嗭紝璇风‘淇濋�夋嫨浜嗕笁涓潪鍏辩嚎鐨勫渾鍛ㄧ偣";
                 return;
             }
-            double[] radiusPoint = pickRadiusPoint(xyPoints, circle);
-            if (radiusPoint == null) {
-                session.captureSuccessful = false;
-                session.captureMessage = "閲囬泦鐨勫渾鍛ㄧ偣寮傚父锛屾棤娉曠敓鎴愬渾";
-                return;
-            }
+            // 浣跨敤璁$畻鍑虹殑鍗婂緞鐢熸垚涓�涓湡姝g殑鍦嗕笂鐐癸紙鍦嗗績鍙充晶鐨勭偣锛夛紝纭繚棰勮鏃惰绠楃殑鍗婂緞鍜岀粨鏉熺粯鍒舵椂鐨勫崐寰勪竴鑷�
+            double radiusX = circle.centerX + circle.radius;
+            double radiusY = circle.centerY;
             String result = String.format(Locale.US, "%.2f,%.2f;%.2f,%.2f",
-                    circle.centerX, circle.centerY, radiusPoint[0], radiusPoint[1]);
+                    circle.centerX, circle.centerY, radiusX, radiusY);
             session.data.put("obstacleCoordinates", result);
             session.captureSuccessful = true;
             session.captureMessage = "宸查噰闆嗗渾褰㈤殰纰嶇墿锛屽叡 " + xyPoints.size() + " 涓偣";
@@ -1436,16 +1440,26 @@
             return;
         }
         String coords = formData.get("obstacleCoordinates");
+        String shapeKey = formData.get("obstacleShape");
+        boolean isCircle = "circle".equals(shapeKey);
+        
         if (isMeaningfulValue(coords)) {
-            int count = countCoordinatePairs(coords);
-            drawingStatusLabel.setText("宸查噰闆嗛殰纰嶇墿鏁版嵁锛岀偣鏁帮細" + count);
+            if (isCircle) {
+                // 瀵逛簬鍦嗗舰闅滅鐗╋紝鏄剧ず鍦嗗績鍧愭爣鍜屽崐寰�
+                String statusText = parseCircleStatusText(coords);
+                drawingStatusLabel.setText(statusText);
+            } else {
+                // 瀵逛簬澶氳竟褰㈤殰纰嶇墿锛屾樉绀虹偣鏁�
+                int count = countCoordinatePairs(coords);
+                drawingStatusLabel.setText("宸查噰闆嗛殰纰嶇墿鏁版嵁锛岀偣鏁帮細" + count);
+            }
             if (!drawingInProgress && drawButton != null) {
                 drawButton.setText("閲嶆柊缁樺埗");
                 drawButton.setEnabled(true);
             }
-            // 缁樺埗瀹屾垚鍚庢樉绀�"鐢熸垚闅滅鐗╄竟鐣�"鎸夐挳
+            // 瀵逛簬鍦嗗舰闅滅鐗╋紝涓嶆樉绀�"鐢熸垚闅滅鐗╄竟鐣�"鎸夐挳
             if (generateBoundaryButton != null) {
-                generateBoundaryButton.setVisible(true);
+                generateBoundaryButton.setVisible(!isCircle);
             }
         } else {
             drawingStatusLabel.setText("灏氭湭閲囬泦闅滅鐗╁潗鏍�");
@@ -1493,10 +1507,20 @@
         if (previewButton == null) {
             return;
         }
+        
+        // 瀵逛簬鍦嗗舰闅滅鐗╋紝涓嶆樉绀洪瑙堟寜閽�
+        String shapeKey = formData.get("obstacleShape");
+        boolean isCircle = "circle".equals(shapeKey);
+        if (isCircle) {
+            previewButton.setVisible(false);
+            previewButton.setEnabled(false);
+            return;
+        }
+        
         // 鍙湁鍦ㄧ敓鎴愯竟鐣屽悗鎵嶆樉绀洪瑙堟寜閽�
         String generatedBoundary = formData.get("generatedBoundaryCoordinates");
         boolean hasGeneratedBoundary = isMeaningfulValue(generatedBoundary);
-        
+
         if (hasGeneratedBoundary) {
             // 鐢熸垚杈圭晫鍚庯紝閲嶆柊鍒涘缓缁胯壊鐨勯瑙堟寜閽�
             // 鑾峰彇鎸夐挳鐨勭埗瀹瑰櫒鍜屼綅缃�
@@ -1572,6 +1596,49 @@
         }
         return count;
     }
+    
+    /**
+     * 瑙f瀽鍦嗗舰闅滅鐗╁潗鏍囷紝鐢熸垚鐘舵�佹枃鏈�
+     * 鏍煎紡锛歝enterX,centerY;radiusX,radiusY
+     * 杩斿洖锛氬綋鍓嶉噰闆嗗渾褰㈠渾蹇冨潗鏍噚,y,鍗婂緞n绫�
+     */
+    private String parseCircleStatusText(String coords) {
+        if (!isMeaningfulValue(coords)) {
+            return "灏氭湭閲囬泦鍦嗗舰闅滅鐗╁潗鏍�";
+        }
+        
+        try {
+            String[] pairs = coords.split(";");
+            if (pairs.length < 2) {
+                return "鍦嗗舰闅滅鐗╁潗鏍囨牸寮忛敊璇�";
+            }
+            
+            // 瑙f瀽鍦嗗績鍧愭爣
+            String[] centerParts = pairs[0].trim().split(",");
+            if (centerParts.length < 2) {
+                return "鍦嗗舰闅滅鐗╁潗鏍囨牸寮忛敊璇�";
+            }
+            double centerX = Double.parseDouble(centerParts[0].trim());
+            double centerY = Double.parseDouble(centerParts[1].trim());
+            
+            // 瑙f瀽鍦嗕笂涓�鐐瑰潗鏍�
+            String[] radiusParts = pairs[1].trim().split(",");
+            if (radiusParts.length < 2) {
+                return "鍦嗗舰闅滅鐗╁潗鏍囨牸寮忛敊璇�";
+            }
+            double radiusX = Double.parseDouble(radiusParts[0].trim());
+            double radiusY = Double.parseDouble(radiusParts[1].trim());
+            
+            // 璁$畻鍗婂緞锛堢背锛�
+            double radius = Math.sqrt(Math.pow(radiusX - centerX, 2) + Math.pow(radiusY - centerY, 2));
+            
+            // 鏍煎紡鍖栨樉绀猴細褰撳墠閲囬泦鍦嗗舰鍦嗗績鍧愭爣x,y,鍗婂緞n绫�
+            return String.format(Locale.US, "褰撳墠閲囬泦鍦嗗舰鍦嗗績鍧愭爣%.2f,%.2f,鍗婂緞%.2f绫�", 
+                    centerX, centerY, radius);
+        } catch (Exception e) {
+            return "鍦嗗舰闅滅鐗╁潗鏍囪В鏋愬け璐�";
+        }
+    }
 
     private void updateSaveButtonState() {
         if (saveButton != null) {
@@ -1651,8 +1718,16 @@
 
         String coords = coordsValue.trim();
         String originalCoords = isMeaningfulValue(originalValue) ? originalValue.trim() : null;
-        // 濡傛灉鏈夌敓鎴愮殑杈圭晫鍧愭爣锛屼娇鐢ㄧ敓鎴愮殑杈圭晫鍧愭爣锛涘惁鍒欎娇鐢ㄥ師濮嬪潗鏍�
-        String finalCoords = isMeaningfulValue(generatedBoundaryValue) ? generatedBoundaryValue.trim() : coords;
+        // 瀵逛簬鍦嗗舰闅滅鐗╋紝鐩存帴浣跨敤obstacleCoordinates锛堝湪绗�3涓偣纭鏃跺凡鐢熸垚锛�
+        // 瀵逛簬澶氳竟褰㈤殰纰嶇墿锛屽鏋滄湁鐢熸垚鐨勮竟鐣屽潗鏍囷紝浣跨敤鐢熸垚鐨勮竟鐣屽潗鏍囷紱鍚﹀垯浣跨敤鍘熷鍧愭爣
+        String finalCoords;
+        if ("circle".equals(shapeKey)) {
+            // 鍦嗗舰闅滅鐗╃洿鎺ヤ娇鐢ㄥ凡鐢熸垚鐨勫潗鏍�
+            finalCoords = coords;
+        } else {
+            // 澶氳竟褰㈤殰纰嶇墿浼樺厛浣跨敤鐢熸垚鐨勮竟鐣屽潗鏍�
+            finalCoords = isMeaningfulValue(generatedBoundaryValue) ? generatedBoundaryValue.trim() : coords;
+        }
         String trimmedName = isMeaningfulValue(obstacleName) ? obstacleName.trim() : null;
         if (!isMeaningfulValue(trimmedName)) {
             JOptionPane.showMessageDialog(this, "闅滅鐗╁悕绉版棤鏁�", "閿欒", JOptionPane.ERROR_MESSAGE);
@@ -1694,14 +1769,128 @@
         }
         
         String shapeKey = formData.get("obstacleShape");
-        if (!"polygon".equals(shapeKey)) {
-            JOptionPane.showMessageDialog(this, "鍙湁澶氳竟褰㈤殰纰嶇墿鎵嶉渶瑕佺敓鎴愯竟鐣屽潗鏍�", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
-            return;
-        }
         
         try {
-            // 妫�鏌ョ粯鍒舵柟寮忥紝鍙湁鍓茶崏鏈虹粯鍒剁殑澶氳竟褰㈡墠璋冪敤bianjieguihua2
             String method = formData.get("drawingMethod");
+            
+            // 澶勭悊鍦嗗舰闅滅鐗�
+            if ("circle".equals(shapeKey)) {
+                if (!"mower".equals(method)) {
+                    JOptionPane.showMessageDialog(this, "鍙湁鍓茶崏鏈虹粯鍒剁殑鍦嗗舰闅滅鐗╂墠鏀寔鐢熸垚杈圭晫鍧愭爣", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+                    return;
+                }
+                
+                // 灏嗗師濮嬪潗鏍囪浆鎹负Coordinate瀵硅薄鍒楄〃
+                List<Coordinate> coordinateList = parseOriginalCoordinatesToCoordinateList(originalCoords);
+                if (coordinateList.size() < 3) {
+                    JOptionPane.showMessageDialog(this, "鍦嗗舰闅滅鐗╄嚦灏戦渶瑕佷笁涓噰闆嗙偣", "閿欒", JOptionPane.ERROR_MESSAGE);
+                    return;
+                }
+                
+                // 瑙f瀽鍩虹珯鍧愭爣
+                String[] baseParts = baseStation.split(",");
+                if (baseParts.length < 4) {
+                    JOptionPane.showMessageDialog(this, "鍩虹珯鍧愭爣鏍煎紡鏃犳晥", "閿欒", JOptionPane.ERROR_MESSAGE);
+                    return;
+                }
+                
+                double baseLat = parseDMToDecimal(baseParts[0].trim(), baseParts[1].trim());
+                double baseLon = parseDMToDecimal(baseParts[2].trim(), baseParts[3].trim());
+                
+                // 灏嗗師濮嬪潗鏍囪浆鎹负XY鍧愭爣锛堟湰鍦板潗鏍囩郴锛�
+                List<double[]> xyPoints = new ArrayList<>();
+                for (Coordinate coord : coordinateList) {
+                    double lat = parseDMToDecimal(coord.getLatitude(), coord.getLatDirection());
+                    double lon = parseDMToDecimal(coord.getLongitude(), coord.getLonDirection());
+                    xyPoints.add(convertLatLonToLocal(lat, lon, baseLat, baseLon));
+                }
+                
+                // 浣跨敤ThreePointCircle绠楁硶璁$畻鍦嗗績鍜屽崐寰�
+                if (xyPoints.size() < 3) {
+                    JOptionPane.showMessageDialog(this, "鑷冲皯闇�瑕佷笁涓偣鎵嶈兘鐢熸垚鍦嗗舰杈圭晫", "閿欒", JOptionPane.ERROR_MESSAGE);
+                    return;
+                }
+                
+                // 鍙栧墠涓変釜鐐硅绠楀渾
+                double[] p1 = xyPoints.get(0);
+                double[] p2 = xyPoints.get(1);
+                double[] p3 = xyPoints.get(2);
+                
+                String point1 = String.format(Locale.US, "%.2f,%.2f", p1[0], p1[1]);
+                String point2 = String.format(Locale.US, "%.2f,%.2f", p2[0], p2[1]);
+                String point3 = String.format(Locale.US, "%.2f,%.2f", p3[0], p3[1]);
+                
+                String circleResult = bianjie.ThreePointCircle.getCircleFromPoints(point1, point2, point3);
+                
+                // 瑙f瀽缁撴灉锛氭牸寮忎负 "鍦嗗績: x,y; 鍗婂緞: r"
+                if (circleResult == null || circleResult.startsWith("閿欒")) {
+                    JOptionPane.showMessageDialog(this, "鐢熸垚鍦嗗舰杈圭晫澶辫触: " + circleResult, "閿欒", JOptionPane.ERROR_MESSAGE);
+                    return;
+                }
+                
+                // 瑙f瀽鍦嗗績鍜屽崐寰�
+                String[] parts = circleResult.split(";");
+                if (parts.length < 2) {
+                    JOptionPane.showMessageDialog(this, "瑙f瀽鍦嗗舰杈圭晫缁撴灉澶辫触", "閿欒", JOptionPane.ERROR_MESSAGE);
+                    return;
+                }
+                
+                // 鎻愬彇鍦嗗績鍧愭爣
+                String centerPart = parts[0].trim(); // "鍦嗗績: x,y"
+                String radiusPart = parts[1].trim(); // "鍗婂緞: r"
+                
+                String centerCoords = centerPart.substring(centerPart.indexOf(":") + 1).trim();
+                String radiusStr = radiusPart.substring(radiusPart.indexOf(":") + 1).trim();
+                
+                String[] centerXY = centerCoords.split(",");
+                if (centerXY.length < 2) {
+                    JOptionPane.showMessageDialog(this, "瑙f瀽鍦嗗績鍧愭爣澶辫触", "閿欒", JOptionPane.ERROR_MESSAGE);
+                    return;
+                }
+                
+                double centerX = Double.parseDouble(centerXY[0].trim());
+                double centerY = Double.parseDouble(centerXY[1].trim());
+                double radius = Double.parseDouble(radiusStr.trim());
+                
+                // 璁$畻鍦嗕笂涓�鐐癸紙鍦嗗績鍙充晶鐨勭偣锛�
+                double radiusX = centerX + radius;
+                double radiusY = centerY;
+                
+                // 鐢熸垚杈圭晫鍧愭爣鏍煎紡锛氬渾蹇僗,鍦嗗績Y;鍦嗕笂鐐筙,鍦嗕笂鐐筜
+                String boundaryCoords = String.format(Locale.US, "%.2f,%.2f;%.2f,%.2f", 
+                    centerX, centerY, radiusX, radiusY);
+                
+                // 淇濆瓨鐢熸垚鐨勮竟鐣屽潗鏍�
+                formData.put("generatedBoundaryCoordinates", boundaryCoords);
+                
+                // 鏇存柊杈圭晫鐘舵�佹爣绛炬樉绀猴紙鍦嗗舰鍙湁2涓偣锛氬渾蹇冨拰鍦嗕笂涓�鐐癸級
+                updateBoundaryStatusLabel(2);
+                
+                // 鏇存柊棰勮鎸夐挳鏄剧ず锛堝彉鎴愮豢鑹插彲鐐瑰嚮锛�
+                updatePreviewButtonState();
+                
+                // 鏇存柊淇濆瓨鎸夐挳鐘舵�侊紙鍙樻垚鍙偣鍑伙級
+                updateSaveButtonState();
+                
+                // 寮哄埗鍒锋柊UI
+                SwingUtilities.invokeLater(() -> {
+                    if (previewButton != null) {
+                        previewButton.revalidate();
+                        previewButton.repaint();
+                    }
+                });
+                
+                JOptionPane.showMessageDialog(this, "鍦嗗舰闅滅鐗╄竟鐣屽潗鏍囧凡鐢熸垚", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+                return;
+            }
+            
+            // 澶勭悊澶氳竟褰㈤殰纰嶇墿
+            if (!"polygon".equals(shapeKey)) {
+                JOptionPane.showMessageDialog(this, "鍙湁澶氳竟褰㈡垨鍦嗗舰闅滅鐗╂墠闇�瑕佺敓鎴愯竟鐣屽潗鏍�", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+                return;
+            }
+            
+            // 妫�鏌ョ粯鍒舵柟寮忥紝鍙湁鍓茶崏鏈虹粯鍒剁殑澶氳竟褰㈡墠璋冪敤bianjieguihua2
             if (!"mower".equals(method)) {
                 JOptionPane.showMessageDialog(this, "鍙湁鍓茶崏鏈虹粯鍒剁殑澶氳竟褰㈡墠鏀寔鐢熸垚杈圭晫鍧愭爣", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
                 return;
@@ -1816,6 +2005,36 @@
         String landName = targetDikuai.getLandName();
         String boundary = targetDikuai.getBoundaryCoordinates();
         
+        // 鑾峰彇闅滅鐗╁潗鏍囷紙浼樺厛浣跨敤鐢熸垚鐨勮竟鐣屽潗鏍囷紝濡傛灉娌℃湁鍒欎娇鐢ㄥ師濮嬪潗鏍囷級
+        String obstacleCoords = generatedBoundary;
+        String shapeKey = formData.get("obstacleShape");
+        String obstacleName = formData.get("obstacleName");
+        
+        // 瀵逛簬鍦嗗舰闅滅鐗╋紝鐢熸垚鐨勮竟鐣屽潗鏍囨牸寮忓氨鏄殰纰嶇墿鍧愭爣鏍煎紡锛屽彲浠ョ洿鎺ヤ娇鐢�
+        // 瀵逛簬澶氳竟褰㈤殰纰嶇墿锛屼篃闇�瑕佷娇鐢ㄧ敓鎴愮殑杈圭晫鍧愭爣
+        // 濡傛灉鐢熸垚鐨勮竟鐣屽潗鏍囦笉鍙敤锛屽皾璇曚娇鐢ㄥ師濮嬮殰纰嶇墿鍧愭爣
+        if (!isMeaningfulValue(obstacleCoords)) {
+            obstacleCoords = formData.get("obstacleCoordinates");
+            if (!isMeaningfulValue(obstacleCoords)) {
+                JOptionPane.showMessageDialog(this, "鏃犳硶鑾峰彇闅滅鐗╁潗鏍囪繘琛岄瑙�", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+                return;
+            }
+        }
+        
+        // 鏋勫缓闅滅鐗╂暟鎹瓧绗︿覆锛屽寘鍚悕绉般�佸舰鐘跺拰鍧愭爣
+        // 鏍煎紡锛氶殰纰嶇墿鍚嶇О::褰㈢姸::鍧愭爣 鎴� 闅滅鐗╁悕绉�:褰㈢姸:鍧愭爣
+        final String obstacleData;
+        if (isMeaningfulValue(obstacleName) && isMeaningfulValue(shapeKey)) {
+            // 浣跨敤 :: 鍒嗛殧绗︽牸寮忥細鍚嶇О::褰㈢姸::鍧愭爣
+            obstacleData = obstacleName.trim() + "::" + shapeKey.trim() + "::" + obstacleCoords;
+        } else if (isMeaningfulValue(shapeKey)) {
+            // 鍙湁褰㈢姸锛氬舰鐘�::鍧愭爣
+            obstacleData = shapeKey.trim() + "::" + obstacleCoords;
+        } else {
+            // 鍙湁鍧愭爣
+            obstacleData = obstacleCoords;
+        }
+        
         // 鍏抽棴褰撳墠瀵硅瘽妗�
         setVisible(false);
         
@@ -1827,7 +2046,7 @@
                     landNumber,
                     landName,
                     boundary,
-                    generatedBoundary,  // 浣跨敤鐢熸垚鐨勮竟鐣屽潗鏍囦綔涓洪殰纰嶇墿鍧愭爣
+                    obstacleData,  // 浣跨敤鍖呭惈鍚嶇О鍜屽舰鐘剁殑闅滅鐗╂暟鎹�
                     null,
                     () -> SwingUtilities.invokeLater(() -> {
                         // 閲嶆柊鎵撳紑鏂板闅滅鐗╂楠�2椤甸潰
@@ -2056,6 +2275,17 @@
             saveButton.setVisible(true);
             updateDrawingStatus();
             updatePreviewButtonState();
+            // 瀵逛簬鍦嗗舰闅滅鐗╋紝纭繚棰勮鎸夐挳鍜岀敓鎴愯竟鐣屾寜閽殣钘�
+            String shapeKey = formData.get("obstacleShape");
+            boolean isCircle = "circle".equals(shapeKey);
+            if (isCircle) {
+                if (previewButton != null) {
+                    previewButton.setVisible(false);
+                }
+                if (generateBoundaryButton != null) {
+                    generateBoundaryButton.setVisible(false);
+                }
+            }
         }
         updateSaveButtonState();
         revalidate();
diff --git a/src/set/Sets.java b/src/set/Sets.java
index b81eac5..9a4d8c2 100644
--- a/src/set/Sets.java
+++ b/src/set/Sets.java
@@ -5,14 +5,12 @@
 import zhuye.MapRenderer;
 import zhuye.Shouye;
 import zhuye.buttonset;
-import set.Setsys;
-
+import zhuye.celiangmoshi;
 import javax.swing.*;
 import javax.swing.filechooser.FileNameExtensionFilter;
 
 import java.awt.*;
 import java.awt.event.*;
-import java.awt.geom.RoundRectangle2D;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
@@ -42,6 +40,7 @@
     private JLabel appVersionLabel;
     private JLabel idleTrailDurationLabel;
     private JLabel boundaryLengthVisibleLabel;
+    private JLabel measurementModeEnabledLabel;
     
     private JButton mowerIdEditBtn;
     private JButton baseStationIdEditBtn;
@@ -156,6 +155,10 @@
         // 鏄剧ず杈圭晫璺濈璁剧疆椤�
         JPanel boundaryLengthPanel = createBoundaryLengthPanel();
         boundaryLengthVisibleLabel = (JLabel) boundaryLengthPanel.getClientProperty("valueLabel");
+        
+        // 寮�鍚祴閲忔ā寮忚缃」
+        JPanel measurementModePanel = createMeasurementModePanel();
+        measurementModeEnabledLabel = (JLabel) measurementModePanel.getClientProperty("valueLabel");
 
         JPanel feedbackPanel = createFeedbackPanel();
         
@@ -171,6 +174,7 @@
         addSettingItem(panel, firmwarePanel, true);
         addSettingItem(panel, idleTrailPanel, true);
         addSettingItem(panel, boundaryLengthPanel, true);
+        addSettingItem(panel, measurementModePanel, true);
         addSettingItem(panel, feedbackPanel, true);
         addSettingItem(panel, appVersionPanel, false);  // 鏈�鍚庝竴椤逛笉鍔犲垎鍓茬嚎
         
@@ -384,6 +388,134 @@
     }
     
     /**
+     * 鍒涘缓寮�鍚祴閲忔ā寮忚缃潰鏉�
+     */
+    private JPanel createMeasurementModePanel() {
+        JPanel panel = new JPanel(new GridBagLayout());
+        panel.setOpaque(false);  // 閫忔槑鑳屾櫙
+        panel.setAlignmentX(Component.LEFT_ALIGNMENT);
+        panel.setMaximumSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT));
+        panel.setPreferredSize(new Dimension(Integer.MAX_VALUE, ROW_HEIGHT));
+        panel.setMinimumSize(new Dimension(0, ROW_HEIGHT));
+        panel.setBorder(BorderFactory.createEmptyBorder(ITEM_PADDING, ITEM_PADDING, ITEM_PADDING, ITEM_PADDING));
+
+        GridBagConstraints gbc = new GridBagConstraints();
+
+        JLabel titleLabel = new JLabel("寮�鍚祴閲忔ā寮�");
+        titleLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
+        titleLabel.setForeground(Color.BLACK);
+        titleLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+        gbc.gridx = 0;
+        gbc.gridy = 0;
+        gbc.weightx = 0;
+        gbc.anchor = GridBagConstraints.EAST;
+        gbc.insets = new Insets(0, 0, 0, 12);
+        panel.add(titleLabel, gbc);
+
+        measurementModeEnabledLabel = new JLabel(setData.isMeasurementModeEnabled() ? "宸插紑鍚�" : "宸插叧闂�");
+        measurementModeEnabledLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+        measurementModeEnabledLabel.setForeground(Color.DARK_GRAY);
+        gbc = new GridBagConstraints();
+        gbc.gridx = 1;
+        gbc.gridy = 0;
+        gbc.weightx = 1.0;
+        gbc.anchor = GridBagConstraints.EAST;
+        panel.add(measurementModeEnabledLabel, gbc);
+
+        panel.putClientProperty("valueLabel", measurementModeEnabledLabel);
+
+        // 鍒涘缓鍒囨崲鎸夐挳锛堜娇鐢ㄥ浘鏍囷級
+        JButton toggleBtn = createMeasurementModeToggleButton();
+        gbc = new GridBagConstraints();
+        gbc.gridx = 2;
+        gbc.gridy = 0;
+        gbc.weightx = 0;
+        gbc.anchor = GridBagConstraints.EAST;
+        panel.add(toggleBtn, gbc);
+        panel.putClientProperty("toggleButton", toggleBtn);
+
+        return panel;
+    }
+    
+    /**
+     * 鍒涘缓娴嬮噺妯″紡鍒囨崲鎸夐挳
+     */
+    private JButton createMeasurementModeToggleButton() {
+        JButton button = new JButton();
+        button.setContentAreaFilled(false);
+        button.setBorder(null);
+        button.setFocusPainted(false);
+        button.setCursor(new Cursor(Cursor.HAND_CURSOR));
+        button.setPreferredSize(new Dimension(32, 32));
+        button.setMinimumSize(new Dimension(32, 32));
+        button.setMaximumSize(new Dimension(32, 32));
+        
+        updateMeasurementModeToggleButton(button);
+        
+        button.addActionListener(e -> toggleMeasurementMode(button));
+        
+        return button;
+    }
+    
+    /**
+     * 鏇存柊娴嬮噺妯″紡鍒囨崲鎸夐挳鍥炬爣
+     */
+    private void updateMeasurementModeToggleButton(JButton button) {
+        boolean isEnabled = setData.isMeasurementModeEnabled();
+        try {
+            String iconPath = isEnabled ? "image/open.png" : "image/close.png";
+            ImageIcon icon = new ImageIcon(iconPath);
+            if (icon.getIconWidth() > 0) {
+                Image scaledImage = icon.getImage().getScaledInstance(32, 32, Image.SCALE_SMOOTH);
+                button.setIcon(new ImageIcon(scaledImage));
+                button.setText(null);
+            } else {
+                button.setIcon(null);
+                button.setText(isEnabled ? "寮�" : "鍏�");
+            }
+        } catch (Exception e) {
+            button.setIcon(null);
+            button.setText(isEnabled ? "寮�" : "鍏�");
+            System.err.println("鏃犳硶鍔犺浇娴嬮噺妯″紡鍥炬爣: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 鍒囨崲娴嬮噺妯″紡鐘舵��
+     */
+    private void toggleMeasurementMode(JButton button) {
+        boolean newValue = !setData.isMeasurementModeEnabled();
+        setData.setMeasurementModeEnabled(newValue);
+        
+        // 淇濆瓨鍒伴厤缃枃浠�
+        setData.updateProperty("measurementModeEnabled", String.valueOf(newValue));
+        
+        // 鏇存柊UI
+        if (measurementModeEnabledLabel != null) {
+            measurementModeEnabledLabel.setText(newValue ? "宸插紑鍚�" : "宸插叧闂�");
+        }
+        updateMeasurementModeToggleButton(button);
+        
+        // 閫氱煡MapRenderer鏇存柊
+        Shouye shouye = Shouye.getInstance();
+        if (shouye != null) {
+            MapRenderer renderer = shouye.getMapRenderer();
+            if (renderer != null) {
+                renderer.setMeasurementMode(newValue);
+            }
+            if (newValue) {
+                celiangmoshi.start();
+            } else {
+                celiangmoshi.stop();
+            }
+            // 鍒锋柊鍦板浘鏄剧ず锛堥�氳繃MapRenderer瑙﹀彂閲嶇粯锛�
+            if (renderer != null) {
+                renderer.repaint();
+            }
+        }
+    }
+    
+    /**
      * 鍒涘缓杈圭晫璺濈鏄剧ず鍒囨崲鎸夐挳
      */
     private JButton createBoundaryLengthToggleButton() {
@@ -580,6 +712,19 @@
             }
         }
         
+        // 鏇存柊娴嬮噺妯″紡鐘舵��
+        if (measurementModeEnabledLabel != null) {
+            measurementModeEnabledLabel.setText(setData.isMeasurementModeEnabled() ? "宸插紑鍚�" : "宸插叧闂�");
+        }
+        // 鏇存柊娴嬮噺妯″紡鍒囨崲鎸夐挳鍥炬爣
+        JPanel measurementModePanel = (JPanel) measurementModeEnabledLabel.getParent();
+        if (measurementModePanel != null) {
+            JButton toggleBtn = (JButton) measurementModePanel.getClientProperty("toggleButton");
+            if (toggleBtn != null) {
+                updateMeasurementModeToggleButton(toggleBtn);
+            }
+        }
+        
         // 鏇存柊APP鐗堟湰鏄剧ず
         if (appVersionLabel != null) {
             appVersionLabel.setText(setData.getAppVersion() != null ? 
diff --git a/src/set/Setsys.java b/src/set/Setsys.java
index 153ef58..a3d9262 100644
--- a/src/set/Setsys.java
+++ b/src/set/Setsys.java
@@ -14,6 +14,7 @@
     private String appVersion;
     private int idleTrailDurationSeconds = DEFAULT_IDLE_TRAIL_DURATION_SECONDS;
     private boolean boundaryLengthVisible = false;  // 榛樿鍏抽棴鏄剧ず杈圭晫璺濈
+    private boolean measurementModeEnabled = false;  // 榛樿鍏抽棴娴嬮噺妯″紡
     
     private static final String PROPERTIES_FILE = "set.properties";
 
@@ -95,6 +96,14 @@
     public void setBoundaryLengthVisible(boolean visible) {
         this.boundaryLengthVisible = visible;
     }
+    
+    public boolean isMeasurementModeEnabled() {
+        return measurementModeEnabled;
+    }
+    
+    public void setMeasurementModeEnabled(boolean enabled) {
+        this.measurementModeEnabled = enabled;
+    }
 
     /**
      * 鍒濆鍖栨柟娉� - 浠巔roperties鏂囦欢璇诲彇鏁版嵁
@@ -115,6 +124,8 @@
             this.idleTrailDurationSeconds = sanitizeIdleTrailDuration(props.getProperty("idleTrailDurationSeconds"));
             String boundaryLengthVisibleStr = props.getProperty("boundaryLengthVisible");
             this.boundaryLengthVisible = "true".equalsIgnoreCase(boundaryLengthVisibleStr);
+            String measurementModeEnabledStr = props.getProperty("measurementModeEnabled");
+            this.measurementModeEnabled = "true".equalsIgnoreCase(measurementModeEnabledStr);
                         
         } catch (FileNotFoundException e) {           
             // 鏂囦欢涓嶅瓨鍦ㄦ椂锛岃缃墍鏈夊睘鎬т负null
@@ -159,6 +170,9 @@
             case "boundaryLengthVisible":
                 this.boundaryLengthVisible = "true".equalsIgnoreCase(value);
                 break;
+            case "measurementModeEnabled":
+                this.measurementModeEnabled = "true".equalsIgnoreCase(value);
+                break;
             case "mapScale":
                 // mapScale涓嶉渶瑕佸湪鍐呭瓨涓瓨鍌紝鐩存帴鏇存柊鍒版枃浠�
                 break;
@@ -228,6 +242,7 @@
         this.appVersion = null;
         this.idleTrailDurationSeconds = DEFAULT_IDLE_TRAIL_DURATION_SECONDS;
         this.boundaryLengthVisible = false;  // 榛樿鍏抽棴
+        this.measurementModeEnabled = false;  // 榛樿鍏抽棴娴嬮噺妯″紡
     }
 
     /**
diff --git a/src/zhangaiwu/yulanzhangaiwu.java b/src/zhangaiwu/yulanzhangaiwu.java
index c79ee83..f9cacf8 100644
--- a/src/zhangaiwu/yulanzhangaiwu.java
+++ b/src/zhangaiwu/yulanzhangaiwu.java
@@ -65,6 +65,11 @@
             return;
         }
 
+        // 鍘绘帀鍦嗗舰闅滅鐗╃殑瀹炴椂棰勮鍔熻兘
+        if ("circle".equals(snapshot.shape)) {
+            return;
+        }
+
         Color originalColor = g2d.getColor();
         Stroke originalStroke = g2d.getStroke();
         try {
@@ -78,11 +83,7 @@
             );
             Stroke solidStroke = new BasicStroke((float) (1.4f / scale), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
 
-            if ("circle".equals(snapshot.shape) && snapshot.circle != null) {
-                drawCirclePreview(g2d, snapshot, dashedStroke, scale);
-            } else {
-                drawPolygonPreview(g2d, snapshot, dashedStroke, solidStroke, scale);
-            }
+            drawPolygonPreview(g2d, snapshot, dashedStroke, solidStroke, scale);
         } finally {
             g2d.setColor(originalColor);
             g2d.setStroke(originalStroke);
diff --git a/src/zhuye/Coordinate.java b/src/zhuye/Coordinate.java
index d15579f..af2c5b3 100644
--- a/src/zhuye/Coordinate.java
+++ b/src/zhuye/Coordinate.java
@@ -86,12 +86,27 @@
 			return;
 		}
 
-		String[] records = gnggaData.split("\\$GNGGA");
-		for (String record : records) {
+		// 澶勭悊瀹屾暣鐨凣NGGA鏁版嵁锛堝彲鑳藉寘鍚�$GNGGA鍓嶇紑锛�
+		String cleaned = gnggaData.trim();
+		if (cleaned.startsWith("$GNGGA")) {
+			// 濡傛灉鏁版嵁浠�$GNGGA寮�澶达紝鐩存帴瑙f瀽
+			String record = cleaned.substring("$GNGGA".length());
 			Coordinate coord = parseSingleGnggaRecord(record, false);
 			if (coord != null) {
 				coordinates.add(coord);
 			}
+		} else {
+			// 澶勭悊鍙兘鍖呭惈澶氫釜$GNGGA璁板綍鐨勬儏鍐�
+			String[] records = cleaned.split("\\$GNGGA");
+			for (String record : records) {
+				if (record == null || record.trim().isEmpty()) {
+					continue;  // 璺宠繃绌哄瓧绗︿覆锛坰plit浜х敓鐨勭涓�涓厓绱犲彲鑳芥槸绌虹殑锛�
+				}
+				Coordinate coord = parseSingleGnggaRecord(record, false);
+				if (coord != null) {
+					coordinates.add(coord);
+				}
+			}
 		}
 	}
 
diff --git a/src/zhuye/MapRenderer.java b/src/zhuye/MapRenderer.java
index 78bbf86..368e316 100644
--- a/src/zhuye/MapRenderer.java
+++ b/src/zhuye/MapRenderer.java
@@ -98,6 +98,7 @@
     private double mowingCompletionRatio;
     private long lastTrackPersistTimeMillis;
     private boolean trackDirty;
+    private boolean measurementModeActive = false;  // 娴嬮噺妯″紡鏄惁婵�娲�
     private boolean handheldBoundaryPreviewActive;
     private boolean pendingTrackBreak = true;
     private boolean idleTrailSuppressed;
@@ -214,6 +215,10 @@
                 if (!SwingUtilities.isLeftMouseButton(e) || e.getClickCount() != 1) {
                     return;
                 }
+                // 浼樺厛澶勭悊娴嬮噺妯″紡鐐瑰嚮
+                if (measurementModeActive && handleMeasurementClick(e.getPoint())) {
+                    return;
+                }
                 if (handleMowerClick(e.getPoint())) {
                     return;
                 }
@@ -415,6 +420,11 @@
 
         drawMower(g2d);
         
+        // 缁樺埗娴嬮噺妯″紡锛堝鏋滄縺娲伙級
+        if (measurementModeActive) {
+            drawMeasurementMode(g2d, scale);
+        }
+        
         // 淇濆瓨褰撳墠鍙樻崲锛堝寘鍚鍥惧彉鎹級鐢ㄤ簬鍧愭爣杞崲
         AffineTransform currentTransformForLength = g2d.getTransform();
         
@@ -1048,6 +1058,145 @@
         double worldY = (screenPoint.y - visualizationPanel.getHeight() / 2.0) / scale - translateY;
         return new Point2D.Double(worldX, worldY);
     }
+    
+    /**
+     * 澶勭悊娴嬮噺妯″紡鐐瑰嚮
+     */
+    private boolean handleMeasurementClick(Point screenPoint) {
+        if (!measurementModeActive) {
+            return false;
+        }
+        Point2D.Double worldPoint = screenToWorld(screenPoint);
+        celiangmoshi.addPoint(worldPoint);
+        visualizationPanel.repaint();
+        return true;
+    }
+    
+    /**
+     * 璁剧疆娴嬮噺妯″紡
+     */
+    public void setMeasurementMode(boolean active) {
+        measurementModeActive = active;
+        if (!active) {
+            celiangmoshi.clear();
+        }
+        if (visualizationPanel != null) {
+            visualizationPanel.repaint();
+        }
+    }
+    
+    /**
+     * 瑙﹀彂鍦板浘閲嶇粯
+     */
+    public void repaint() {
+        if (visualizationPanel != null) {
+            visualizationPanel.repaint();
+        }
+    }
+    
+    /**
+     * 缁樺埗娴嬮噺妯″紡
+     */
+    private void drawMeasurementMode(Graphics2D g2d, double scale) {
+        List<Point2D.Double> points = celiangmoshi.getPoints();
+        if (points.isEmpty()) {
+            return;
+        }
+        
+        // 淇濆瓨鍘熷鍙樻崲
+        AffineTransform originalTransform = g2d.getTransform();
+        
+        // 璁剧疆娴嬮噺妯″紡棰滆壊
+        Color lineColor = new Color(255, 0, 0, 200);  // 绾㈣壊鍗婇�忔槑
+        Color pointColor = new Color(255, 0, 0, 255);  // 绾㈣壊
+        Color textColor = new Color(33, 37, 41, 220);  // 娣辩伆鑹叉枃瀛�
+        
+        // 璁剧疆绾垮鍜岀偣鐨勫ぇ灏忥紙纭繚鐐瑰拰绾胯繛鎺ワ級
+        float lineWidth = (float)(2.0 / scale);
+        // 鐐圭殑澶у皬锛堝湪涓栫晫鍧愭爣绯讳腑锛岀背锛夛紝纭繚鐐硅冻澶熷ぇ浠ヨ鐩栫嚎鐨勭鐐�
+        double pointSizeInWorld = 0.15d;  // 鐐圭殑澶у皬锛堢背锛�
+        double halfSize = pointSizeInWorld / 2.0;
+        
+        // 鍏堢粯鍒舵墍鏈夋祴閲忕偣锛堜綔涓哄熀纭�灞傦級
+        g2d.setColor(pointColor);
+        for (Point2D.Double point : points) {
+            // 鐐圭殑涓績鍦� point.x, point.y
+            Ellipse2D.Double pointShape = new Ellipse2D.Double(
+                point.x - halfSize,
+                point.y - halfSize,
+                pointSizeInWorld,
+                pointSizeInWorld
+            );
+            g2d.fill(pointShape);
+        }
+        
+        // 鐒跺悗缁樺埗杩炵嚎锛岀‘淇濈嚎浠庣偣鐨勪腑蹇冨紑濮嬪拰缁撴潫锛岀偣鍜岀嚎杩炴帴鍦ㄤ竴璧�
+        g2d.setColor(lineColor);
+        g2d.setStroke(new BasicStroke(lineWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+        
+        // 缁樺埗杩炵嚎鍜岃窛绂绘枃瀛楋紙鏄剧ず涓や釜鐩搁偦鐐硅繛绾跨殑闀垮害锛�
+        for (int i = 0; i < points.size() - 1; i++) {
+            Point2D.Double p1 = points.get(i);
+            Point2D.Double p2 = points.get(i + 1);
+            
+            // 缁樺埗杩炵嚎锛堜粠绗竴涓偣鐨勪腑蹇冨埌绗簩涓偣鐨勪腑蹇冿級
+            // 浣跨敤 Path2D 纭繚绮剧‘缁樺埗锛屼繚鎸佹诞鐐圭簿搴�
+            Path2D.Double linePath = new Path2D.Double();
+            linePath.moveTo(p1.x, p1.y);
+            linePath.lineTo(p2.x, p2.y);
+            g2d.draw(linePath);
+            
+            // 璁$畻璺濈锛堜袱涓浉閭荤偣杩炵嚎鐨勯暱搴︼級
+            double distance = celiangmoshi.calculateDistance(p1, p2);
+            String distanceText = celiangmoshi.formatDistance(distance);
+            
+            // 璁$畻涓偣浣嶇疆锛堢敤浜庢樉绀烘枃瀛楋級
+            double midX = (p1.x + p2.x) / 2.0;
+            double midY = (p1.y + p2.y) / 2.0;
+            
+            // 灏嗕笘鐣屽潗鏍囪浆鎹负灞忓箷鍧愭爣锛堢敤浜庢枃瀛楁樉绀猴級
+            Point2D.Double worldMid = new Point2D.Double(midX, midY);
+            Point2D.Double screenMid = worldToScreen(worldMid);
+            
+            // 鎭㈠鍘熷鍙樻崲浠ョ粯鍒舵枃瀛楋紙鍥哄畾澶у皬锛屼笉闅忕缉鏀惧彉鍖栵級
+            g2d.setTransform(new AffineTransform());
+            
+            // 璁剧疆瀛椾綋锛堜笌缂╂斁鏂囧瓧澶у皬涓�鑷达紝11鍙峰瓧浣擄級
+            Font labelFont = new Font("寰蒋闆呴粦", Font.PLAIN, 11);
+            g2d.setFont(labelFont);
+            FontMetrics metrics = g2d.getFontMetrics(labelFont);
+            
+            // 璁$畻鏂囧瓧浣嶇疆锛堝眳涓樉绀猴級
+            int textWidth = metrics.stringWidth(distanceText);
+            int textHeight = metrics.getHeight();
+            int textX = (int)Math.round(screenMid.x - textWidth / 2.0);
+            int textY = (int)Math.round(screenMid.y - textHeight / 2.0) + metrics.getAscent();
+            
+            // 缁樺埗鏂囧瓧鑳屾櫙锛堝彲閫夛紝鐢ㄤ簬鎻愰珮鍙鎬э級
+            g2d.setColor(new Color(255, 255, 255, 200));
+            g2d.fillRoundRect(textX - 2, textY - metrics.getAscent() - 2, textWidth + 4, textHeight + 4, 4, 4);
+            
+            // 缁樺埗鏂囧瓧
+            g2d.setColor(textColor);
+            g2d.drawString(distanceText, textX, textY);
+            
+            // 鎭㈠鍙樻崲
+            g2d.setTransform(originalTransform);
+        }
+        
+        // 鏈�鍚庡啀娆$粯鍒舵祴閲忕偣锛堝湪杩炵嚎涔嬩笂锛岀‘淇濈偣瑕嗙洊鍦ㄧ嚎鐨勭鐐逛笂锛岀偣鍜岀嚎杩炴帴鍦ㄤ竴璧凤級
+        g2d.setColor(pointColor);
+        for (Point2D.Double point : points) {
+            // 鐐圭殑涓績鍦� point.x, point.y锛屾濂芥槸绾跨殑绔偣浣嶇疆
+            Ellipse2D.Double pointShape = new Ellipse2D.Double(
+                point.x - halfSize,
+                point.y - halfSize,
+                pointSizeInWorld,
+                pointSizeInWorld
+            );
+            g2d.fill(pointShape);
+        }
+    }
 
     private void drawCurrentBoundary(Graphics2D g2d) {
         bianjiedrwa.drawBoundary(g2d, currentBoundary, scale, GRASS_FILL_COLOR, GRASS_BORDER_COLOR);
@@ -1062,33 +1211,53 @@
         if (markers == null || markers.isEmpty()) {
             return;
         }
+        
+        // 淇濆瓨鍘熷鍙樻崲
+        AffineTransform originalTransform = g2d.getTransform();
+        
         Shape markerShape;
         double half = CIRCLE_SAMPLE_SIZE / 2.0;
         g2d.setColor(CIRCLE_SAMPLE_COLOR);
         g2d.setStroke(new BasicStroke((float) (1.2f / scale), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
-        Font originalFont = g2d.getFont();
-        float baseSize = (float) Math.max(12f / scale, 9f);
-        float reducedSize = Math.max(baseSize / 3f, 3f);
-        Font labelFont = originalFont.deriveFont(reducedSize);
+        
+        // 璁剧疆瀛椾綋锛堜笌缂╂斁鏂囧瓧澶у皬涓�鑷达紝11鍙峰瓧浣擄紝涓嶉殢缂╂斁鍙樺寲锛�
+        Font labelFont = new Font("寰蒋闆呴粦", Font.PLAIN, 11);
         g2d.setFont(labelFont);
-        FontMetrics metrics = g2d.getFontMetrics();
+        FontMetrics metrics = g2d.getFontMetrics(labelFont);
+        
         for (double[] pt : markers) {
             if (pt == null || pt.length < 2 || !Double.isFinite(pt[0]) || !Double.isFinite(pt[1])) {
                 continue;
             }
             double x = pt[0];
             double y = pt[1];
+            
+            // 缁樺埗鐐癸紙鍦ㄤ笘鐣屽潗鏍囩郴涓紝闅忕缉鏀惧彉鍖栵級
             markerShape = new Ellipse2D.Double(x - half, y - half, CIRCLE_SAMPLE_SIZE, CIRCLE_SAMPLE_SIZE);
             g2d.fill(markerShape);
+            
+            // 灏嗕笘鐣屽潗鏍囪浆鎹负灞忓箷鍧愭爣浠ョ粯鍒舵枃瀛楋紙涓嶉殢缂╂斁鍙樺寲锛�
+            Point2D.Double worldPoint = new Point2D.Double(x, y);
+            Point2D.Double screenPoint = new Point2D.Double();
+            originalTransform.transform(worldPoint, screenPoint);
+            
+            // 鎭㈠鍘熷鍙樻崲浠ヤ娇鐢ㄥ睆骞曞潗鏍囩粯鍒舵枃瀛�
+            g2d.setTransform(new AffineTransform());
+            
             String label = String.format(Locale.US, "%.2f,%.2f", x, y);
             int textWidth = metrics.stringWidth(label);
-            float textX = (float) (x - textWidth / 2.0);
-            float textY = (float) (y - half - 0.2d) - metrics.getDescent();
+            int textHeight = metrics.getHeight();
+            
+            // 鍦ㄥ睆骞曞潗鏍囩郴涓粯鍒舵枃瀛楋紙涓嶉殢缂╂斁鍙樺寲锛�
+            int textX = (int)(screenPoint.x - textWidth / 2.0);
+            int textY = (int)(screenPoint.y - half - 0.2d) - metrics.getDescent();
             g2d.setColor(new Color(33, 37, 41, 220));
             g2d.drawString(label, textX, textY);
+            
+            // 鎭㈠鍘熷鍙樻崲
+            g2d.setTransform(originalTransform);
             g2d.setColor(CIRCLE_SAMPLE_COLOR);
         }
-        g2d.setFont(originalFont);
     }
 
     private void drawCircleCaptureOverlay(Graphics2D g2d, CircleCaptureOverlay overlay, double scale) {
diff --git a/src/zhuye/Shouye.java b/src/zhuye/Shouye.java
index 56c4e39..30c1444 100644
--- a/src/zhuye/Shouye.java
+++ b/src/zhuye/Shouye.java
@@ -453,10 +453,18 @@
 		}
 		mapRenderer.setIdleTrailDurationSeconds(durationSeconds);
 		
-		// 搴旂敤杈圭晫璺濈鏄剧ず璁剧疆
+		// 搴旂敤杈圭晫璺濈鏄剧ず璁剧疆鍜屾祴閲忔ā寮忚缃�
 		Setsys setsys = new Setsys();
 		setsys.initializeFromProperties();
 		mapRenderer.setBoundaryLengthVisible(setsys.isBoundaryLengthVisible());
+		// 鍒濆鍖栨祴閲忔ā寮�
+		boolean measurementEnabled = setsys.isMeasurementModeEnabled();
+		mapRenderer.setMeasurementMode(measurementEnabled);
+		if (measurementEnabled) {
+			celiangmoshi.start();
+		} else {
+			celiangmoshi.stop();
+		}
 	}
 
 	private void createHeaderPanel() {
@@ -3260,7 +3268,34 @@
 		if (latest == null) {
 			return false;
 		}
-		return lastCapturedCoordinate == null || latest != lastCapturedCoordinate;
+		
+		// 妫�鏌ユ槸鍚︽湁鏂扮殑鍧愭爣锛堜笌涓婃閲囬泦鐨勪笉鍚岋級
+		if (lastCapturedCoordinate != null && latest == lastCapturedCoordinate) {
+			return false;
+		}
+		
+		// 妫�鏌ュ畾浣嶇姸鎬佹槸鍚︿负4锛堝浐瀹氳В锛�
+		// 褰撻�夋嫨鍓茶崏鏈虹粯鍒跺渾褰㈤殰纰嶇墿鏃讹紝闇�瑕佹鏌ヨ澶囩紪鍙峰拰瀹氫綅鐘舵��
+		Device device = Device.getGecaoji();
+		if (device == null) {
+			return false;
+		}
+		
+		String positioningStatus = device.getPositioningStatus();
+		if (positioningStatus == null || !"4".equals(positioningStatus.trim())) {
+			return false;
+		}
+		
+		// 妫�鏌ヨ澶囩紪鍙锋槸鍚﹀尮閰嶅壊鑽夋満缂栧彿
+		String mowerId = Setsys.getPropertyValue("mowerId");
+		String deviceId = device.getMowerNumber();
+		if (mowerId != null && !mowerId.trim().isEmpty()) {
+			if (deviceId == null || !mowerId.trim().equals(deviceId.trim())) {
+				return false;
+			}
+		}
+		
+		return true;
 	}
 
 	private void applyCirclePrimaryButtonState(boolean enabled) {
diff --git a/src/zhuye/celiangmoshi.java b/src/zhuye/celiangmoshi.java
new file mode 100644
index 0000000..0bf8154
--- /dev/null
+++ b/src/zhuye/celiangmoshi.java
@@ -0,0 +1,83 @@
+package zhuye;
+
+import java.awt.*;
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * 娴嬮噺妯″紡 - 鍦ㄥ湴鍥句笂鐐瑰嚮娴嬮噺璺濈
+ */
+public class celiangmoshi {
+    private static boolean active = false;
+    private static final List<Point2D.Double> measurementPoints = new ArrayList<>();
+    
+    private celiangmoshi() {
+    }
+    
+    /**
+     * 鍚姩娴嬮噺妯″紡
+     */
+    public static void start() {
+        active = true;
+        measurementPoints.clear();
+    }
+    
+    /**
+     * 鍋滄娴嬮噺妯″紡
+     */
+    public static void stop() {
+        active = false;
+        measurementPoints.clear();
+    }
+    
+    /**
+     * 妫�鏌ユ槸鍚﹀浜庢祴閲忔ā寮�
+     */
+    public static boolean isActive() {
+        return active;
+    }
+    
+    /**
+     * 娣诲姞娴嬮噺鐐�
+     */
+    public static void addPoint(Point2D.Double point) {
+        if (active && point != null) {
+            measurementPoints.add(new Point2D.Double(point.x, point.y));
+        }
+    }
+    
+    /**
+     * 鑾峰彇鎵�鏈夋祴閲忕偣
+     */
+    public static List<Point2D.Double> getPoints() {
+        return new ArrayList<>(measurementPoints);
+    }
+    
+    /**
+     * 璁$畻涓ょ偣涔嬮棿鐨勮窛绂伙紙绫筹級
+     */
+    public static double calculateDistance(Point2D.Double p1, Point2D.Double p2) {
+        if (p1 == null || p2 == null) {
+            return 0.0;
+        }
+        double dx = p2.x - p1.x;
+        double dy = p2.y - p1.y;
+        return Math.sqrt(dx * dx + dy * dy);
+    }
+    
+    /**
+     * 鏍煎紡鍖栬窛绂绘樉绀猴紙淇濈暀2浣嶅皬鏁帮紝鍗曚綅绫筹級
+     */
+    public static String formatDistance(double distance) {
+        return String.format(Locale.US, "%.2fm", distance);
+    }
+    
+    /**
+     * 娓呴櫎鎵�鏈夋祴閲忕偣
+     */
+    public static void clear() {
+        measurementPoints.clear();
+    }
+}

--
Gitblit v1.10.0