From 48ee74129bb09a817a0bbbabe860c4007b74c66b Mon Sep 17 00:00:00 2001
From: 826220679@qq.com <826220679@qq.com>
Date: 星期日, 21 十二月 2025 12:37:44 +0800
Subject: [PATCH] 新增了往返路径
---
src/dikuai/ObstacleManagementPage.java | 24
src/gecaoji/Device.java | 16
src/zhuye/AreaSelectionDialog.java | 4
dikuai.properties | 8
src/zhuye/WangfanDraw.java | 411 +++++++++
src/dikuai/Wangfanpathpage.java | 200 ++++
src/zhuye/MapRenderer.java | 80 +
set.properties | 10
src/udpdell/UDPServer.java | 18
pom.xml | 35
src/publicway/buttonset.java | 2
src/dikuai/Dikuaiguanli.java | 220 ++++
src/gecaoji/GecaojiMeg.java | 4
src/set/Sets.java | 3
src/lujing/MowingPathGenerationPage.java | 2
src/publicway/Fuzhibutton.java | 2
src/zhangaiwu/yulanzhangaiwu.java | 32
src/zhuye/daohangyulan.java | 1
src/zhuye/zijian.java | 3
src/dikuai/addzhangaiwu.java | 31
src/dikuai/daohangyulan.java | 2
src/yaokong/Control02.java | 26
src/zhuye/LegendDialog.java | 30
.vscode/settings.json | 3
src/zhangaiwu/AddDikuai.java | 2
src/dikuai/FanhuiDialog.java | 5
src/zhuye/Shouye.java | 225 ++++
src/dikuai/Dikuai.java | 16
src/publicway/Gpstoxuzuobiao.java | 141 +++
src/set/debug.java | 3
src/publicway/Lookbutton.java | 9
src/dikuai/Huizhiwanfanpath.java | 896 +++++++++++++++++++++
src/baseStation/BaseStationDialog.java | 3
33 files changed, 2,280 insertions(+), 187 deletions(-)
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..9250139
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx4G -Xms100m -Xlog:disable"
+}
\ No newline at end of file
diff --git a/dikuai.properties b/dikuai.properties
index 15b98c9..b6eab9a 100644
--- a/dikuai.properties
+++ b/dikuai.properties
@@ -1,5 +1,5 @@
#Dikuai Properties
-#Sat Dec 20 14:47:09 GMT+08:00 2025
+#Sun Dec 21 11:15:33 GMT+08:00 2025
LAND2.boundaryCoordinates=5.38,-6.41;-11.15,-8.94;-12.75,-4.68;-12.23,-3.28;-11.12,-3.17;-9.29,-3.53;-7.46,-3.89;-5.62,-4.25;-3.79,-4.61;-1.96,-4.97;-0.12,-5.33;1.71,-5.69;3.54,-6.05;5.38,-6.41
LAND1.intelligentSceneAnalysis=-1
LAND1.mowingSafetyDistance=-1
@@ -11,13 +11,15 @@
LAND2.intelligentSceneAnalysis=-1
LAND2.mowingOverlapDistance=0.06
LAND2.landArea=55.11
+LAND2.returnPathRawCoordinates=38.12,1.53;36.68,1.24;35.24,0.94;33.80,0.59;32.43,0.16;31.15,-0.38;30.26,-1.26;30.03,-2.37;30.12,-3.81;30.34,-5.22;30.59,-6.60;30.87,-7.98;31.10,-9.36;31.33,-10.77;31.57,-12.20;31.82,-13.68;32.07,-15.16;32.28,-16.60;32.52,-17.92;32.78,-19.37;33.07,-20.80;33.35,-22.37;33.62,-23.91;33.89,-25.35;34.16,-26.79;34.23,-28.32;33.75,-29.45;32.72,-30.03;31.41,-30.28;30.15,-30.22;29.26,-29.73;28.93,-28.62;28.72,-27.16;28.54,-25.66;28.26,-24.17;28.01,-22.74
LAND1.returnPathCoordinates=-1
LAND1.mowingPattern=骞宠绾�
LAND1.mowingOverlapDistance=0.06
-LAND2.updateTime=2025-12-20 14\:47\:09
+LAND2.updateTime=2025-12-21 11\:15\:33
+LAND1.returnPathRawCoordinates=-1
LAND2.createTime=2025-12-20 12\:24\:28
LAND1.mowingWidth=34
-LAND2.returnPathCoordinates=-1
+LAND2.returnPathCoordinates=38.12,1.53;33.80,0.59;31.15,-0.38;30.26,-1.26;30.03,-2.37;30.34,-5.22;34.16,-26.79;34.23,-28.32;33.75,-29.45;32.72,-30.03;31.41,-30.28;30.15,-30.22;29.26,-29.73;28.93,-28.62;28.01,-22.74
LAND2.angleThreshold=-1
LAND2.boundaryPointInterval=-1
LAND2.mowingWidth=34
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..e0c6222
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.example</groupId>
+ <artifactId>gecao-app</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <!-- Use a widely compatible release for baseline; will upgrade to 21 in plan -->
+ <maven.compiler.release>11</maven.compiler.release>
+ </properties>
+
+ <build>
+ <!-- Project uses non-standard src layout, point Maven to existing source directory -->
+ <sourceDirectory>src</sourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.11.0</version>
+ <configuration>
+ <release>${maven.compiler.release}</release>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <!-- Add dependencies as needed; current project appears to use local libs -->
+ </dependencies>
+</project>
diff --git a/set.properties b/set.properties
index a526d41..f4e9df7 100644
--- a/set.properties
+++ b/set.properties
@@ -1,5 +1,5 @@
-#Current work land selection updated
-#Sat Dec 20 15:10:30 GMT+08:00 2025
+#Mower Configuration Properties - Updated
+#Sun Dec 21 12:36:00 GMT+08:00 2025
appVersion=-1
simCardNumber=-1
currentWorkLandNumber=LAND2
@@ -7,13 +7,13 @@
boundaryLengthVisible=false
idleTrailDurationSeconds=60
handheldMarkerId=1872
-viewCenterX=5.62
-viewCenterY=6.22
+viewCenterX=-13.31
+viewCenterY=3.75
manualBoundaryDrawingMode=false
mowerId=860
serialPortName=COM15
serialAutoConnect=true
-mapScale=13.68
+mapScale=11.63
measurementModeEnabled=false
firmwareVersion=-1
cuttingWidth=200
diff --git a/src/baseStation/BaseStationDialog.java b/src/baseStation/BaseStationDialog.java
index dc9d8b9..a878c06 100644
--- a/src/baseStation/BaseStationDialog.java
+++ b/src/baseStation/BaseStationDialog.java
@@ -3,6 +3,7 @@
import javax.swing.*;
import gecaoji.Device;
+import publicway.buttonset;
import java.awt.*;
import java.awt.event.ActionListener;
@@ -16,8 +17,6 @@
import java.util.Locale;
import java.util.Properties;
-import zhuye.buttonset;
-
public class BaseStationDialog extends JDialog {
private final Color THEME_COLOR;
private final Device device;
diff --git a/src/dikuai/Dikuai.java b/src/dikuai/Dikuai.java
index a21456c..12a4edd 100644
--- a/src/dikuai/Dikuai.java
+++ b/src/dikuai/Dikuai.java
@@ -25,6 +25,8 @@
private String returnPointCoordinates;
// 寰�杩旇矾寰勫潗鏍囷紙鍓茶崏鏈哄畬鎴愬壊鑽変綔涓氳繑鍥炵殑璺緞鍧愭爣锛屾牸寮忥細X1,Y1;X2,Y2;...;XN,YN锛�
private String returnPathCoordinates;
+ // 寰�杩旇矾寰勫師濮嬪潗鏍�
+ private String returnPathRawCoordinates;
// 杈圭晫鐐归棿闅�
private String boundaryPointInterval;
// 瑙掑害闃堝��
@@ -103,6 +105,7 @@
dikuai.plannedPath = landProps.getProperty("plannedPath", "-1");
dikuai.returnPointCoordinates = landProps.getProperty("returnPointCoordinates", "-1");
dikuai.returnPathCoordinates = landProps.getProperty("returnPathCoordinates", "-1");
+ dikuai.returnPathRawCoordinates = landProps.getProperty("returnPathRawCoordinates", "-1");
dikuai.boundaryPointInterval = landProps.getProperty("boundaryPointInterval", "-1");
dikuai.angleThreshold = landProps.getProperty("angleThreshold", "-1");
dikuai.intelligentSceneAnalysis = landProps.getProperty("intelligentSceneAnalysis", "-1");
@@ -210,6 +213,9 @@
case "returnPathCoordinates":
this.returnPathCoordinates = value;
return true;
+ case "returnPathRawCoordinates":
+ this.returnPathRawCoordinates = value;
+ return true;
case "boundaryPointInterval":
this.boundaryPointInterval = value;
return true;
@@ -274,6 +280,7 @@
if (dikuai.plannedPath != null) properties.setProperty(landNumber + ".plannedPath", dikuai.plannedPath);
if (dikuai.returnPointCoordinates != null) properties.setProperty(landNumber + ".returnPointCoordinates", dikuai.returnPointCoordinates);
if (dikuai.returnPathCoordinates != null) properties.setProperty(landNumber + ".returnPathCoordinates", dikuai.returnPathCoordinates);
+ if (dikuai.returnPathRawCoordinates != null) properties.setProperty(landNumber + ".returnPathRawCoordinates", dikuai.returnPathRawCoordinates);
if (dikuai.boundaryPointInterval != null) properties.setProperty(landNumber + ".boundaryPointInterval", dikuai.boundaryPointInterval);
if (dikuai.angleThreshold != null) properties.setProperty(landNumber + ".angleThreshold", dikuai.angleThreshold);
if (dikuai.intelligentSceneAnalysis != null) properties.setProperty(landNumber + ".intelligentSceneAnalysis", dikuai.intelligentSceneAnalysis);
@@ -366,6 +373,14 @@
this.returnPathCoordinates = returnPathCoordinates;
}
+ public String getReturnPathRawCoordinates() {
+ return returnPathRawCoordinates;
+ }
+
+ public void setReturnPathRawCoordinates(String returnPathRawCoordinates) {
+ this.returnPathRawCoordinates = returnPathRawCoordinates;
+ }
+
public String getBoundaryPointInterval() {
return boundaryPointInterval;
}
@@ -481,6 +496,7 @@
", plannedPath='" + plannedPath + '\'' +
", returnPointCoordinates='" + returnPointCoordinates + '\'' +
", returnPathCoordinates='" + returnPathCoordinates + '\'' +
+ ", returnPathRawCoordinates='" + returnPathRawCoordinates + '\'' +
", boundaryPointInterval='" + boundaryPointInterval + '\'' +
", angleThreshold='" + angleThreshold + '\'' +
", intelligentSceneAnalysis='" + intelligentSceneAnalysis + '\'' +
diff --git a/src/dikuai/Dikuaiguanli.java b/src/dikuai/Dikuaiguanli.java
index 740ccf8..70bc5b1 100644
--- a/src/dikuai/Dikuaiguanli.java
+++ b/src/dikuai/Dikuaiguanli.java
@@ -23,14 +23,14 @@
import lujing.Lunjingguihua;
import lujing.MowingPathGenerationPage;
+import publicway.Fuzhibutton;
+import publicway.Lookbutton;
+import publicway.buttonset;
import zhangaiwu.AddDikuai;
import zhangaiwu.Obstacledge;
import zhuye.MapRenderer;
import zhuye.Shouye;
import zhuye.Coordinate;
-import zhuye.buttonset;
-import zhuye.Fuzhibutton;
-import zhuye.Lookbutton;
import gecaoji.Device;
/**
@@ -316,8 +316,7 @@
contentPanel.add(Box.createRigidArea(new Dimension(0, 10)));
// 寰�杩旂偣璺緞锛堝甫鏌ョ湅鍥炬爣鎸夐挳锛�
- JPanel returnPathPanel = createCardInfoItemWithButton("寰�杩旂偣璺緞:",
- getDisplayValue(dikuai.getReturnPathCoordinates(), "鏈缃�"),
+ JPanel returnPathPanel = createCardInfoItemWithIconButton("寰�杩旂偣璺緞:",
createViewButton(e -> editReturnPath(dikuai)));
configureInteractiveLabel(getInfoItemTitleLabel(returnPathPanel),
() -> editReturnPath(dikuai),
@@ -755,6 +754,20 @@
}
private String promptCoordinateEditing(String title, String initialValue) {
+ return promptCoordinateEditing(title, initialValue, null);
+ }
+
+ private String promptCoordinateEditing(String title, String initialValue, Dikuai dikuai) {
+ // 鍒ゆ柇鏄惁鏄線杩旂偣璺緞瀵硅瘽妗�
+ boolean isReturnPathDialog = title != null && title.contains("寰�杩旂偣璺緞");
+
+ if (isReturnPathDialog) {
+ Window owner = SwingUtilities.getWindowAncestor(this);
+ Wangfanpathpage page = new Wangfanpathpage(owner, title, initialValue, dikuai);
+ page.setVisible(true);
+ return page.getResult();
+ }
+
JTextArea textArea = new JTextArea(prepareCoordinateForEditor(initialValue));
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
@@ -763,7 +776,8 @@
textArea.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
JScrollPane scrollPane = new JScrollPane(textArea);
- scrollPane.setPreferredSize(new Dimension(360, 240));
+ // 濡傛灉鏄線杩旂偣璺緞瀵硅瘽妗嗭紝楂樺害璋冩暣涓洪�傚簲涓や釜鏂囨湰鍩�
+ scrollPane.setPreferredSize(new Dimension(360, isReturnPathDialog ? 100 : 240));
Window owner = SwingUtilities.getWindowAncestor(this);
JDialog dialog;
@@ -776,17 +790,115 @@
}
dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
- JPanel contentPanel = new JPanel(new BorderLayout());
+ JPanel contentPanel = new JPanel();
contentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
- contentPanel.add(scrollPane, BorderLayout.CENTER);
+
+ if (isReturnPathDialog) {
+ contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS));
+ // 鍑忓皬杈硅窛浠ュ鍔犳枃鏈煙瀹藉害 (98%宸﹀彸)
+ contentPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+
+ // 1. 鍘熷寰�杩旇矾寰勫潗鏍囧尯鍩�
+ String rawCoords = dikuai != null ? prepareCoordinateForEditor(dikuai.getReturnPathRawCoordinates()) : "";
+ int rawCount = 0;
+ if (rawCoords != null && !rawCoords.isEmpty() && !"-1".equals(rawCoords)) {
+ rawCount = rawCoords.split(";").length;
+ }
+
+ JPanel rawHeaderPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
+ rawHeaderPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ rawHeaderPanel.setBackground(BACKGROUND_COLOR);
+ rawHeaderPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
+
+ JLabel rawTitleLabel = new JLabel("鍘熷寰�杩旇矾寰勫潗鏍� (" + rawCount + "鐐�) ");
+ rawTitleLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
+ rawHeaderPanel.add(rawTitleLabel);
+
+ // 鍘熷鍧愭爣澶嶅埗鎸夐挳
+ final String finalRawCoords = rawCoords;
+ JButton rawCopyBtn = Fuzhibutton.createCopyButton(
+ () -> {
+ if (finalRawCoords == null || finalRawCoords.isEmpty() || "-1".equals(finalRawCoords)) return null;
+ return finalRawCoords;
+ },
+ "澶嶅埗",
+ new Color(230, 250, 240)
+ );
+ rawCopyBtn.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+ rawCopyBtn.setPreferredSize(new Dimension(50, 24));
+ rawCopyBtn.setMargin(new Insets(0,0,0,0));
+ rawHeaderPanel.add(rawCopyBtn);
+
+ contentPanel.add(rawHeaderPanel);
+ contentPanel.add(Box.createVerticalStrut(5));
+
+ JTextArea rawTextArea = new JTextArea(rawCoords);
+ rawTextArea.setLineWrap(true);
+ rawTextArea.setWrapStyleWord(true);
+ rawTextArea.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
+ rawTextArea.setEditable(false); // 鍘熷鍧愭爣閫氬父涓嶅彲缂栬緫
+ rawTextArea.setRows(4);
+ rawTextArea.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+
+ JScrollPane rawScroll = new JScrollPane(rawTextArea);
+ rawScroll.setAlignmentX(Component.LEFT_ALIGNMENT);
+ // 璁剧疆鏈�澶у搴﹀厑璁告墿灞曪紝棣栭�夊搴﹂�備腑
+ rawScroll.setPreferredSize(new Dimension(300, 100));
+ rawScroll.setMaximumSize(new Dimension(Integer.MAX_VALUE, 100));
+ contentPanel.add(rawScroll);
+
+ contentPanel.add(Box.createVerticalStrut(15));
+
+ // 2. 浼樺寲鍚庡線杩旇矾寰勫潗鏍囧尯鍩�
+ String optCoords = prepareCoordinateForEditor(initialValue);
+ int optCount = 0;
+ if (optCoords != null && !optCoords.isEmpty() && !"-1".equals(optCoords)) {
+ optCount = optCoords.split(";").length;
+ }
+
+ JPanel optHeaderPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
+ optHeaderPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ optHeaderPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
+
+ JLabel optTitleLabel = new JLabel("浼樺寲鍚庡線杩旇矾寰勫潗鏍� (" + optCount + "鐐�) ");
+ optTitleLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
+ optHeaderPanel.add(optTitleLabel);
+
+ // 浼樺寲鍧愭爣澶嶅埗鎸夐挳 - 鍔ㄦ�佽幏鍙栨枃鏈煙鍐呭
+ JButton optCopyBtn = Fuzhibutton.createCopyButton(
+ () -> {
+ String text = textArea.getText();
+ if (text == null || text.trim().isEmpty() || "-1".equals(text.trim())) return null;
+ return text;
+ },
+ "澶嶅埗",
+ new Color(230, 250, 240)
+ );
+ optCopyBtn.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+ optCopyBtn.setPreferredSize(new Dimension(50, 24));
+ optCopyBtn.setMargin(new Insets(0,0,0,0));
+ optHeaderPanel.add(optCopyBtn);
+
+ contentPanel.add(optHeaderPanel);
+ contentPanel.add(Box.createVerticalStrut(5));
+
+ // 浣跨敤浼犲叆鐨� textArea (宸插垵濮嬪寲涓� initialValue)
+ textArea.setRows(4);
+ scrollPane.setAlignmentX(Component.LEFT_ALIGNMENT);
+ scrollPane.setPreferredSize(new Dimension(300, 100));
+ scrollPane.setMaximumSize(new Dimension(Integer.MAX_VALUE, 100));
+ contentPanel.add(scrollPane);
+
+ } else {
+ contentPanel.setLayout(new BorderLayout());
+ contentPanel.add(scrollPane, BorderLayout.CENTER);
+ }
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
- // 鍒ゆ柇鏄惁鏄線杩旂偣璺緞瀵硅瘽妗�
- boolean isReturnPathDialog = title != null && title.contains("寰�杩旂偣璺緞");
JButton okButton;
JButton cancelButton;
- JButton copyButton;
+ JButton copyButton = null; // 鍒濆鍖栦负null
if (isReturnPathDialog) {
// 寰�杩旂偣璺緞瀵硅瘽妗嗭細浣跨敤 buttonset 椋庢牸鐨勭‘瀹氭寜閽紝鍥炬爣鎸夐挳
@@ -811,22 +923,8 @@
public void mouseExited(MouseEvent e) { cancelButton.setOpaque(false); }
});
- // 浣跨敤 Fuzhibutton 鍒涘缓澶嶅埗鎸夐挳
- copyButton = Fuzhibutton.createCopyButton(
- (java.util.function.Supplier<String>) () -> {
- String text = textArea.getText();
- if (text == null) {
- text = "";
- }
- String trimmed = text.trim();
- if (trimmed.isEmpty() || "-1".equals(trimmed)) {
- return null; // 杩斿洖null浼氳Е鍙�"鏈缃澶嶅埗鐨勫唴瀹�"鎻愮ず
- }
- return text;
- },
- "澶嶅埗" + title,
- new Color(230, 250, 240)
- );
+ // 浣跨敤 Fuzhibutton 鍒涘缓澶嶅埗鎸夐挳 (杩欓噷涓嶅啀闇�瑕佸簳閮ㄧ殑澶嶅埗鎸夐挳锛屽洜涓轰笂闈㈠凡缁忔湁浜�)
+ // copyButton = ...
} else {
// 鍏朵粬瀵硅瘽妗嗕繚鎸佸師鏈夋牱寮�
@@ -860,18 +958,41 @@
final String[] resultHolder = new String[1];
okButton.addActionListener(e -> {
- resultHolder[0] = textArea.getText();
- confirmed[0] = true;
- dialog.dispose();
+ if (isReturnPathDialog) {
+ // 寰�杩旂偣璺緞瀵硅瘽妗嗭細鏍囪涓烘墦寮�缁樺埗椤甸潰
+ // 濡傛灉鏂囨湰鍩熶腑宸茬粡鏈夊潗鏍囷紝琛ㄧず瑕侀噸鏂扮粯鍒�
+ String currentText = textArea.getText();
+ if (currentText != null && !currentText.trim().isEmpty() && !"-1".equals(currentText.trim())) {
+ // 鏈夊潗鏍囷紝琛ㄧず閲嶆柊缁樺埗
+ resultHolder[0] = "__OPEN_DRAW_PAGE_REFRESH__";
+ } else {
+ // 娌℃湁鍧愭爣锛屾甯哥粯鍒�
+ resultHolder[0] = "__OPEN_DRAW_PAGE__";
+ }
+ confirmed[0] = true;
+ dialog.dispose();
+ } else {
+ resultHolder[0] = textArea.getText();
+ confirmed[0] = true;
+ dialog.dispose();
+ }
});
cancelButton.addActionListener(e -> dialog.dispose());
buttonPanel.add(okButton);
buttonPanel.add(cancelButton);
- buttonPanel.add(copyButton);
+ if (copyButton != null) {
+ buttonPanel.add(copyButton);
+ }
- contentPanel.add(buttonPanel, BorderLayout.SOUTH);
+ contentPanel.add(buttonPanel, isReturnPathDialog ? null : BorderLayout.SOUTH);
+ if (isReturnPathDialog) {
+ // 瀵逛簬 BoxLayout锛岀洿鎺ユ坊鍔犲埌搴曢儴
+ JPanel bottomWrapper = new JPanel(new BorderLayout());
+ bottomWrapper.add(buttonPanel, BorderLayout.EAST);
+ contentPanel.add(bottomWrapper);
+ }
dialog.setContentPane(contentPanel);
dialog.getRootPane().setDefaultButton(okButton);
dialog.pack();
@@ -1914,10 +2035,41 @@
if (dikuai == null) {
return;
}
- String edited = promptCoordinateEditing("鏌ョ湅 / 缂栬緫寰�杩旂偣璺緞", dikuai.getReturnPathCoordinates());
+ String edited = promptCoordinateEditing("鏌ョ湅 / 缂栬緫寰�杩旂偣璺緞", dikuai.getReturnPathCoordinates(), dikuai);
if (edited == null) {
return;
}
+ // 妫�鏌ユ槸鍚︽槸鐗规畩鏍囪锛岃〃绀虹偣鍑讳簡"鍘荤粯鍒�"鎸夐挳
+ if ("__OPEN_DRAW_PAGE__".equals(edited) || "__OPEN_DRAW_PAGE_REFRESH__".equals(edited)) {
+ // 鑾峰彇鍦板潡绠$悊瀵硅瘽妗�
+ Window owner = SwingUtilities.getWindowAncestor(this);
+ Window managementWindow = null;
+ if (owner instanceof JDialog) {
+ managementWindow = owner;
+ }
+
+ // 鑾峰彇鍦板潡绠$悊瀵硅瘽妗嗙殑鐖剁獥鍙o紙涓荤獥鍙o級锛屼綔涓虹粯鍒堕〉闈㈢殑鐖剁獥鍙�
+ Window drawPageOwner = null;
+ if (managementWindow != null) {
+ drawPageOwner = managementWindow.getOwner();
+ }
+ if (drawPageOwner == null && owner != null) {
+ drawPageOwner = owner.getOwner();
+ }
+ if (drawPageOwner == null) {
+ drawPageOwner = owner;
+ }
+
+ // 鍏堝叧闂湴鍧楃鐞嗛〉闈�
+ if (managementWindow != null) {
+ managementWindow.dispose();
+ }
+
+ // 鐒跺悗鎵撳紑缁樺埗椤甸潰锛屽鏋滄槸閲嶆柊缁樺埗锛屼紶閫掓爣璁�
+ boolean isRefresh = "__OPEN_DRAW_PAGE_REFRESH__".equals(edited);
+ Huizhiwanfanpath.showDrawReturnPathDialog(drawPageOwner, dikuai, isRefresh);
+ return;
+ }
String normalized = normalizeCoordinateInput(edited);
if (!saveFieldAndRefresh(dikuai, "returnPathCoordinates", normalized)) {
JOptionPane.showMessageDialog(this, "鏃犳硶鏇存柊寰�杩旂偣璺緞", "閿欒", JOptionPane.ERROR_MESSAGE);
@@ -2322,4 +2474,6 @@
return new ArrayList<>(names);
}
}
+
+
}
\ No newline at end of file
diff --git a/src/dikuai/FanhuiDialog.java b/src/dikuai/FanhuiDialog.java
index ab5188f..bca38a7 100644
--- a/src/dikuai/FanhuiDialog.java
+++ b/src/dikuai/FanhuiDialog.java
@@ -1,13 +1,16 @@
package dikuai;
import javax.swing.*;
+
+import publicway.buttonset;
+
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileInputStream;
import java.util.Properties;
import zhuye.MowerLocationData;
-import zhuye.buttonset;
+
import java.text.DecimalFormat;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
diff --git a/src/dikuai/Huizhiwanfanpath.java b/src/dikuai/Huizhiwanfanpath.java
new file mode 100644
index 0000000..b3806f0
--- /dev/null
+++ b/src/dikuai/Huizhiwanfanpath.java
@@ -0,0 +1,896 @@
+package dikuai;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+
+import set.Setsys;
+import ui.UIConfig;
+import zhuye.Coordinate;
+import lujing.WangfanpathJisuan;
+import publicway.buttonset;
+
+/**
+ * 缁樺埗寰�杩旇矾寰勯〉闈�
+ * 鍙傝�冩柊澧炲湴鍧楁楠�2锛岀敤浜庣粯鍒跺線杩旂偣璺緞
+ */
+public class Huizhiwanfanpath extends JDialog {
+ private static final long serialVersionUID = 1L;
+
+ // 涓婚棰滆壊 - 浼樺寲閰嶈壊鏂规
+ private final Color PRIMARY_COLOR = new Color(46, 139, 87);
+ private final Color PRIMARY_LIGHT = new Color(232, 245, 233);
+ private final Color PRIMARY_DARK = new Color(30, 107, 69);
+ private final Color WHITE = Color.WHITE;
+ private final Color LIGHT_GRAY = new Color(248, 249, 250);
+ private final Color TEXT_COLOR = new Color(33, 37, 41);
+ private final Color LIGHT_TEXT = new Color(108, 117, 125);
+ private final Color BORDER_COLOR = new Color(222, 226, 230);
+ private final Color ERROR_COLOR = new Color(220, 53, 69);
+
+ // 涓婚潰鏉�
+ private JPanel mainPanel;
+ private Map<String, JPanel> drawingOptionPanels = new HashMap<>();
+
+ // 閫夐」鐘舵��
+ private JPanel selectedOptionPanel = null;
+ private JButton startEndDrawingBtn;
+ private boolean isDrawing = false;
+ private boolean hasDrawnPath = false; // 鏄惁宸茬粡缁樺埗杩囪矾寰�
+ private JLabel pathCountLabel;
+ private JLabel drawingMethodHintLabel; // 缁樺埗鏂瑰紡鎻愮ず鏍囩
+ private JButton generatePathBtn; // 鐢熸垚璺緞鎸夐挳
+ private JButton previewBtn; // 棰勮鎸夐挳
+ private JButton saveBtn; // 淇濆瓨鎸夐挳
+ private JPanel actionButtonsPanel; // 鎿嶄綔鎸夐挳闈㈡澘
+
+ // 鍦板潡淇℃伅
+ private Dikuai currentDikuai;
+ private boolean isRefreshMode = false; // 鏄惁閲嶆柊缁樺埗妯″紡
+ private String optimizedPathCoordinates = null; // 浼樺寲鍚庣殑璺緞鍧愭爣
+ private JTextArea rawCoordinatesArea; // 鍘熷鍧愭爣鏂囨湰鍩�
+ private JTextArea optimizedCoordinatesArea; // 璁$畻鍚庡潗鏍囨枃鏈煙
+ private JLabel rawCoordinatesLabel; // 鍘熷鍧愭爣鏍囬
+ private JLabel optimizedCoordinatesLabel; // 浼樺寲鍚庡潗鏍囨爣棰�
+
+ public Huizhiwanfanpath(Window parent, Dikuai dikuai) {
+ this(parent, dikuai, false);
+ }
+
+ public Huizhiwanfanpath(Window parent, Dikuai dikuai, boolean isRefresh) {
+ super(parent, "缁樺埗寰�杩旇矾寰�", Dialog.ModalityType.APPLICATION_MODAL);
+ this.currentDikuai = dikuai;
+ this.isRefreshMode = isRefresh;
+ initializeUI();
+ }
+
+ private void initializeUI() {
+ setLayout(new BorderLayout());
+ setBackground(WHITE);
+
+ // 缁熶竴浣跨敤 6.5 瀵哥珫灞忛�傞厤灏哄
+ setSize(UIConfig.DIALOG_WIDTH, UIConfig.DIALOG_HEIGHT);
+ setLocationRelativeTo(getParent());
+ setResizable(false);
+
+ createMainPanel();
+ add(mainPanel, BorderLayout.CENTER);
+ }
+
+ private void createMainPanel() {
+ mainPanel = new JPanel();
+ mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
+ mainPanel.setBackground(WHITE);
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ // 姝ラ鏍囬 - 宸﹀榻愶紙鍘绘帀"缁樺埗杈圭晫"鏂囧瓧锛�
+ // JLabel stepTitle = new JLabel("姝ラ1锛�");
+ // stepTitle.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 20));
+ // stepTitle.setForeground(TEXT_COLOR);
+ // stepTitle.setAlignmentX(Component.LEFT_ALIGNMENT);
+ // mainPanel.add(stepTitle);
+ // mainPanel.add(Box.createRigidArea(new Dimension(0, 20)));
+
+ // 姝ラ璇存槑
+ JPanel instructionPanel = createInstructionPanel(
+ "璇烽�夋嫨缁樺埗寰�杩旇矾寰勭殑鏂瑰紡銆傚壊鑽夋満缁樺埗闇�瑕侀┚椹跺壊鑽夋満娌胯矾寰勮椹讹紝" +
+ "鎵嬫寔璁惧缁樺埗鍒欎娇鐢ㄤ究鎼鸿澶囨爣璁拌矾寰勭偣銆�"
+ );
+ instructionPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ mainPanel.add(instructionPanel);
+ mainPanel.add(Box.createRigidArea(new Dimension(0, 25)));
+
+ // 缁樺埗鏂瑰紡閫夋嫨
+ JLabel methodLabel = new JLabel("閫夋嫨缁樺埗鏂瑰紡");
+ methodLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
+ methodLabel.setForeground(TEXT_COLOR);
+ methodLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ mainPanel.add(methodLabel);
+ mainPanel.add(Box.createRigidArea(new Dimension(0, 15)));
+
+ // 缁樺埗閫夐」闈㈡澘 - 鍨傜洿甯冨眬浠ュ畬鏁存樉绀哄浘鏍�
+ JPanel optionsPanel = new JPanel();
+ optionsPanel.setLayout(new BoxLayout(optionsPanel, BoxLayout.Y_AXIS));
+ optionsPanel.setBackground(WHITE);
+ optionsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ // 鍓茶崏鏈虹粯鍒堕�夐」
+ JPanel mowerOption = createDrawingOption("馃殰", "鍓茶崏鏈虹粯鍒�",
+ "", "mower");
+ mowerOption.setAlignmentX(Component.LEFT_ALIGNMENT);
+ mowerOption.setBorder(BorderFactory.createLineBorder(BORDER_COLOR, 2));
+
+ // 鎵嬫寔璁惧缁樺埗閫夐」
+ JPanel handheldOption = createDrawingOption("馃摫", "鎵嬫寔璁惧缁樺埗",
+ "", "handheld");
+ handheldOption.setAlignmentX(Component.LEFT_ALIGNMENT);
+ handheldOption.setBorder(BorderFactory.createLineBorder(BORDER_COLOR, 2));
+
+ optionsPanel.add(mowerOption);
+ optionsPanel.add(Box.createRigidArea(new Dimension(0, 15)));
+ optionsPanel.add(handheldOption);
+
+ mainPanel.add(optionsPanel);
+ mainPanel.add(Box.createRigidArea(new Dimension(0, 30)));
+
+ // 缁樺埗鏂瑰紡鎻愮ず鏍囩锛堝鏋滄病鏈夐�夋嫨缁樺埗鏂瑰紡鏃舵樉绀猴級
+ // 鎻愮ず鏂囨湰锛氭彁閱掑厛閫夋嫨缁樺埗鏂瑰紡鍐嶅紑濮�
+ drawingMethodHintLabel = new JLabel("璇峰厛閫夋嫨缁樺埗鏂瑰紡鍐嶇偣鍑诲紑濮嬬粯鍒�");
+ drawingMethodHintLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+ drawingMethodHintLabel.setForeground(ERROR_COLOR);
+ drawingMethodHintLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ drawingMethodHintLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
+ drawingMethodHintLabel.setVisible(false);
+ mainPanel.add(drawingMethodHintLabel);
+
+ // 寮�濮�/缁撴潫缁樺埗鎸夐挳
+ startEndDrawingBtn = createPrimaryButton("寮�濮嬬粯鍒�", 16);
+ startEndDrawingBtn.setAlignmentX(Component.LEFT_ALIGNMENT);
+ startEndDrawingBtn.setMaximumSize(new Dimension(400, 55));
+ startEndDrawingBtn.setEnabled(true); // 鍒濆鍚敤锛屼互渚跨偣鍑绘椂妫�鏌ユ槸鍚﹂�夋嫨浜嗙粯鍒舵柟寮�
+
+ startEndDrawingBtn.addActionListener(e -> toggleDrawing());
+
+ mainPanel.add(startEndDrawingBtn);
+
+ // 鍘熷鍧愭爣鏄剧ず鍖哄煙锛堥噸鏂扮粯鍒舵寜閽笅鏂癸級
+ rawCoordinatesLabel = new JLabel("璺緞鍘熷鍧愭爣");
+ rawCoordinatesLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
+ rawCoordinatesLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ rawCoordinatesLabel.setBorder(BorderFactory.createEmptyBorder(20, 0, 5, 0));
+ rawCoordinatesLabel.setVisible(false);
+ mainPanel.add(rawCoordinatesLabel);
+
+ rawCoordinatesArea = new JTextArea(3, 20);
+ rawCoordinatesArea.setFont(new Font("Consolas", Font.PLAIN, 12));
+ rawCoordinatesArea.setLineWrap(true);
+ rawCoordinatesArea.setWrapStyleWord(true);
+ rawCoordinatesArea.setEditable(false);
+ rawCoordinatesArea.setBorder(BorderFactory.createLineBorder(BORDER_COLOR));
+
+ JScrollPane rawScrollPane = new JScrollPane(rawCoordinatesArea);
+ rawScrollPane.setAlignmentX(Component.LEFT_ALIGNMENT);
+ rawScrollPane.setMaximumSize(new Dimension(400, 60)); // 闄愬埗楂樺害
+ rawScrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); // 鍘绘帀椤堕儴闂撮殧锛岀敱鏍囩鎻愪緵
+ rawScrollPane.setVisible(false); // 鍒濆闅愯棌
+ mainPanel.add(rawScrollPane);
+
+ // 鎿嶄綔鎸夐挳闈㈡澘锛堢敓鎴愯矾寰勩�侀瑙堛�佷繚瀛橈級
+ actionButtonsPanel = createActionButtonsPanel();
+ actionButtonsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ actionButtonsPanel.setVisible(false);
+ mainPanel.add(actionButtonsPanel);
+
+ // 璁$畻鍚庡潗鏍囨樉绀哄尯鍩燂紙淇濆瓨鎸夐挳涓嬫柟锛�
+ optimizedCoordinatesLabel = new JLabel("浼樺寲鍚庤矾寰勫潗鏍�");
+ optimizedCoordinatesLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
+ optimizedCoordinatesLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ optimizedCoordinatesLabel.setBorder(BorderFactory.createEmptyBorder(20, 0, 5, 0));
+ optimizedCoordinatesLabel.setVisible(false);
+ mainPanel.add(optimizedCoordinatesLabel);
+
+ optimizedCoordinatesArea = new JTextArea(3, 20);
+ optimizedCoordinatesArea.setFont(new Font("Consolas", Font.PLAIN, 12));
+ optimizedCoordinatesArea.setLineWrap(true);
+ optimizedCoordinatesArea.setWrapStyleWord(true);
+ optimizedCoordinatesArea.setEditable(false);
+ optimizedCoordinatesArea.setBorder(BorderFactory.createLineBorder(BORDER_COLOR));
+
+ JScrollPane optimizedScrollPane = new JScrollPane(optimizedCoordinatesArea);
+ optimizedScrollPane.setAlignmentX(Component.LEFT_ALIGNMENT);
+ optimizedScrollPane.setMaximumSize(new Dimension(400, 60)); // 闄愬埗楂樺害
+ optimizedScrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); // 鍘绘帀椤堕儴闂撮殧锛岀敱鏍囩鎻愪緵
+ optimizedScrollPane.setVisible(false); // 鍒濆闅愯棌
+ mainPanel.add(optimizedScrollPane);
+
+ mainPanel.add(Box.createVerticalGlue());
+ }
+
+ private JPanel createDrawingOption(String icon, String text, String description, String type) {
+ JPanel optionPanel = new JPanel(new BorderLayout(15, 0));
+ optionPanel.setBackground(WHITE);
+ optionPanel.setCursor(new Cursor(Cursor.HAND_CURSOR));
+ optionPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+ optionPanel.setMaximumSize(new Dimension(500, 120));
+
+ // 鍥炬爣鍖哄煙
+ JLabel iconLabel;
+ if ("handheld".equals(type) || "mower".equals(type)) {
+ iconLabel = new JLabel();
+ iconLabel.setHorizontalAlignment(SwingConstants.CENTER);
+ iconLabel.setPreferredSize(new Dimension(80, 80));
+ String iconPath = "handheld".equals(type) ? "image/URT.png" : "image/mow.png";
+ 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));
+ } else {
+ iconLabel.setText(icon);
+ iconLabel.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 48));
+ }
+ } else {
+ iconLabel = new JLabel(icon, JLabel.CENTER);
+ iconLabel.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 48));
+ iconLabel.setPreferredSize(new Dimension(80, 80));
+ }
+
+ // 鏂囨湰鍖哄煙
+ JPanel textPanel = new JPanel(new GridBagLayout());
+ textPanel.setBackground(WHITE);
+
+ JLabel textLabel = new JLabel(text);
+ textLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
+ textLabel.setHorizontalAlignment(SwingConstants.CENTER);
+ textLabel.setVerticalAlignment(SwingConstants.CENTER);
+
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.gridx = 0;
+ gbc.gridy = 0;
+ gbc.weightx = 1;
+ gbc.weighty = description == null || description.trim().isEmpty() ? 1 : 0;
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ gbc.anchor = GridBagConstraints.CENTER;
+ textPanel.add(textLabel, gbc);
+
+ if (description != null && !description.trim().isEmpty()) {
+ JLabel descLabel = new JLabel(description);
+ descLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
+ descLabel.setForeground(LIGHT_TEXT);
+ descLabel.setHorizontalAlignment(SwingConstants.CENTER);
+
+ GridBagConstraints descGbc = new GridBagConstraints();
+ descGbc.gridx = 0;
+ descGbc.gridy = 1;
+ descGbc.weightx = 1;
+ descGbc.weighty = 1;
+ descGbc.fill = GridBagConstraints.HORIZONTAL;
+ descGbc.anchor = GridBagConstraints.CENTER;
+ textPanel.add(descLabel, descGbc);
+ }
+
+ optionPanel.putClientProperty("titleLabel", textLabel);
+
+ optionPanel.add(iconLabel, BorderLayout.WEST);
+ optionPanel.add(textPanel, BorderLayout.CENTER);
+
+ // 娣诲姞鐐瑰嚮浜嬩欢
+ optionPanel.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (!optionPanel.isEnabled()) {
+ return;
+ }
+ if (selectDrawingOption(optionPanel, type, true)) {
+ startEndDrawingBtn.setEnabled(true); // 閫夋嫨鍚庡惎鐢ㄦ寜閽�
+ // 闅愯棌鎻愮ず鏂囧瓧
+ if (drawingMethodHintLabel != null) {
+ drawingMethodHintLabel.setVisible(false);
+ }
+ }
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent e) {
+ if (optionPanel != selectedOptionPanel) {
+ optionPanel.setBackground(new Color(245, 245, 245));
+ }
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ if (optionPanel != selectedOptionPanel) {
+ optionPanel.setBackground(WHITE);
+ }
+ }
+ });
+
+ drawingOptionPanels.put(type, optionPanel);
+ return optionPanel;
+ }
+
+ private boolean selectDrawingOption(JPanel optionPanel, String type, boolean userTriggered) {
+ if (optionPanel == null) {
+ return false;
+ }
+ if (userTriggered && "handheld".equalsIgnoreCase(type) && !hasConfiguredHandheldMarker()) {
+ JOptionPane.showMessageDialog(this, "璇峰厛鍘荤郴缁熻缃坊鍔犱究鎼烘墦鐐瑰櫒缂栧彿", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return false;
+ }
+
+ // 閲嶇疆涔嬪墠閫変腑鐨勯�夐」
+ if (selectedOptionPanel != null) {
+ selectedOptionPanel.setBorder(BorderFactory.createLineBorder(BORDER_COLOR, 2));
+ selectedOptionPanel.setBackground(WHITE);
+ Object oldTitle = selectedOptionPanel.getClientProperty("titleLabel");
+ if (oldTitle instanceof JLabel) {
+ ((JLabel) oldTitle).setForeground(TEXT_COLOR);
+ }
+ }
+
+ // 璁剧疆鏂扮殑閫変腑鐘舵��
+ optionPanel.setBorder(BorderFactory.createLineBorder(PRIMARY_COLOR, 3));
+ optionPanel.setBackground(PRIMARY_LIGHT);
+ Object titleObj = optionPanel.getClientProperty("titleLabel");
+ if (titleObj instanceof JLabel) {
+ ((JLabel) titleObj).setForeground(PRIMARY_COLOR);
+ }
+ selectedOptionPanel = optionPanel;
+
+ return true;
+ }
+
+ private boolean hasConfiguredHandheldMarker() {
+ String handheldId = Setsys.getPropertyValue("handheldMarkerId");
+ return handheldId != null && !handheldId.trim().isEmpty();
+ }
+
+ private void toggleDrawing() {
+ if (!isDrawing) {
+ // 妫�鏌ユ槸鍚﹂�夋嫨浜嗙粯鍒舵柟寮�
+ if (selectedOptionPanel == null) {
+ // 鏄剧ず鎻愮ず鏂囧瓧
+ if (drawingMethodHintLabel != null) {
+ drawingMethodHintLabel.setVisible(true);
+ }
+ return;
+ } else {
+ // 闅愯棌鎻愮ず鏂囧瓧
+ if (drawingMethodHintLabel != null) {
+ drawingMethodHintLabel.setVisible(false);
+ }
+ }
+
+ // 濡傛灉鏄噸鏂扮粯鍒讹紝娓呯┖涔嬪墠鐨勮矾寰勭偣
+ if (hasDrawnPath || isRefreshMode) {
+ int confirm = JOptionPane.showConfirmDialog(this,
+ "閲嶆柊缁樺埗灏嗘竻绌轰箣鍓嶇殑璺緞鐐癸紝鏄惁缁х画锛�",
+ "纭",
+ JOptionPane.YES_NO_OPTION);
+ if (confirm != JOptionPane.YES_OPTION) {
+ return;
+ }
+ // 娓呯┖涔嬪墠鐨勫潗鏍�
+ Coordinate.coordinates.clear();
+ lujing.SavaXyZuobiao.clearCoordinates(); // 娓呯┖宸ュ叿绫诲潗鏍�
+ hasDrawnPath = false;
+ isRefreshMode = false;
+ optimizedPathCoordinates = null;
+ // 闅愯棌鎿嶄綔鎸夐挳
+ if (actionButtonsPanel != null) {
+ actionButtonsPanel.setVisible(false);
+ }
+ // 闅愯棌鍘熷鍧愭爣鍜岃绠楀悗鍧愭爣
+ if (rawCoordinatesArea != null && rawCoordinatesArea.getParent() instanceof JViewport) {
+ rawCoordinatesArea.getParent().getParent().setVisible(false);
+ rawCoordinatesArea.setText("");
+ }
+ if (optimizedCoordinatesArea != null && optimizedCoordinatesArea.getParent() instanceof JViewport) {
+ optimizedCoordinatesArea.getParent().getParent().setVisible(false);
+ optimizedCoordinatesArea.setText("");
+ }
+ // 閲嶇疆淇濆瓨鎸夐挳鐘舵��
+ if (saveBtn != null) {
+ saveBtn.setEnabled(false);
+ }
+ hidePathPointSummary();
+ }
+
+ if (!prepareDrawingSession()) {
+ return;
+ }
+ isDrawing = true;
+ // hidePathPointSummary();
+ Coordinate.setStartSaveGngga(true);
+ lujing.SavaXyZuobiao.startSaving(); // 寮�濮嬩繚瀛樺潗鏍�
+ startEndDrawingBtn.setText("缁撴潫缁樺埗");
+ startEndDrawingBtn.setBackground(ERROR_COLOR);
+ updateOtherOptionsState(true);
+
+ if (!startDrawingPath()) {
+ Coordinate.setStartSaveGngga(false);
+ resetDrawingState();
+ }
+ } else {
+ // 鐢ㄦ埛鍦ㄥ璇濇鍐呬富鍔ㄧ粨鏉�
+ isDrawing = false;
+ Coordinate.setStartSaveGngga(false);
+ startEndDrawingBtn.setText(hasDrawnPath ? "閲嶆柊缁樺埗" : "寮�濮嬬粯鍒�");
+ startEndDrawingBtn.setBackground(PRIMARY_COLOR);
+ updateOtherOptionsState(false);
+ JOptionPane.showMessageDialog(this, "寰�杩旇矾寰勭粯鍒跺凡瀹屾垚", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+ // showPathPointSummary();
+ // 鏄剧ず鎿嶄綔鎸夐挳
+ if (actionButtonsPanel != null) {
+ actionButtonsPanel.setVisible(true);
+ }
+ // 閲嶇疆淇濆瓨鎸夐挳鐘舵�侊紙闇�瑕佺敓鎴愯矾寰勫悗鎵嶈兘淇濆瓨锛�
+ if (saveBtn != null) {
+ saveBtn.setEnabled(false);
+ }
+ optimizedPathCoordinates = null; // 娓呯┖浼樺寲璺緞
+ hasDrawnPath = true;
+ }
+ }
+
+ private boolean prepareDrawingSession() {
+ if (currentDikuai == null) {
+ JOptionPane.showMessageDialog(this, "鍦板潡淇℃伅涓嶅瓨鍦�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return false;
+ }
+ if (selectedOptionPanel == null) {
+ JOptionPane.showMessageDialog(this, "璇烽�夋嫨缁樺埗鏂瑰紡", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return false;
+ }
+ // 娓呯┖涔嬪墠鐨勫潗鏍�
+ Coordinate.coordinates.clear();
+ return true;
+ }
+
+ private void updateOtherOptionsState(boolean disable) {
+ if (selectedOptionPanel == null) {
+ return;
+ }
+ Component[] components = selectedOptionPanel.getParent().getComponents();
+ for (Component comp : components) {
+ if (comp instanceof JPanel && comp != selectedOptionPanel) {
+ comp.setEnabled(!disable);
+ ((JPanel) comp).setBackground(disable ? LIGHT_GRAY : WHITE);
+ }
+ }
+ }
+
+ private void resetDrawingState() {
+ isDrawing = false;
+ Coordinate.setStartSaveGngga(false);
+ startEndDrawingBtn.setText("寮�濮嬬粯鍒�");
+ startEndDrawingBtn.setBackground(PRIMARY_COLOR);
+ updateOtherOptionsState(false);
+ // hidePathPointSummary();
+ }
+
+ private boolean startDrawingPath() {
+ // 鑾峰彇閫変腑鐨勭粯鍒舵柟寮�
+ String method = null;
+ for (Map.Entry<String, JPanel> entry : drawingOptionPanels.entrySet()) {
+ if (entry.getValue() == selectedOptionPanel) {
+ method = entry.getKey();
+ break;
+ }
+ }
+
+ if ("mower".equals(method) || "handheld".equals(method)) {
+ zhuye.Shouye shouye = zhuye.Shouye.getInstance();
+ if (shouye == null) {
+ JOptionPane.showMessageDialog(this, "鏃犳硶杩涘叆涓婚〉闈紝璇风◢鍚庨噸璇�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return false;
+ }
+
+ // 鍒涘缓瀹屾垚缁樺埗鍥炶皟锛岃繑鍥炲埌缁樺埗椤甸潰
+ Runnable finishCallback = () -> {
+ SwingUtilities.invokeLater(() -> {
+ // 鍋滄淇濆瓨鍧愭爣
+ lujing.SavaXyZuobiao.pauseSaving();
+
+ // 鎭㈠棣栭〉榛樿鐨勬殏鍋滃拰缁撴潫鎸夐挳
+ // 杩欎釜鍦� stopReturnPathDrawing 涓凡缁忓鐞嗕簡
+
+ // 鏄剧ず缁樺埗椤甸潰
+ Window owner = SwingUtilities.getWindowAncestor(this);
+ if (owner == null) {
+ owner = (Window) SwingUtilities.getWindowAncestor(shouye);
+ }
+ if (owner != null) {
+ setLocationRelativeTo(owner);
+ }
+
+ // 閲嶇疆缁樺埗鐘舵��
+ isDrawing = false;
+ hasDrawnPath = true; // 鏍囪宸茬粯鍒惰繃璺緞
+ startEndDrawingBtn.setText("閲嶆柊缁樺埗");
+ startEndDrawingBtn.setBackground(PRIMARY_COLOR);
+ startEndDrawingBtn.setEnabled(true); // 淇濇寔鍚敤鐘舵��
+ updateOtherOptionsState(false);
+
+ // 鏄剧ず鍘熷鍧愭爣
+ if (rawCoordinatesArea != null) {
+ String rawCoords = lujing.SavaXyZuobiao.getCoordinatesString();
+ rawCoordinatesArea.setText(rawCoords);
+ if (rawCoordinatesArea.getParent() instanceof JViewport) {
+ rawCoordinatesArea.getParent().getParent().setVisible(true);
+ }
+ if (rawCoordinatesLabel != null) {
+ int count = 0;
+ if (rawCoords != null && !rawCoords.trim().isEmpty()) {
+ count = rawCoords.split(";").length;
+ }
+ rawCoordinatesLabel.setText("璺緞鍘熷鍧愭爣 (鍏�" + count + "涓偣)");
+ rawCoordinatesLabel.setVisible(true);
+ }
+ }
+
+ // 鏄剧ず鎿嶄綔鎸夐挳
+ if (actionButtonsPanel != null) {
+ actionButtonsPanel.setVisible(true);
+ }
+ // 閲嶇疆淇濆瓨鎸夐挳鐘舵�侊紙闇�瑕佺敓鎴愯矾寰勫悗鎵嶈兘淇濆瓨锛�
+ if (saveBtn != null) {
+ saveBtn.setEnabled(false);
+ }
+ optimizedPathCoordinates = null; // 娓呯┖浼樺寲璺緞
+
+ // 寮哄埗鍒锋柊甯冨眬
+ if (mainPanel != null) {
+ mainPanel.revalidate();
+ mainPanel.repaint();
+ }
+
+ setVisible(true);
+
+ // 鏇存柊璺緞鐐硅鏁�
+ // showPathPointSummary();
+
+ // JOptionPane.showMessageDialog(this, "寰�杩旇矾寰勭粯鍒跺凡瀹屾垚", "鎻愮ず", JOptionPane.INFORMATION_MESSAGE);
+ });
+ };
+
+ // 鍚姩寰�杩旇矾寰勭粯鍒�
+ if (!shouye.startReturnPathDrawing(finishCallback)) {
+ JOptionPane.showMessageDialog(this, "鏈兘寮�濮嬬粯鍒讹紝璇风‘璁よ澶囩姸鎬佸拰鍩哄噯绔欒缃悗閲嶈瘯", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return false;
+ }
+
+ // 鏄剧ず棣栭〉鐨勨�滅粨鏉熺粯鍒垛�濇偓娴寜閽紝骞剁粦瀹氱粨鏉熼�昏緫
+ shouye.showEndDrawingButton(() -> {
+ // 鍋滄缁樺埗骞舵墽琛屽畬鎴愬洖璋冿紙鎭㈠鍒版湰椤甸潰骞舵樉绀轰繚瀛�/棰勮绛夛級
+ shouye.stopReturnPathDrawing();
+ zhuye.WangfanDraw drawer = shouye.getReturnPathDrawer();
+ if (drawer != null) {
+ drawer.executeFinishCallback();
+ }
+ // 鍏抽棴鎮诞鎺у埗
+ shouye.hideEndDrawingButton();
+ });
+
+ setVisible(false);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 淇濆瓨璺緞鍧愭爣锛堜繚瀛樹紭鍖栧悗鐨勫潗鏍囷紝濡傛灉娌℃湁浼樺寲鍒欎繚瀛樺師濮嬪潗鏍囷級
+ */
+ private void savePathCoordinates() {
+ if (currentDikuai == null) {
+ return;
+ }
+
+ // 濡傛灉宸茬粡鐢熸垚浼樺寲璺緞锛屼娇鐢ㄤ紭鍖栧悗鐨勫潗鏍�
+ String pathCoordinates = optimizedPathCoordinates;
+ if (pathCoordinates == null || pathCoordinates.trim().isEmpty()) {
+ // 濡傛灉娌℃湁浼樺寲璺緞锛屼娇鐢ㄥ師濮嬪潗鏍囩偣锛圶,Y鏍煎紡锛屽崟浣嶇背锛�
+ pathCoordinates = buildXYCoordinatesString();
+ }
+
+ if (pathCoordinates == null || pathCoordinates.trim().isEmpty() || "-1".equals(pathCoordinates.trim())) {
+ JOptionPane.showMessageDialog(this, "娌℃湁鍙繚瀛樼殑璺緞鍧愭爣", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
+ // 淇濆瓨鍒板湴鍧�
+ if (Dikuai.updateField(currentDikuai.getLandNumber(), "returnPathCoordinates", pathCoordinates)) {
+ // 鍚屾椂淇濆瓨鍘熷鍧愭爣
+ String rawCoords = rawCoordinatesArea != null ? rawCoordinatesArea.getText() : "";
+ if (rawCoords != null && !rawCoords.trim().isEmpty()) {
+ Dikuai.updateField(currentDikuai.getLandNumber(), "returnPathRawCoordinates", rawCoords);
+ }
+
+ Dikuai.updateField(currentDikuai.getLandNumber(), "updateTime", getCurrentTime());
+ Dikuai.saveToProperties();
+
+ // 娓呯┖宸ュ叿绫诲潗鏍�
+ lujing.SavaXyZuobiao.clearCoordinates();
+
+ JOptionPane.showMessageDialog(this, "璺緞宸蹭繚瀛�", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+ } else {
+ JOptionPane.showMessageDialog(this, "淇濆瓨澶辫触", "閿欒", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ /**
+ * 鏋勫缓X,Y鍧愭爣瀛楃涓诧紙鍗曚綅绫筹紝绮剧‘鍒板皬鏁扮偣鍚�2浣嶏級
+ */
+ private String buildXYCoordinatesString() {
+ // 浠� WangfanDraw 鑾峰彇璺緞鐐癸紙宸茬粡鏄疿,Y鍧愭爣锛屽崟浣嶇背锛�
+ zhuye.Shouye shouye = zhuye.Shouye.getInstance();
+ if (shouye == null || shouye.getReturnPathDrawer() == null) {
+ return "-1";
+ }
+
+ List<java.awt.geom.Point2D.Double> points = shouye.getReturnPathDrawer().getPointsSnapshot();
+ if (points == null || points.isEmpty()) {
+ return "-1";
+ }
+
+ StringBuilder sb = new StringBuilder();
+ java.text.DecimalFormat xyFormat = new java.text.DecimalFormat("0.00");
+
+ for (java.awt.geom.Point2D.Double point : points) {
+ if (point == null) continue;
+
+ if (sb.length() > 0) {
+ sb.append(";");
+ }
+ sb.append(xyFormat.format(point.x)).append(",")
+ .append(xyFormat.format(point.y));
+ }
+
+ return sb.length() > 0 ? sb.toString() : "-1";
+ }
+
+ private double convertToDecimalDegree(String dmm, String direction) {
+ if (dmm == null || dmm.isEmpty()) {
+ return 0.0;
+ }
+ try {
+ int dotIndex = dmm.indexOf('.');
+ if (dotIndex == -1) {
+ return 0.0;
+ }
+ int degrees = Integer.parseInt(dmm.substring(0, dotIndex - 2));
+ double minutes = Double.parseDouble(dmm.substring(dotIndex - 2));
+ double decimal = degrees + minutes / 60.0;
+ if ("S".equals(direction) || "W".equals(direction)) {
+ decimal = -decimal;
+ }
+ return decimal;
+ } catch (Exception e) {
+ return 0.0;
+ }
+ }
+
+ private String getCurrentTime() {
+ java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ return sdf.format(new java.util.Date());
+ }
+
+ private JPanel createInstructionPanel(String text) {
+ JPanel instructionPanel = new JPanel(new BorderLayout());
+ instructionPanel.setBackground(PRIMARY_LIGHT);
+ instructionPanel.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createMatteBorder(0, 5, 0, 0, PRIMARY_COLOR),
+ BorderFactory.createEmptyBorder(12, 12, 12, 12)
+ ));
+ instructionPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 70));
+
+ JLabel iconLabel = new JLabel("馃挕");
+ iconLabel.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 16));
+ iconLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
+
+ JTextArea instructionText = new JTextArea(text);
+ instructionText.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
+ instructionText.setForeground(TEXT_COLOR);
+ instructionText.setBackground(PRIMARY_LIGHT);
+ instructionText.setLineWrap(true);
+ instructionText.setWrapStyleWord(true);
+ instructionText.setEditable(false);
+
+ instructionPanel.add(iconLabel, BorderLayout.WEST);
+ instructionPanel.add(instructionText, BorderLayout.CENTER);
+
+ return instructionPanel;
+ }
+
+ private JButton createPrimaryButton(String text, int fontSize) {
+ JButton button = buttonset.createStyledButton(text, PRIMARY_COLOR);
+ button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, fontSize));
+ button.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createLineBorder(PRIMARY_DARK, 2),
+ BorderFactory.createEmptyBorder(12, 25, 12, 25)
+ ));
+ button.setCursor(new Cursor(Cursor.HAND_CURSOR));
+
+ 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 void showPathPointSummary() {
+ if (pathCountLabel == null) {
+ return;
+ }
+ int count = Coordinate.coordinates != null ? Coordinate.coordinates.size() : 0;
+ pathCountLabel.setText("宸查噰闆嗗埌鏈夋晥鍧愭爣鐐�" + count + "涓�");
+ // pathCountLabel.setVisible(true);
+ }
+
+ private void hidePathPointSummary() {
+ if (pathCountLabel != null) {
+ // pathCountLabel.setVisible(false);
+ }
+ }
+
+ /**
+ * 鍒涘缓鎿嶄綔鎸夐挳闈㈡澘锛堢敓鎴愯矾寰勩�侀瑙堛�佷繚瀛橈級
+ */
+ private JPanel createActionButtonsPanel() {
+ JPanel panel = new JPanel();
+ panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
+ panel.setBackground(WHITE);
+ panel.setBorder(BorderFactory.createEmptyBorder(15, 0, 0, 0));
+ panel.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ // 鐢熸垚璺緞鎸夐挳
+ generatePathBtn = createPrimaryButton("鐢熸垚璺緞", 16);
+ generatePathBtn.setAlignmentX(Component.LEFT_ALIGNMENT);
+ generatePathBtn.addActionListener(e -> generatePath());
+
+ // 棰勮鎸夐挳
+ previewBtn = createPrimaryButton("棰勮", 16);
+ previewBtn.setAlignmentX(Component.LEFT_ALIGNMENT);
+ previewBtn.addActionListener(e -> previewPath());
+
+ // 淇濆瓨鎸夐挳锛堝垵濮嬩笉鍙敤锛�
+ saveBtn = createPrimaryButton("淇濆瓨", 16);
+ saveBtn.setAlignmentX(Component.LEFT_ALIGNMENT);
+ saveBtn.setEnabled(false); // 鍒濆涓嶅彲鐢紝鐢熸垚璺緞鍚庢墠鍙偣鍑�
+ saveBtn.addActionListener(e -> savePath());
+
+ panel.add(generatePathBtn);
+ panel.add(Box.createHorizontalStrut(15));
+ panel.add(previewBtn);
+ panel.add(Box.createHorizontalStrut(15));
+ panel.add(saveBtn);
+
+ return panel;
+ }
+
+ /**
+ * 鐢熸垚璺緞
+ */
+ private void generatePath() {
+ // 鑾峰彇璺緞鐐癸紙浠庡師濮嬪潗鏍囨枃鏈煙鑾峰彇锛�
+ String pathStr = rawCoordinatesArea != null ? rawCoordinatesArea.getText() : "";
+ if (pathStr == null || pathStr.trim().isEmpty()) {
+ JOptionPane.showMessageDialog(this, "娌℃湁鍙敤鐨勮矾寰勭偣锛岃鍏堝畬鎴愮粯鍒�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
+ try {
+ // 璋冪敤 WangfanpathJisuan 浼樺寲璺緞
+ lujing.WangfanpathJisuan calculator = new lujing.WangfanpathJisuan();
+ // 璁剧疆杈撳嚭绮惧害涓�2浣嶅皬鏁�
+ lujing.WangfanpathJisuan.OptimizationConfig config = new lujing.WangfanpathJisuan.OptimizationConfig();
+ config.setOutputPrecision(2);
+
+ String optimizedPath = calculator.optimizePath(pathStr, config);
+
+ if (optimizedPath == null || optimizedPath.trim().isEmpty()) {
+ JOptionPane.showMessageDialog(this, "璺緞浼樺寲澶辫触", "閿欒", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ // 淇濆瓨浼樺寲鍚庣殑璺緞鍧愭爣
+ optimizedPathCoordinates = optimizedPath;
+
+ // 鏄剧ず璁$畻鍚庡潗鏍�
+ if (optimizedCoordinatesArea != null) {
+ optimizedCoordinatesArea.setText(optimizedPath);
+ if (optimizedCoordinatesArea.getParent() instanceof JViewport) {
+ optimizedCoordinatesArea.getParent().getParent().setVisible(true);
+ }
+ if (optimizedCoordinatesLabel != null) {
+ int count = 0;
+ if (optimizedPath != null && !optimizedPath.trim().isEmpty()) {
+ count = optimizedPath.split(";").length;
+ }
+ optimizedCoordinatesLabel.setText("浼樺寲鍚庤矾寰勫潗鏍� (鍏�" + count + "涓偣)");
+ optimizedCoordinatesLabel.setVisible(true);
+ }
+ }
+
+ // 鍚敤淇濆瓨鎸夐挳
+ if (saveBtn != null) {
+ saveBtn.setEnabled(true);
+ }
+
+ // 鍒锋柊鐣岄潰
+ if (mainPanel != null) {
+ mainPanel.revalidate();
+ mainPanel.repaint();
+ }
+ } catch (Exception e) {
+ JOptionPane.showMessageDialog(this, "璺緞鐢熸垚澶辫触: " + e.getMessage(), "閿欒", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ /**
+ * 棰勮璺緞
+ */
+ private void previewPath() {
+ if (optimizedPathCoordinates == null || optimizedPathCoordinates.trim().isEmpty()) {
+ JOptionPane.showMessageDialog(this, "璇峰厛鐢熸垚璺緞", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
+ zhuye.Shouye shouye = zhuye.Shouye.getInstance();
+ if (shouye != null) {
+ // 闅愯棌褰撳墠绐楀彛
+ setVisible(false);
+
+ // 鍚姩棰勮
+ shouye.startReturnPathPreview(optimizedPathCoordinates, () -> {
+ // 杩斿洖鍥炶皟锛氭樉绀哄綋鍓嶇獥鍙�
+ setVisible(true);
+ });
+ }
+ }
+
+ /**
+ * 淇濆瓨璺緞
+ */
+ private void savePath() {
+ if (optimizedPathCoordinates == null || optimizedPathCoordinates.trim().isEmpty()) {
+ JOptionPane.showMessageDialog(this, "璇峰厛鐢熸垚璺緞", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+ savePathCoordinates();
+ }
+
+ /**
+ * 鏄剧ず缁樺埗寰�杩旇矾寰勫璇濇
+ */
+ public static void showDrawReturnPathDialog(Window parent, Dikuai dikuai) {
+ showDrawReturnPathDialog(parent, dikuai, false);
+ }
+
+ /**
+ * 鏄剧ず缁樺埗寰�杩旇矾寰勫璇濇
+ * @param parent 鐖剁獥鍙�
+ * @param dikuai 鍦板潡淇℃伅
+ * @param isRefresh 鏄惁閲嶆柊缁樺埗锛堝鏋滃凡鏈夊潗鏍囷級
+ */
+ public static void showDrawReturnPathDialog(Window parent, Dikuai dikuai, boolean isRefresh) {
+ Huizhiwanfanpath dialog = new Huizhiwanfanpath(parent, dikuai, isRefresh);
+ dialog.setVisible(true);
+ }
+}
+
diff --git a/src/dikuai/ObstacleManagementPage.java b/src/dikuai/ObstacleManagementPage.java
index 96c801a..45e5d7e 100644
--- a/src/dikuai/ObstacleManagementPage.java
+++ b/src/dikuai/ObstacleManagementPage.java
@@ -20,6 +20,8 @@
/**
* 闅滅鐗╃鐞嗛〉闈� - UI浼樺寲鐗�
*/
+import publicway.Gpstoxuzuobiao;
+
public class ObstacleManagementPage extends JDialog {
private static final long serialVersionUID = 1L;
@@ -904,29 +906,11 @@
}
private double parseDMToDecimal(String dmm, String direction) {
- if (dmm == null || dmm.trim().isEmpty()) return Double.NaN;
- try {
- String trimmed = dmm.trim();
- int dotIndex = trimmed.indexOf('.');
- if (dotIndex < 2) return Double.NaN;
- int degrees = Integer.parseInt(trimmed.substring(0, dotIndex - 2));
- double minutes = Double.parseDouble(trimmed.substring(dotIndex - 2));
- double decimal = degrees + minutes / 60.0;
- if ("S".equalsIgnoreCase(direction) || "W".equalsIgnoreCase(direction)) decimal = -decimal;
- return decimal;
- } catch (NumberFormatException ex) {
- return Double.NaN;
- }
+ return Gpstoxuzuobiao.parseDMToDecimal(dmm, direction);
}
private double[] convertLatLonToLocal(double lat, double lon, double baseLat, double baseLon) {
- double deltaLat = lat - baseLat;
- double deltaLon = lon - baseLon;
- double meanLatRad = Math.toRadians((baseLat + lat) / 2.0);
- double METERS_PER_DEGREE_LAT = 111320.0;
- double eastMeters = deltaLon * METERS_PER_DEGREE_LAT * Math.cos(meanLatRad);
- double northMeters = deltaLat * METERS_PER_DEGREE_LAT;
- return new double[]{eastMeters, northMeters};
+ return Gpstoxuzuobiao.convertLatLonToLocal(lat, lon, baseLat, baseLon);
}
private void deleteObstacle(Obstacledge.Obstacle obstacle) {
diff --git a/src/dikuai/Wangfanpathpage.java b/src/dikuai/Wangfanpathpage.java
new file mode 100644
index 0000000..d7f53f0
--- /dev/null
+++ b/src/dikuai/Wangfanpathpage.java
@@ -0,0 +1,200 @@
+package dikuai;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import publicway.Fuzhibutton;
+import zhuye.Shouye;
+
+/**
+ * 鏌ョ湅/缂栬緫寰�杩旂偣璺緞椤甸潰
+ * 鐙珛鐨勫璇濇绫伙紝鐢ㄤ簬鏄剧ず鍜岀紪杈戝線杩旇矾寰�
+ */
+public class Wangfanpathpage extends JDialog {
+ private static final long serialVersionUID = 1L;
+
+ // 灏哄甯搁噺 - 涓庡湴鍧楃鐞嗛〉闈繚鎸佷竴鑷�
+ private static final int SCREEN_WIDTH = 400;
+ private static final int SCREEN_HEIGHT = 800;
+
+ // 棰滆壊甯搁噺
+ private static final Color PRIMARY_COLOR = new Color(46, 139, 87);
+ private static final Color PRIMARY_DARK = new Color(30, 107, 69);
+ private static final Color TEXT_COLOR = new Color(51, 51, 51);
+ private static final Color WHITE = Color.WHITE;
+ private static final Color BORDER_COLOR = new Color(200, 200, 200);
+ private static final Color BACKGROUND_COLOR = new Color(250, 250, 250);
+
+ private String result = null;
+
+ public Wangfanpathpage(Window owner, String title, String initialValue, Dikuai dikuai) {
+ super(owner, title, Dialog.ModalityType.APPLICATION_MODAL);
+ initializeUI(title, initialValue, dikuai);
+ }
+
+ public String getResult() {
+ return result;
+ }
+
+ private void initializeUI(String title, String initialValue, Dikuai dikuai) {
+ setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ getContentPane().setLayout(new BorderLayout());
+ getContentPane().setBackground(BACKGROUND_COLOR);
+
+ JPanel contentPanel = new JPanel();
+ contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS));
+ contentPanel.setBackground(BACKGROUND_COLOR);
+ contentPanel.setBorder(BorderFactory.createEmptyBorder(12, 16, 12, 16));
+
+ // 鏍囬
+ String landName = dikuai != null ? (dikuai.getLandName() != null ? dikuai.getLandName() : "鏈煡鍦板潡") : "鏈煡鍦板潡";
+ String landNumber = dikuai != null ? (dikuai.getLandNumber() != null ? dikuai.getLandNumber() : "鏈煡缂栧彿") : "鏈煡缂栧彿";
+ JLabel headerLabel = new JLabel(landName + " / " + landNumber);
+ headerLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
+ headerLabel.setForeground(TEXT_COLOR);
+ headerLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ contentPanel.add(headerLabel);
+ contentPanel.add(Box.createVerticalStrut(12));
+
+ // 1. 鍘熷寰�杩旇矾寰勫潗鏍囧尯鍩�
+ String rawCoords = dikuai != null ? prepareCoordinateForEditor(dikuai.getReturnPathRawCoordinates()) : "";
+ int rawCount = 0;
+ if (rawCoords != null && !rawCoords.isEmpty() && !"-1".equals(rawCoords)) {
+ rawCount = rawCoords.split(";").length;
+ }
+ JTextArea rawTextArea = createInfoTextArea(rawCoords, false, 4);
+ contentPanel.add(createTextAreaSection("鍘熷寰�杩旇矾寰勫潗鏍� (" + rawCount + "鐐�)", rawTextArea));
+
+ // 2. 浼樺寲鍚庡線杩旇矾寰勫潗鏍囧尯鍩�
+ String optCoords = prepareCoordinateForEditor(initialValue);
+ int optCount = 0;
+ if (optCoords != null && !optCoords.isEmpty() && !"-1".equals(optCoords)) {
+ optCount = optCoords.split(";").length;
+ }
+ JTextArea optTextArea = createInfoTextArea(optCoords, true, 4);
+ contentPanel.add(createTextAreaSection("浼樺寲鍚庡線杩旇矾寰勫潗鏍� (" + optCount + "鐐�)", optTextArea));
+
+ JScrollPane dialogScrollPane = new JScrollPane(contentPanel);
+ dialogScrollPane.setBorder(BorderFactory.createEmptyBorder());
+ dialogScrollPane.getVerticalScrollBar().setUnitIncrement(16);
+ add(dialogScrollPane, BorderLayout.CENTER);
+
+ // 鎸夐挳闈㈡澘
+ JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 12, 12));
+ buttonPanel.setBackground(BACKGROUND_COLOR);
+
+ JButton goDrawBtn = createPrimaryFooterButton("鍘荤粯鍒�");
+ JButton closeBtn = createPrimaryFooterButton("鍏抽棴");
+
+ goDrawBtn.addActionListener(e -> {
+ String currentText = optTextArea.getText();
+ if (currentText != null && !currentText.trim().isEmpty() && !"-1".equals(currentText.trim())) {
+ result = "__OPEN_DRAW_PAGE_REFRESH__";
+ } else {
+ result = "__OPEN_DRAW_PAGE__";
+ }
+ dispose();
+ });
+
+ closeBtn.addActionListener(e -> dispose());
+
+ buttonPanel.add(goDrawBtn);
+ buttonPanel.add(closeBtn);
+ add(buttonPanel, BorderLayout.SOUTH);
+
+ pack();
+ setSize(SCREEN_WIDTH, SCREEN_HEIGHT); // 璁剧疆涓哄浐瀹氬昂瀵�
+ setLocationRelativeTo(getOwner());
+ }
+
+ private String prepareCoordinateForEditor(String value) {
+ if (value == null) {
+ return "";
+ }
+ String trimmed = value.trim();
+ if (trimmed.isEmpty() || "-1".equals(trimmed)) {
+ return "";
+ }
+ return trimmed;
+ }
+
+ private JTextArea createInfoTextArea(String text, boolean editable, int rows) {
+ JTextArea area = new JTextArea(text);
+ area.setEditable(editable);
+ area.setLineWrap(true);
+ area.setWrapStyleWord(true);
+ area.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
+ area.setRows(Math.max(rows, 2));
+ area.setCaretPosition(0);
+ area.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
+ area.setBackground(editable ? WHITE : new Color(245, 245, 245));
+ return area;
+ }
+
+ private JPanel createTextAreaSection(String title, JTextArea textArea) {
+ JPanel section = new JPanel(new BorderLayout(0, 6));
+ section.setBackground(BACKGROUND_COLOR);
+ section.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ // 鍒涘缓鏍囬闈㈡澘锛屽寘鍚爣棰樺拰澶嶅埗鍥炬爣
+ JPanel titlePanel = new JPanel(new BorderLayout());
+ titlePanel.setBackground(BACKGROUND_COLOR);
+ titlePanel.setOpaque(false);
+
+ JLabel titleLabel = new JLabel(title);
+ titleLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
+ titleLabel.setForeground(TEXT_COLOR);
+ titlePanel.add(titleLabel, BorderLayout.WEST);
+
+ // 鍒涘缓澶嶅埗鎸夐挳锛堜娇鐢� Fuzhibutton锛�
+ JButton copyButton = Fuzhibutton.createCopyButton(
+ () -> {
+ String text = textArea.getText();
+ if (text == null || text.trim().isEmpty() || "-1".equals(text.trim())) {
+ return null; // 杩斿洖null浼氳Е鍙�"鏈缃�"鎻愮ず
+ }
+ return text;
+ },
+ "澶嶅埗", // 绠�鍖栨寜閽枃瀛楋紝鍙樉绀�"澶嶅埗"
+ new Color(230, 250, 240)
+ );
+ // 璋冩暣澶嶅埗鎸夐挳鏍峰紡浠ュ尮閰�
+ copyButton.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+ copyButton.setPreferredSize(new Dimension(50, 24));
+ copyButton.setMargin(new Insets(0,0,0,0));
+
+ titlePanel.add(copyButton, BorderLayout.EAST);
+
+ section.add(titlePanel, BorderLayout.NORTH);
+
+ JScrollPane scrollPane = new JScrollPane(textArea);
+ scrollPane.setBorder(BorderFactory.createLineBorder(BORDER_COLOR));
+ scrollPane.getVerticalScrollBar().setUnitIncrement(12);
+ section.add(scrollPane, BorderLayout.CENTER);
+
+ section.setBorder(BorderFactory.createEmptyBorder(4, 0, 12, 0));
+ return section;
+ }
+
+ private JButton createPrimaryFooterButton(String text) {
+ JButton button = new JButton(text);
+ button.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 12));
+ button.setBackground(PRIMARY_COLOR);
+ button.setForeground(WHITE);
+ button.setBorder(BorderFactory.createEmptyBorder(6, 12, 6, 12));
+ button.setFocusPainted(false);
+ button.setCursor(new Cursor(Cursor.HAND_CURSOR));
+
+ button.addMouseListener(new java.awt.event.MouseAdapter() {
+ public void mouseEntered(java.awt.event.MouseEvent e) {
+ button.setBackground(PRIMARY_DARK);
+ }
+
+ public void mouseExited(java.awt.event.MouseEvent e) {
+ button.setBackground(PRIMARY_COLOR);
+ }
+ });
+
+ return button;
+ }
+}
diff --git a/src/dikuai/addzhangaiwu.java b/src/dikuai/addzhangaiwu.java
index 662c5c1..1fa7376 100644
--- a/src/dikuai/addzhangaiwu.java
+++ b/src/dikuai/addzhangaiwu.java
@@ -46,6 +46,7 @@
import baseStation.BaseStation;
import gecaoji.Device;
+import publicway.buttonset;
import set.Setsys;
import ui.UIConfig;
import zhuye.Coordinate;
@@ -54,13 +55,14 @@
import zhangaiwu.AddDikuai;
import zhangaiwu.Obstacledge;
import zhangaiwu.yulanzhangaiwu;
-import zhuye.buttonset;
import bianjie.bianjieguihua2;
import bianjie.ThreePointCircle;
/**
* 闅滅鐗╂柊澧�/缂栬緫瀵硅瘽妗嗐�傝璁¤瑷�鍙傝�� {@link AddDikuai}锛屾敮鎸侀�氳繃瀹炲湴缁樺埗閲囬泦闅滅鐗╁潗鏍囥��
*/
+import publicway.Gpstoxuzuobiao;
+
public class addzhangaiwu extends JDialog {
private static final long serialVersionUID = 1L;
private static final double METERS_PER_DEGREE_LAT = 111320.0d;
@@ -1217,34 +1219,11 @@
}
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;
- }
+ return Gpstoxuzuobiao.parseDMToDecimal(dmm, direction);
}
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};
+ return Gpstoxuzuobiao.convertLatLonToLocal(lat, lon, baseLat, baseLon);
}
private static CircleFitResult fitCircleFromPoints(List<double[]> points) {
diff --git a/src/dikuai/daohangyulan.java b/src/dikuai/daohangyulan.java
index a65ffcf..2db9acb 100644
--- a/src/dikuai/daohangyulan.java
+++ b/src/dikuai/daohangyulan.java
@@ -9,9 +9,9 @@
import java.util.ArrayList;
import zhuye.Shouye;
import zhuye.MapRenderer;
-import zhuye.buttonset;
import gecaoji.Gecaoji;
import gecaoji.lujingdraw;
+import publicway.buttonset;
/**
* 瀵艰埅棰勮鍔熻兘绫�
diff --git a/src/gecaoji/Device.java b/src/gecaoji/Device.java
index d37e21f..97a3333 100644
--- a/src/gecaoji/Device.java
+++ b/src/gecaoji/Device.java
@@ -1,6 +1,7 @@
package gecaoji;
import baseStation.BaseStation;
import set.Setsys;
+import zhuye.MowerLocationData;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -436,6 +437,12 @@
}
positioningStatus = defaultIfEmpty(sanitizeField(fields, 6));
+ // 鍚屾鍒扮粯鍒舵ā鍧楃殑鏁版嵁婧愶紝淇濊瘉寰�杩旂粯鍒跺畾鏃跺櫒鑳借瘑鍒畾浣嶈川閲�
+ try {
+ MowerLocationData.updateProperty("positioningQuality", positioningStatus);
+ } catch (Throwable ignored) {
+ // 闃插尽寮忥細鍗充娇鏇存柊澶辫触涔熶笉褰卞搷璁惧鏁版嵁澶勭悊
+ }
satelliteCount = defaultIfEmpty(sanitizeField(fields, 7));
differentialAge = defaultIfEmpty(sanitizeField(fields, 13));
battery = defaultIfEmpty(sanitizeField(fields, 16));
@@ -485,6 +492,12 @@
}
positioningStatus = defaultIfEmpty(sanitizeField(fields, 6));
+ // 鍚屾鍒扮粯鍒舵ā鍧楃殑鏁版嵁婧愶紝淇濊瘉寰�杩旂粯鍒跺畾鏃跺櫒鑳借瘑鍒畾浣嶈川閲�
+ try {
+ MowerLocationData.updateProperty("positioningQuality", positioningStatus);
+ } catch (Throwable ignored) {
+ // 闃插尽寮忥細鍗充娇鏇存柊澶辫触涔熶笉褰卞搷璁惧鏁版嵁澶勭悊
+ }
satelliteCount = defaultIfEmpty(sanitizeField(fields, 7));
differentialAge = defaultIfEmpty(sanitizeField(fields, 13));
realtimeSpeed ="0";
@@ -546,6 +559,9 @@
if (Double.isFinite(eastMeters) && Double.isFinite(northMeters)) {
realtimeX = formatMeters(eastMeters);
realtimeY = formatMeters(northMeters);
+
+ // 淇濆瓨鍧愭爣鍒板伐鍏风被
+ lujing.SavaXyZuobiao.addCoordinate(eastMeters, northMeters);
}
}
diff --git a/src/gecaoji/GecaojiMeg.java b/src/gecaoji/GecaojiMeg.java
index a1560d2..e573211 100644
--- a/src/gecaoji/GecaojiMeg.java
+++ b/src/gecaoji/GecaojiMeg.java
@@ -1,12 +1,14 @@
package gecaoji;
import javax.swing.*;
+
+import publicway.buttonset;
+
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.SimpleDateFormat;
import java.util.Date;
-import zhuye.buttonset;
// Manages the mower info dialog to keep MapRenderer focused on rendering.
public class GecaojiMeg {
diff --git a/src/lujing/MowingPathGenerationPage.java b/src/lujing/MowingPathGenerationPage.java
index c55639b..503a1fd 100644
--- a/src/lujing/MowingPathGenerationPage.java
+++ b/src/lujing/MowingPathGenerationPage.java
@@ -16,12 +16,12 @@
import lujing.Qufenxingzhuang;
import lujing.AoxinglujingNoObstacle;
import lujing.YixinglujingNoObstacle;
+import publicway.Fuzhibutton;
import lujing.AoxinglujingHaveObstacel;
import lujing.YixinglujingHaveObstacel;
import org.locationtech.jts.geom.Coordinate;
import gecaoji.Device;
import java.util.Locale;
-import zhuye.Fuzhibutton;
/**
* 鐢熸垚鍓茶崏璺緞椤甸潰
diff --git a/src/zhuye/Fuzhibutton.java b/src/publicway/Fuzhibutton.java
similarity index 99%
rename from src/zhuye/Fuzhibutton.java
rename to src/publicway/Fuzhibutton.java
index 340977f..8f1dc10 100644
--- a/src/zhuye/Fuzhibutton.java
+++ b/src/publicway/Fuzhibutton.java
@@ -1,4 +1,4 @@
-package zhuye;
+package publicway;
import javax.swing.*;
import java.awt.*;
diff --git a/src/publicway/Gpstoxuzuobiao.java b/src/publicway/Gpstoxuzuobiao.java
new file mode 100644
index 0000000..072d319
--- /dev/null
+++ b/src/publicway/Gpstoxuzuobiao.java
@@ -0,0 +1,141 @@
+package publicway;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+public class Gpstoxuzuobiao {
+ private static final double METERS_PER_DEGREE_LAT = 111320.0d;
+
+ // 缂撳瓨鍩哄噯绔欏潗鏍�
+ private static double cachedBaseLat = 0.0;
+ private static double cachedBaseLon = 0.0;
+ private static boolean baseStationLoaded = false;
+
+ /**
+ * 瑙f瀽GNGGA鏁版嵁骞惰浆鎹负XY鍧愭爣
+ * @param gnggaData $GNGGA鏁版嵁
+ * @return double[]{x, y} 鐩稿鍧愭爣锛屽鏋滆В鏋愬け璐ユ垨鏃犲熀绔欐暟鎹繑鍥瀗ull
+ */
+ public static double[] processGNGGAToXY(String gnggaData) {
+ ensureBaseStationLoaded();
+ if (!baseStationLoaded) {
+ return null;
+ }
+ return processGNGGAToXY(gnggaData, cachedBaseLat, cachedBaseLon);
+ }
+
+ /**
+ * 瑙f瀽GNGGA鏁版嵁骞惰浆鎹负XY鍧愭爣
+ * @param gnggaData $GNGGA鏁版嵁
+ * @param baseLat 鍩哄噯绔欑含搴�
+ * @param baseLon 鍩哄噯绔欑粡搴�
+ * @return double[]{x, y} 鐩稿鍧愭爣
+ */
+ public static double[] processGNGGAToXY(String gnggaData, double baseLat, double baseLon) {
+ if (gnggaData == null || !gnggaData.contains("$GNGGA")) {
+ return null;
+ }
+
+ // 绠�鍗曠殑瑙f瀽閫昏緫锛屾彁鍙栫粡绾害
+ // 鏍煎紡: $GNGGA,hhmmss.ss,lat,latDir,lon,lonDir,quality,sats,hdop,alt,units,sep,units,age,refID*cs
+
+ String[] parts = gnggaData.split(",");
+ // 鎵惧埌$GNGGA鐨勪綅缃�
+ int index = -1;
+ for(int i=0; i<parts.length; i++) {
+ if (parts[i].contains("$GNGGA")) {
+ index = i;
+ break;
+ }
+ }
+
+ // 纭繚鏈夎冻澶熺殑瀛楁: lat(2), latDir(3), lon(4), lonDir(5)
+ if (index == -1 || index + 5 >= parts.length) {
+ return null;
+ }
+
+ String latStr = parts[index + 2];
+ String latDir = parts[index + 3];
+ String lonStr = parts[index + 4];
+ String lonDir = parts[index + 5];
+
+ if (latStr.isEmpty() || lonStr.isEmpty()) {
+ return null;
+ }
+
+ double lat = parseDMToDecimal(latStr, latDir);
+ double lon = parseDMToDecimal(lonStr, lonDir);
+
+ return convertLatLonToLocal(lat, lon, baseLat, baseLon);
+ }
+
+ /**
+ * 灏嗗害鍒嗘牸寮�(DMM)杞崲涓哄崄杩涘埗鏍煎紡(DD)
+ * @param dmm 搴﹀垎鏍煎紡瀛楃涓� (e.g. "3015.1234")
+ * @param direction 鏂瑰悜 (N/S/E/W)
+ * @return 鍗佽繘鍒剁粡绾害
+ */
+ public static double parseDMToDecimal(String dmm, String direction) {
+ if (dmm == null || dmm.isEmpty()) {
+ return 0.0;
+ }
+ try {
+ double val = Double.parseDouble(dmm);
+ int degrees = (int) (val / 100);
+ double minutes = val - degrees * 100;
+ double decimal = degrees + minutes / 60.0;
+ if (direction != null && (direction.equalsIgnoreCase("S") || direction.equalsIgnoreCase("W"))) {
+ decimal = -decimal;
+ }
+ return decimal;
+ } catch (NumberFormatException e) {
+ return 0.0;
+ }
+ }
+
+ /**
+ * 灏嗙粡绾害杞崲涓虹浉瀵瑰潗鏍�(XY)
+ * @param lat 鐩爣绾害
+ * @param lon 鐩爣缁忓害
+ * @param baseLat 鍩哄噯绔欑含搴�
+ * @param baseLon 鍩哄噯绔欑粡搴�
+ * @return double[]{x, y} (鍗曚綅: 绫�)
+ */
+ public 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 static void ensureBaseStationLoaded() {
+ if (baseStationLoaded) return;
+
+ Properties props = new Properties();
+ try (FileInputStream input = new FileInputStream("basestation.properties")) {
+ props.load(input);
+ String coords = props.getProperty("installationCoordinates");
+ if (coords != null && !coords.isEmpty() && !"-1".equals(coords)) {
+ String[] parts = coords.split(",");
+ if (parts.length >= 4) {
+ cachedBaseLat = parseDMToDecimal(parts[0], parts[1]);
+ cachedBaseLon = parseDMToDecimal(parts[2], parts[3]);
+ baseStationLoaded = true;
+ }
+ }
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+
+ /**
+ * 閲嶆柊鍔犺浇鍩哄噯绔欎俊鎭�
+ */
+ public static void reloadBaseStation() {
+ baseStationLoaded = false;
+ ensureBaseStationLoaded();
+ }
+}
diff --git a/src/zhuye/Lookbutton.java b/src/publicway/Lookbutton.java
similarity index 98%
rename from src/zhuye/Lookbutton.java
rename to src/publicway/Lookbutton.java
index aebfb9e..52d7727 100644
--- a/src/zhuye/Lookbutton.java
+++ b/src/publicway/Lookbutton.java
@@ -1,4 +1,4 @@
-package zhuye;
+package publicway;
import javax.swing.*;
import java.awt.*;
@@ -105,3 +105,10 @@
}
}
+
+
+
+
+
+
+
diff --git a/src/zhuye/buttonset.java b/src/publicway/buttonset.java
similarity index 99%
rename from src/zhuye/buttonset.java
rename to src/publicway/buttonset.java
index 549a9f5..9b87678 100644
--- a/src/zhuye/buttonset.java
+++ b/src/publicway/buttonset.java
@@ -1,4 +1,4 @@
-package zhuye;
+package publicway;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
diff --git a/src/set/Sets.java b/src/set/Sets.java
index 6f5ea93..ab09098 100644
--- a/src/set/Sets.java
+++ b/src/set/Sets.java
@@ -3,10 +3,9 @@
import baseStation.BaseStation;
import gecaoji.Device;
import gecaoji.MowerSafetyDistanceCalculator;
-
+import publicway.buttonset;
import zhuye.MapRenderer;
import zhuye.Shouye;
-import zhuye.buttonset;
import zhuye.celiangmoshi;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
diff --git a/src/set/debug.java b/src/set/debug.java
index ca549df..a8af0fa 100644
--- a/src/set/debug.java
+++ b/src/set/debug.java
@@ -4,6 +4,8 @@
import chuankou.SerialPortPreferences;
import chuankou.SerialPortService;
import chuankou.sendmessage;
+import publicway.buttonset;
+
import com.fazecast.jSerialComm.SerialPort;
import ui.UIConfig;
import javax.swing.*;
@@ -16,7 +18,6 @@
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
-import zhuye.buttonset;
/**
* 绯荤粺璋冭瘯瀵硅瘽妗嗐��
diff --git a/src/udpdell/UDPServer.java b/src/udpdell/UDPServer.java
index 7243090..b59526f 100644
--- a/src/udpdell/UDPServer.java
+++ b/src/udpdell/UDPServer.java
@@ -10,6 +10,8 @@
import gecaoji.Device;
import zhuye.Coordinate;
+import publicway.Gpstoxuzuobiao;
+
public class UDPServer {
private static final int PORT = 7000; // 榛樿UDP鐩戝惉绔彛
private static final int BUFFER_SIZE = 65507; // UDP鏈�澶у寘澶у皬
@@ -87,6 +89,14 @@
}
int sequence = incrementReceivedPacketCounter();
System.out.println("鏀跺埌浜嗗樊鍒嗘暟鎹�(" + sequence + ")锛�" + message);
+
+ // 浣跨敤Gpstoxuzuobiao澶勭悊骞惰幏鍙朮Y鍧愭爣
+ double[] xy = Gpstoxuzuobiao.processGNGGAToXY(message);
+ if (xy != null) {
+ // 杩欓噷鍙互灏哫Y鍧愭爣浼犻�掔粰鍏朵粬鏂规硶浣跨敤
+ // System.out.println("UDP GNGGA -> XY: " + xy[0] + ", " + xy[1]);
+ }
+
Coordinate.parseGNGGAToCoordinateList(message);
int count = Coordinate.coordinates.size();
System.out.println("savenum:" + count);
@@ -110,6 +120,14 @@
}
int sequence = incrementReceivedPacketCounter();
System.out.println("鏀跺埌浜嗕覆鍙f暟鎹�(" + sequence + ")锛�" + message);
+
+ // 浣跨敤Gpstoxuzuobiao澶勭悊骞惰幏鍙朮Y鍧愭爣
+ double[] xy = Gpstoxuzuobiao.processGNGGAToXY(message);
+ if (xy != null) {
+ // 杩欓噷鍙互灏哫Y鍧愭爣浼犻�掔粰鍏朵粬鏂规硶浣跨敤
+ // System.out.println("Serial GNGGA -> XY: " + xy[0] + ", " + xy[1]);
+ }
+
Coordinate.dellchuankougngga(message);
int count = Coordinate.coordinates.size();
System.out.println("savenum:" + count);
diff --git a/src/yaokong/Control02.java b/src/yaokong/Control02.java
index afe55be..5cef25c 100644
--- a/src/yaokong/Control02.java
+++ b/src/yaokong/Control02.java
@@ -3,6 +3,8 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import publicway.Gpstoxuzuobiao;
+
public class Control02 {
/**
@@ -98,29 +100,7 @@
* 渚嬪锛�3949.90238860 -> 39.83170647666667
*/
private static double parseDMToDecimal(String dmm, String direction) {
- try {
- // 鎵惧埌灏忔暟鐐圭殑浣嶇疆
- int dotIndex = dmm.indexOf('.');
- if (dotIndex < 2) {
- throw new IllegalArgumentException("搴﹀垎鏍煎紡閿欒: " + dmm);
- }
-
- // 鎻愬彇搴﹀拰鍒�
- int degrees = Integer.parseInt(dmm.substring(0, dotIndex - 2));
- double minutes = Double.parseDouble(dmm.substring(dotIndex - 2));
-
- // 杞崲涓哄崄杩涘埗
- double decimal = degrees + minutes / 60.0;
-
- // 鏍规嵁鏂瑰悜璋冩暣姝h礋
- if ("S".equalsIgnoreCase(direction) || "W".equalsIgnoreCase(direction)) {
- decimal = -decimal;
- }
-
- return decimal;
- } catch (Exception e) {
- throw new IllegalArgumentException("鍧愭爣鏍煎紡瑙f瀽閿欒: " + dmm, e);
- }
+ return Gpstoxuzuobiao.parseDMToDecimal(dmm, direction);
}
private static String bytesToHex(byte[] bytes) {
diff --git a/src/zhangaiwu/AddDikuai.java b/src/zhangaiwu/AddDikuai.java
index 8d8001b..32a7a4f 100644
--- a/src/zhangaiwu/AddDikuai.java
+++ b/src/zhangaiwu/AddDikuai.java
@@ -27,12 +27,12 @@
import dikuai.Dikuaiguanli;
import bianjie.bianjieguihua2;
import lujing.Lunjingguihua;
+import publicway.buttonset;
import set.Setsys;
import ui.UIConfig;
import zhuye.MowerLocationData;
import zhuye.Shouye;
import zhuye.Coordinate;
-import zhuye.buttonset;
import gecaoji.Device;
/**
diff --git a/src/zhangaiwu/yulanzhangaiwu.java b/src/zhangaiwu/yulanzhangaiwu.java
index f9cacf8..14b266a 100644
--- a/src/zhangaiwu/yulanzhangaiwu.java
+++ b/src/zhangaiwu/yulanzhangaiwu.java
@@ -12,6 +12,7 @@
import java.util.List;
import java.util.Locale;
+import javax.swing.JDialog;
import javax.swing.SwingUtilities;
import zhuye.Coordinate;
@@ -20,7 +21,9 @@
/**
* 鍦ㄥ湴鍥句笂瀹炴椂棰勮姝e湪缁樺埗鐨勯殰纰嶇墿銆�
*/
-public final class yulanzhangaiwu {
+import publicway.Gpstoxuzuobiao;
+
+public class yulanzhangaiwu extends JDialog {
private static final Object LOCK = new Object();
private static final double METERS_PER_DEGREE_LAT = 111320.0d;
private static final Color PREVIEW_LINE_COLOR = new Color(0, 123, 255, 200);
@@ -229,34 +232,11 @@
}
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;
- }
+ return Gpstoxuzuobiao.parseDMToDecimal(dmm, direction);
}
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};
+ return Gpstoxuzuobiao.convertLatLonToLocal(lat, lon, baseLat, baseLon);
}
private static CircleState fitCircle(List<double[]> points) {
diff --git a/src/zhuye/AreaSelectionDialog.java b/src/zhuye/AreaSelectionDialog.java
index 0e87a12..965e1dc 100644
--- a/src/zhuye/AreaSelectionDialog.java
+++ b/src/zhuye/AreaSelectionDialog.java
@@ -3,9 +3,11 @@
import ui.UIConfig;
import javax.swing.*;
+
+import publicway.buttonset;
+
import java.awt.*;
import java.awt.event.*;
-import zhuye.buttonset;
public class AreaSelectionDialog extends JDialog {
private final Color THEME_COLOR;
diff --git a/src/zhuye/LegendDialog.java b/src/zhuye/LegendDialog.java
index 096e239..f3c852f 100644
--- a/src/zhuye/LegendDialog.java
+++ b/src/zhuye/LegendDialog.java
@@ -48,6 +48,7 @@
{"璺緞鏂瑰悜", "255,0,0", "arrow"},
{"闅滅鐗╁尯鍩�", "255,0,0", "obstacle_fill"},
{"鍓茶崏鏈轰綅缃�", "0,150,0", "mow"},
+ {"寰�杩旇矾寰�", "0,0,0", "railway"},
{"鍓茶崏杞ㄨ抗", "100,150,200", "trail"}
};
@@ -120,11 +121,30 @@
g2d.fillPolygon(xPoints, yPoints, 3);
break;
case "mow":
- g2d.setColor(itemColor);
- g2d.fillOval(x, y, size, size);
- g2d.setColor(new Color(100, 100, 100));
- g2d.setStroke(new BasicStroke(1));
- g2d.drawOval(x, y, size, size);
+ ImageIcon icon = loadIcon("image/gecaoji.png", size + 8, size + 8);
+ if (icon != null) {
+ icon.paintIcon(this, g2d, x - 4, y - 4);
+ } else {
+ g2d.setColor(itemColor);
+ g2d.fillOval(x, y, size, size);
+ g2d.setColor(new Color(100, 100, 100));
+ g2d.setStroke(new BasicStroke(1));
+ g2d.drawOval(x, y, size, size);
+ }
+ break;
+ case "railway":
+ // 1. 缁樺埗搴曞眰榛戣壊瀹炵嚎
+ g2d.setColor(Color.BLACK);
+ g2d.setStroke(new BasicStroke(3.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+ g2d.drawLine(x, y + size/2, x + size, y + size/2);
+
+ // 2. 缁樺埗椤跺眰鐧借壊铏氱嚎
+ float dashLen = 3.0f * 2.0f;
+ float dashSpace = 3.0f * 2.0f;
+ float[] dashPattern = {dashLen, dashSpace};
+ g2d.setColor(Color.WHITE);
+ g2d.setStroke(new BasicStroke(1.2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 10.0f, dashPattern, 0.0f));
+ g2d.drawLine(x, y + size/2, x + size, y + size/2);
break;
case "trail":
g2d.setColor(itemColor);
diff --git a/src/zhuye/MapRenderer.java b/src/zhuye/MapRenderer.java
index 790f3b9..ed61778 100644
--- a/src/zhuye/MapRenderer.java
+++ b/src/zhuye/MapRenderer.java
@@ -107,6 +107,9 @@
private boolean idleTrailSuppressed;
private Path2D.Double realtimeBoundaryPathCache;
private String realtimeBoundaryPathLand;
+ private WangfanDraw returnPathDrawer; // 寰�杩旇矾寰勭粯鍒剁鐞嗗櫒
+ private List<Point2D.Double> currentReturnPath; // 褰撳墠鍦板潡鐨勫線杩旇矾寰勶紙鐢ㄤ簬鏄剧ず锛�
+ private List<Point2D.Double> previewReturnPath; // 棰勮鐨勫線杩旇矾寰�
private static final double TRACK_SAMPLE_MIN_DISTANCE_METERS = 0.2d;
private static final double TRACK_DUPLICATE_TOLERANCE_METERS = 1e-3d;
@@ -459,6 +462,17 @@
drawNavigationPreviewCoverage(g2d);
}
+ // 鍏堢敾寰�杩旇矾寰勶紙绾�+鐐癸級锛屼繚璇佸壊鑽夋満鍥炬爣鍦ㄥ叾涓婃柟
+ if (returnPathDrawer != null && returnPathDrawer.isActive()) {
+ returnPathDrawer.draw(g2d, scale);
+ } else if (previewReturnPath != null && !previewReturnPath.isEmpty()) {
+ // 缁樺埗棰勮鐨勫線杩旇矾寰勶紙閾佺嚎璺浘椋庢牸锛�
+ WangfanDraw.drawRailwayPath(g2d, previewReturnPath, scale);
+ } else if (currentReturnPath != null && !currentReturnPath.isEmpty()) {
+ // 缁樺埗淇濆瓨鐨勫線杩旇矾寰勶紙閾佺嚎璺浘椋庢牸锛�
+ WangfanDraw.drawRailwayPath(g2d, currentReturnPath, scale);
+ }
+
drawMower(g2d);
// 缁樺埗瀵艰埅棰勮閫熷害锛堝鏋滄鍦ㄥ鑸瑙堬級
@@ -962,6 +976,14 @@
mowerEffectiveWidthMeters = defaultMowerWidthMeters;
}
+ // 鍔犺浇寰�杩旇矾寰�
+ String returnPathStr = dikuai != null ? dikuai.getReturnPathCoordinates() : null;
+ if (returnPathStr != null && !returnPathStr.isEmpty() && !"-1".equals(returnPathStr)) {
+ currentReturnPath = lujingdraw.parsePlannedPath(returnPathStr);
+ } else {
+ currentReturnPath = null;
+ }
+
loadRealtimeTrack(landNumber, dikuai != null ? dikuai.getMowingTrack() : null);
visualizationPanel.repaint();
}
@@ -2993,5 +3015,63 @@
public Gecaoji getMower() {
return mower;
}
+
+ /**
+ * 璁剧疆寰�杩旇矾寰勭粯鍒剁鐞嗗櫒
+ */
+ public void setReturnPathDrawer(WangfanDraw drawer) {
+ this.returnPathDrawer = drawer;
+ }
+
+ /**
+ * 璁剧疆棰勮鐨勫線杩旇矾寰�
+ */
+ public void setPreviewReturnPath(List<Point2D.Double> path) {
+ this.previewReturnPath = path;
+ if (visualizationPanel != null) {
+ visualizationPanel.repaint();
+ }
+ }
+
+ /**
+ * 寮�濮嬪線杩旇矾寰勭粯鍒�
+ */
+ public void startReturnPathDrawing() {
+ if (returnPathDrawer != null) {
+ // 绂佺敤鎷栧熬鏁堟灉锛堝湪寰�杩旇矾寰勭粯鍒舵ā寮忎笅涓嶆樉绀哄疄鏃惰建杩规嫋灏撅級
+ idleTrailSuppressed = true;
+ clearIdleMowerTrail();
+ // 娓呯┖涔嬪墠鐨勮矾寰勭偣锛堥�氳繃 WangfanDraw 绠$悊锛�
+ repaint();
+ }
+ }
+
+ /**
+ * 鍋滄寰�杩旇矾寰勭粯鍒�
+ */
+ public void stopReturnPathDrawing() {
+ // 鎭㈠鎷栧熬鏁堟灉
+ idleTrailSuppressed = false;
+ repaint();
+ }
+
+ /**
+ * 娣诲姞寰�杩旇矾寰勭偣锛堝凡搴熷純锛岃矾寰勭偣鐢� WangfanDraw 鐩存帴绠$悊锛�
+ */
+ @Deprecated
+ public void addReturnPathPoint(double x, double y) {
+ // 璺緞鐐圭敱 WangfanDraw 鐩存帴绠$悊锛岃繖閲屽彧闇�瑕侀噸缁�
+ repaint();
+ }
+
+ /**
+ * 鑾峰彇寰�杩旇矾寰勭偣鍒楄〃鐨勫揩鐓�
+ */
+ public List<Point2D.Double> getReturnPathPointsSnapshot() {
+ if (returnPathDrawer != null) {
+ return returnPathDrawer.getPointsSnapshot();
+ }
+ return new ArrayList<>();
+ }
}
\ No newline at end of file
diff --git a/src/zhuye/Shouye.java b/src/zhuye/Shouye.java
index 1dbc337..22f8fc6 100644
--- a/src/zhuye/Shouye.java
+++ b/src/zhuye/Shouye.java
@@ -19,23 +19,23 @@
import gecaoji.Device;
import gecaoji.Gecaoji;
import gecaoji.MowerBoundaryChecker;
+import publicway.buttonset;
import set.Sets;
import set.debug;
import udpdell.UDPServer;
import zhangaiwu.AddDikuai;
-import yaokong.Control04;
import yaokong.RemoteControlDialog;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Locale;
-import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.awt.geom.Point2D;
+import publicway.Gpstoxuzuobiao;
+
/**
* 棣栭〉鐣岄潰 - 閫傞厤6.5瀵哥珫灞忥紝浣跨敤鐙珛鐨凪apRenderer杩涜缁樺埗
*/
@@ -107,12 +107,14 @@
// 鍦板浘娓叉煋鍣�
private MapRenderer mapRenderer;
+ private boolean pathPreviewActive;
+
private final Consumer<String> serialLineListener = line -> {
SwingUtilities.invokeLater(() -> {
updateDataPacketCountLabel();
// 濡傛灉鏀跺埌$GNGGA鏁版嵁锛岀珛鍗虫洿鏂版嫋灏�
if (line != null && line.trim().startsWith("$GNGGA")) {
- if (mapRenderer != null) {
+ if (mapRenderer != null && !pathPreviewActive) {
mapRenderer.forceUpdateIdleMowerTrail();
}
}
@@ -125,7 +127,6 @@
private JPanel floatingButtonColumn;
private Runnable endDrawingCallback;
private JButton pathPreviewReturnButton;
- private boolean pathPreviewActive;
private Runnable pathPreviewReturnAction;
private JButton settingsReturnButton; // 杩斿洖绯荤粺璁剧疆椤甸潰鐨勬偓娴寜閽�
private JButton saveManualBoundaryButton; // 淇濆瓨鎵嬪姩缁樺埗杈圭晫鐨勬寜閽�
@@ -170,6 +171,7 @@
private boolean storedStopButtonActive;
private String storedStatusBeforeDrawing;
private boolean handheldCaptureInlineUiActive;
+ private WangfanDraw returnPathDrawer; // 寰�杩旇矾寰勭粯鍒剁鐞嗗櫒
private Timer handheldCaptureStatusTimer;
private String handheldCaptureStoredStatusText;
private Color handheldStartButtonOriginalBackground;
@@ -209,6 +211,54 @@
// 鍒濆鍖栧湴鍥炬覆鏌撳櫒
mapRenderer = new MapRenderer(visualizationPanel);
applyIdleTrailDurationFromSettings();
+
+ // 鍒濆鍖栧線杩旇矾寰勭粯鍒剁鐞嗗櫒
+ returnPathDrawer = new WangfanDraw(this, mapRenderer, new WangfanDraw.DrawingHelper() {
+ @Override
+ public double[] resolveBaseLatLon() {
+ return resolveCircleBaseLatLon();
+ }
+
+ @Override
+ public Coordinate getLatestCoordinate() {
+ return Shouye.this.getLatestCoordinate();
+ }
+
+ @Override
+ public double parseDMToDecimal(String dmm, String direction) {
+ return Shouye.this.parseDMToDecimal(dmm, direction);
+ }
+
+ @Override
+ public double[] convertLatLonToLocal(double lat, double lon, double baseLat, double baseLon) {
+ return Shouye.this.convertLatLonToLocal(lat, lon, baseLat, baseLon);
+ }
+
+ @Override
+ public boolean arePointsClose(Point2D.Double a, Point2D.Double b) {
+ return Shouye.this.arePointsClose(a, b);
+ }
+
+ @Override
+ public void enterDrawingControlMode() {
+ Shouye.this.enterDrawingControlMode();
+ }
+
+ @Override
+ public void exitDrawingControlMode() {
+ Shouye.this.exitDrawingControlMode();
+ }
+
+ @Override
+ public boolean isDrawingPaused() {
+ return drawingPaused;
+ }
+ });
+
+ // 璁剧疆 MapRenderer 鐨勫線杩旇矾寰勭粯鍒剁鐞嗗櫒
+ if (mapRenderer != null) {
+ mapRenderer.setReturnPathDrawer(returnPathDrawer);
+ }
// 鍒濆鍖栧璇濇寮曠敤涓簄ull锛屽欢杩熷垱寤�
legendDialog = null;
@@ -1839,7 +1889,11 @@
}
private void handleDrawingStopFromControlPanel() {
- if (endDrawingCallback != null) {
+ // 濡傛灉鏄線杩旇矾寰勭粯鍒舵ā寮忥紝璋冪敤瀹屾垚缁樺埗鍥炶皟
+ if (returnPathDrawer != null && returnPathDrawer.isActive()) {
+ returnPathDrawer.stop();
+ returnPathDrawer.executeFinishCallback();
+ } else if (endDrawingCallback != null) {
endDrawingCallback.run();
} else {
addzhangaiwu.finishDrawingSession();
@@ -2086,7 +2140,8 @@
startBtn.setText(drawingPaused ? "寮�濮嬬粯鍒�" : "鏆傚仠缁樺埗");
}
if (stopBtn != null) {
- stopBtn.setText("缁撴潫缁樺埗");
+ // 濡傛灉鏄線杩旇矾寰勭粯鍒舵ā寮忥紝鏄剧ず"瀹屾垚缁樺埗"锛屽惁鍒欐樉绀�"缁撴潫缁樺埗"
+ stopBtn.setText((returnPathDrawer != null && returnPathDrawer.isActive()) ? "瀹屾垚缁樺埗" : "缁撴潫缁樺埗");
}
}
@@ -3645,34 +3700,11 @@
}
private double parseDMToDecimal(String dmm, String direction) {
- if (dmm == null || dmm.trim().isEmpty()) {
- return Double.NaN;
- }
- try {
- String trimmed = dmm.trim();
- int dotIndex = trimmed.indexOf('.');
- if (dotIndex < 2) {
- return Double.NaN;
- }
- int degrees = Integer.parseInt(trimmed.substring(0, dotIndex - 2));
- double minutes = Double.parseDouble(trimmed.substring(dotIndex - 2));
- double decimal = degrees + minutes / 60.0;
- if ("S".equalsIgnoreCase(direction) || "W".equalsIgnoreCase(direction)) {
- decimal = -decimal;
- }
- return decimal;
- } catch (NumberFormatException ex) {
- return Double.NaN;
- }
+ return Gpstoxuzuobiao.parseDMToDecimal(dmm, direction);
}
private double[] convertLatLonToLocal(double lat, double lon, double baseLat, double baseLon) {
- double deltaLat = lat - baseLat;
- double deltaLon = lon - baseLon;
- double meanLatRad = Math.toRadians((baseLat + lat) / 2.0);
- double eastMeters = deltaLon * METERS_PER_DEGREE_LAT * Math.cos(meanLatRad);
- double northMeters = deltaLat * METERS_PER_DEGREE_LAT;
- return new double[]{eastMeters, northMeters};
+ return Gpstoxuzuobiao.convertLatLonToLocal(lat, lon, baseLat, baseLon);
}
private CircleSolution fitCircleFromPoints(List<double[]> points) {
@@ -4114,6 +4146,135 @@
}
+ /**
+ * 鍚姩寰�杩旇矾寰勭粯鍒�
+ * @param finishCallback 瀹屾垚缁樺埗鏃剁殑鍥炶皟
+ * @return 鏄惁鎴愬姛鍚姩
+ */
+ public boolean startReturnPathDrawing(Runnable finishCallback) {
+ if (returnPathDrawer == null) {
+ return false;
+ }
+ return returnPathDrawer.start(finishCallback);
+ }
+
+ /**
+ * 鍋滄寰�杩旇矾寰勭粯鍒�
+ */
+ public void stopReturnPathDrawing() {
+ if (returnPathDrawer != null) {
+ returnPathDrawer.stop();
+ }
+ }
+
+ /**
+ * 鑾峰彇寰�杩旇矾寰勭粯鍒剁鐞嗗櫒
+ */
+ public WangfanDraw getReturnPathDrawer() {
+ return returnPathDrawer;
+ }
+
+ /**
+ * 鍚姩寰�杩旇矾寰勯瑙�
+ * @param coordinatesStr 璺緞鍧愭爣瀛楃涓� (x,y;x,y)
+ * @param returnCallback 杩斿洖鍥炶皟
+ */
+ public void startReturnPathPreview(String coordinatesStr, Runnable returnCallback) {
+ if (returnPathDrawer == null || coordinatesStr == null || coordinatesStr.isEmpty()) {
+ return;
+ }
+
+ // 瑙f瀽鍧愭爣
+ List<Point2D.Double> points = new ArrayList<>();
+ String[] pairs = coordinatesStr.split(";");
+ for (String pair : pairs) {
+ String[] xy = pair.split(",");
+ if (xy.length == 2) {
+ try {
+ double x = Double.parseDouble(xy[0]);
+ double y = Double.parseDouble(xy[1]);
+ points.add(new Point2D.Double(x, y));
+ } catch (NumberFormatException e) {
+ // 蹇界暐鏃犳晥鍧愭爣
+ }
+ }
+ }
+
+ if (points.isEmpty()) {
+ JOptionPane.showMessageDialog(this, "娌℃湁鏈夋晥鐨勮矾寰勭偣鍙瑙�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
+ // 璁剧疆棰勮鐐�
+ returnPathDrawer.setPoints(points);
+ if (mapRenderer != null) {
+ mapRenderer.setPreviewReturnPath(points);
+ }
+
+ // 寮�鍚瑙堟ā寮�
+ pathPreviewActive = true;
+ if (mapRenderer != null) {
+ mapRenderer.clearIdleTrail();
+ }
+ pathPreviewReturnAction = returnCallback;
+
+ // 纭繚鎮诞鎸夐挳鍩虹璁炬柦宸插垱寤�
+ ensureFloatingButtonInfrastructure();
+
+ // 鍒涘缓鎴栨樉绀鸿繑鍥炴寜閽�
+ if (pathPreviewReturnButton == null) {
+ pathPreviewReturnButton = publicway.buttonset.createStyledButton("杩斿洖", null);
+ pathPreviewReturnButton.setToolTipText("杩斿洖缁樺埗椤甸潰");
+ pathPreviewReturnButton.addActionListener(e -> {
+ // 鍋滄棰勮
+ stopReturnPathPreview();
+ });
+ }
+
+ // 闅愯棌鍏朵粬鎮诞鎸夐挳
+ hideFloatingDrawingControls();
+
+ // 鏄剧ず杩斿洖鎸夐挳
+ pathPreviewReturnButton.setVisible(true);
+ floatingButtonPanel.setVisible(true);
+ if (floatingButtonPanel.getParent() != visualizationPanel) {
+ visualizationPanel.add(floatingButtonPanel, BorderLayout.SOUTH);
+ }
+ rebuildFloatingButtonColumn();
+
+ visualizationPanel.revalidate();
+ visualizationPanel.repaint();
+ }
+
+ /**
+ * 鍋滄寰�杩旇矾寰勯瑙�
+ */
+ private void stopReturnPathPreview() {
+ pathPreviewActive = false;
+
+ // 娓呯┖棰勮鐐�
+ if (returnPathDrawer != null) {
+ returnPathDrawer.clearPoints();
+ }
+ if (mapRenderer != null) {
+ mapRenderer.setPreviewReturnPath(null);
+ }
+
+ // 闅愯棌杩斿洖鎸夐挳
+ if (pathPreviewReturnButton != null) {
+ pathPreviewReturnButton.setVisible(false);
+ }
+
+ // 闅愯棌鎮诞闈㈡澘
+ if (floatingButtonPanel != null) {
+ floatingButtonPanel.setVisible(false);
+ }
+
+ // 鎵ц杩斿洖鍥炶皟
+ if (pathPreviewReturnAction != null) {
+ pathPreviewReturnAction.run();
+ }
+ }
// 娴嬭瘯鏂规硶
public static void main(String[] args) {
diff --git a/src/zhuye/WangfanDraw.java b/src/zhuye/WangfanDraw.java
new file mode 100644
index 0000000..464cedf
--- /dev/null
+++ b/src/zhuye/WangfanDraw.java
@@ -0,0 +1,411 @@
+package zhuye;
+
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+import java.util.List;
+import javax.swing.Timer;
+import gecaoji.Device;
+
+/**
+ * 寰�杩旇矾寰勭粯鍒剁鐞嗗櫒
+ * 璐熻矗绠$悊寰�杩旇矾寰勭殑缁樺埗閫昏緫鍜岀姸鎬�
+ */
+public class WangfanDraw {
+ // 寰�杩旇矾寰勭偣鍒楄〃
+ private final List<Point2D.Double> returnPathPoints = new ArrayList<>();
+
+ // 缁樺埗鐘舵��
+ private boolean drawingActive = false;
+ private boolean paused = false;
+
+ // 鍩哄噯鍧愭爣
+ private double[] baseLatLon;
+
+ // 鐩戞帶瀹氭椂鍣�
+ private Timer monitorTimer;
+
+ // 鍥炶皟鎺ュ彛
+ private Runnable finishCallback;
+
+ // 渚濊禆瀵硅薄
+ private Shouye shouye;
+ private MapRenderer mapRenderer;
+
+ // 寰�杩旇矾寰勭粯鍒舵帴鍙�
+ public interface DrawingHelper {
+ double[] resolveBaseLatLon();
+ Coordinate getLatestCoordinate();
+ double parseDMToDecimal(String dmm, String direction);
+ double[] convertLatLonToLocal(double lat, double lon, double baseLat, double baseLon);
+ boolean arePointsClose(Point2D.Double a, Point2D.Double b);
+ void enterDrawingControlMode();
+ void exitDrawingControlMode();
+ boolean isDrawingPaused();
+ }
+
+ private DrawingHelper helper;
+
+ /**
+ * 鏋勯�犲嚱鏁�
+ */
+ public WangfanDraw(Shouye shouye, MapRenderer mapRenderer, DrawingHelper helper) {
+ this.shouye = shouye;
+ this.mapRenderer = mapRenderer;
+ this.helper = helper;
+ }
+
+ /**
+ * 鍚姩寰�杩旇矾寰勭粯鍒�
+ * @param finishCallback 瀹屾垚缁樺埗鏃剁殑鍥炶皟
+ * @return 鏄惁鎴愬姛鍚姩
+ */
+ public boolean start(Runnable finishCallback) {
+ if (mapRenderer == null || helper == null) {
+ return false;
+ }
+
+ double[] baseLatLonCandidate = helper.resolveBaseLatLon();
+ if (baseLatLonCandidate == null) {
+ return false;
+ }
+
+ if (mapRenderer != null) {
+ mapRenderer.clearIdleTrail();
+ }
+
+ drawingActive = true;
+ paused = false;
+ this.finishCallback = finishCallback;
+ this.baseLatLon = baseLatLonCandidate;
+
+ // 娓呯┖璺緞鐐�
+ synchronized (returnPathPoints) {
+ returnPathPoints.clear();
+ }
+
+ synchronized (Coordinate.coordinates) {
+ Coordinate.coordinates.clear();
+ }
+
+ if (mapRenderer != null) {
+ mapRenderer.startReturnPathDrawing();
+ }
+
+ Coordinate.setStartSaveGngga(true);
+ if (helper != null) {
+ helper.enterDrawingControlMode();
+ }
+ startMonitor();
+ return true;
+ }
+
+ /**
+ * 璁剧疆棰勮璺緞鐐�
+ * @param points 璺緞鐐瑰垪琛�
+ */
+ public void setPoints(List<Point2D.Double> points) {
+ synchronized (returnPathPoints) {
+ returnPathPoints.clear();
+ if (points != null) {
+ returnPathPoints.addAll(points);
+ }
+ }
+ if (mapRenderer != null) {
+ mapRenderer.repaint();
+ }
+ }
+
+ /**
+ * 娓呯┖璺緞鐐�
+ */
+ public void clearPoints() {
+ synchronized (returnPathPoints) {
+ returnPathPoints.clear();
+ }
+ if (mapRenderer != null) {
+ mapRenderer.repaint();
+ }
+ }
+
+ /**
+ * 鍚姩鐩戞帶瀹氭椂鍣�
+ */
+ private void startMonitor() {
+ if (monitorTimer == null) {
+ monitorTimer = new Timer(600, new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ pollCoordinate();
+ }
+ });
+ monitorTimer.setRepeats(true);
+ }
+ if (!monitorTimer.isRunning()) {
+ monitorTimer.start();
+ }
+ pollCoordinate();
+ }
+
+ /**
+ * 鍋滄鐩戞帶瀹氭椂鍣�
+ */
+ private void stopMonitor() {
+ if (monitorTimer != null && monitorTimer.isRunning()) {
+ monitorTimer.stop();
+ }
+ }
+
+ /**
+ * 杞寰�杩旇矾寰勫潗鏍囷紙鍙繚瀛樺畾浣嶇姸鎬�=4鐨勬湁鏁堝潗鏍囷級
+ */
+ private void pollCoordinate() {
+ if (!drawingActive || (helper != null && helper.isDrawingPaused())) {
+ return;
+ }
+
+ // 妫�鏌ュ畾浣嶇姸鎬佹槸鍚︿负4锛堝浐瀹氳В锛�- 缁熶竴鏉ユ簮涓鸿澶囨暟鎹�
+ Device device = Device.getGecaoji();
+ if (device == null) {
+ return;
+ }
+ String positioningQuality = device.getPositioningStatus();
+ if (positioningQuality == null || !"4".equals(positioningQuality.trim())) {
+ return; // 鍙繚瀛樺畾浣嶇姸鎬�=4鐨勬湁鏁堝潗鏍�
+ }
+
+ if (helper == null) {
+ return;
+ }
+
+ Coordinate latest = helper.getLatestCoordinate();
+ if (latest == null) {
+ return;
+ }
+
+ if (baseLatLon == null || baseLatLon.length < 2) {
+ return;
+ }
+
+ double lat = helper.parseDMToDecimal(latest.getLatitude(), latest.getLatDirection());
+ double lon = helper.parseDMToDecimal(latest.getLongitude(), latest.getLonDirection());
+ if (!Double.isFinite(lat) || !Double.isFinite(lon)) {
+ return;
+ }
+
+ double[] local = helper.convertLatLonToLocal(lat, lon, baseLatLon[0], baseLatLon[1]);
+ Point2D.Double candidate = new Point2D.Double(local[0], local[1]);
+ if (!Double.isFinite(candidate.x) || !Double.isFinite(candidate.y)) {
+ return;
+ }
+
+ // 妫�鏌ユ槸鍚︿笌涓婁竴涓偣澶繎锛堥伩鍏嶉噸澶嶇偣锛�
+ synchronized (returnPathPoints) {
+ if (!returnPathPoints.isEmpty()) {
+ Point2D.Double lastPoint = returnPathPoints.get(returnPathPoints.size() - 1);
+ if (helper.arePointsClose(lastPoint, candidate)) {
+ return; // 鐐瑰お杩戯紝璺宠繃
+ }
+ }
+ }
+
+ // 娣诲姞鍒拌矾寰勭偣鍒楄〃
+ synchronized (returnPathPoints) {
+ returnPathPoints.add(candidate);
+ }
+
+ // 瑙﹀彂閲嶇粯
+ if (mapRenderer != null) {
+ mapRenderer.repaint();
+ }
+
+ // 鍚屾椂淇濆瓨鍒� Coordinate.coordinates 鐢ㄤ簬鍚庣画淇濆瓨
+ synchronized (Coordinate.coordinates) {
+ Coordinate.coordinates.add(latest);
+ }
+ }
+
+ /**
+ * 鍋滄寰�杩旇矾寰勭粯鍒�
+ */
+ public void stop() {
+ stopMonitor();
+ drawingActive = false;
+ paused = false;
+ baseLatLon = null;
+ if (mapRenderer != null) {
+ mapRenderer.stopReturnPathDrawing();
+ }
+ Coordinate.setStartSaveGngga(false);
+ if (helper != null) {
+ helper.exitDrawingControlMode();
+ }
+ }
+
+ /**
+ * 鏆傚仠/鎭㈠缁樺埗
+ */
+ public void setPaused(boolean paused) {
+ this.paused = paused;
+ }
+
+ /**
+ * 妫�鏌ユ槸鍚︽鍦ㄧ粯鍒�
+ */
+ public boolean isActive() {
+ return drawingActive;
+ }
+
+ /**
+ * 妫�鏌ユ槸鍚︽殏鍋�
+ */
+ public boolean isPaused() {
+ return paused;
+ }
+
+ /**
+ * 鑾峰彇瀹屾垚缁樺埗鍥炶皟
+ */
+ public Runnable getFinishCallback() {
+ return finishCallback;
+ }
+
+ /**
+ * 鎵ц瀹屾垚缁樺埗鍥炶皟
+ */
+ public void executeFinishCallback() {
+ if (finishCallback != null) {
+ finishCallback.run();
+ finishCallback = null;
+ }
+ }
+
+ /**
+ * 鑾峰彇璺緞鐐瑰垪琛ㄧ殑蹇収
+ */
+ public List<Point2D.Double> getPointsSnapshot() {
+ synchronized (returnPathPoints) {
+ return new ArrayList<>(returnPathPoints);
+ }
+ }
+
+ /**
+ * 缁樺埗寰�杩旇矾寰�
+ */
+ public void draw(Graphics2D g2d, double scale) {
+ List<Point2D.Double> points = getPointsSnapshot();
+ if (points.isEmpty()) {
+ return;
+ }
+
+ // 淇濆瓨鍘熷鍙樻崲
+ AffineTransform originalTransform = g2d.getTransform();
+
+ // 璁剧疆寰�杩旇矾寰勯鑹�
+ Color lineColor = Color.GREEN; // 缁胯壊杩炵嚎
+ Color pointColor = Color.RED; // 绾㈣壊瀹炲績鍦�
+
+ // 璁剧疆绾垮
+ float lineWidth = (float)(3.0 / Math.max(0.5, scale));
+ g2d.setStroke(new BasicStroke(lineWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+
+ // 鐐圭殑澶у皬锛堝湪涓栫晫鍧愭爣绯讳腑锛岀背锛夛紝鐩村緞涓虹嚎瀹藉害鐨�3鍊�
+ // 娉ㄦ剰锛歭ineWidth宸茬粡鏄笘鐣屽潗鏍囩郴涓嬬殑瀹藉害(3.0/scale)锛屾墍浠ョ洿鎺ヤ箻浠ュ�嶆暟鍗冲彲寰楀埌涓栫晫鍧愭爣绯讳笅鐨勭洿寰�
+ // 涓嶉渶瑕佸啀闄や互scale锛屽惁鍒欎細瀵艰嚧鏀惧ぇ鍦板浘鏃跺渾鍦堝彉灏�
+ double pointSizeInWorld = lineWidth * 3.0;
+ double halfSize = pointSizeInWorld / 2.0;
+
+ // 鍏堢粯鍒舵墍鏈夎矾寰勭偣锛堜綔涓哄熀纭�灞傦級
+ g2d.setColor(pointColor);
+ for (Point2D.Double point : points) {
+ Ellipse2D.Double pointShape = new Ellipse2D.Double(
+ point.x - halfSize,
+ point.y - halfSize,
+ pointSizeInWorld,
+ pointSizeInWorld
+ );
+ g2d.fill(pointShape);
+ }
+
+ // 鐒跺悗缁樺埗杩炵嚎
+ g2d.setColor(lineColor);
+ if (points.size() >= 2) {
+ Path2D.Double linePath = new Path2D.Double();
+ linePath.moveTo(points.get(0).x, points.get(0).y);
+ for (int i = 1; i < points.size(); i++) {
+ linePath.lineTo(points.get(i).x, points.get(i).y);
+ }
+ g2d.draw(linePath);
+ }
+
+ // 鏈�鍚庡啀娆$粯鍒惰矾寰勭偣锛堝湪杩炵嚎涔嬩笂锛岀‘淇濈偣瑕嗙洊鍦ㄧ嚎鐨勭鐐逛笂锛�
+ // 鍑嗗瀛椾綋锛氬ぇ灏忎笌鍦嗗湀涓�鑷达紙涓栫晫鍧愭爣绯伙級锛岄粦鑹�
+ float fontSizeInWorld = (float)pointSizeInWorld;
+ // 浣跨敤0.8鍊嶅ぇ灏忎互纭繚鏁板瓧鍦ㄥ渾鍦堝唴涓嶆嫢鎸�
+ Font font = new Font("Arial", Font.BOLD, 1).deriveFont(fontSizeInWorld * 0.8f);
+ g2d.setFont(font);
+ FontMetrics fm = g2d.getFontMetrics();
+
+ for (int i = 0; i < points.size(); i++) {
+ Point2D.Double point = points.get(i);
+
+ // 缁樺埗鐐�
+ g2d.setColor(pointColor);
+ Ellipse2D.Double pointShape = new Ellipse2D.Double(
+ point.x - halfSize,
+ point.y - halfSize,
+ pointSizeInWorld,
+ pointSizeInWorld
+ );
+ g2d.fill(pointShape);
+
+ // 缁樺埗缂栧彿
+ g2d.setColor(Color.BLACK);
+ String number = String.valueOf(i + 1);
+ java.awt.geom.Rectangle2D bounds = fm.getStringBounds(number, g2d);
+ double textX = point.x - bounds.getWidth() / 2.0;
+ double textY = point.y - bounds.getCenterY();
+ g2d.drawString(number, (float)textX, (float)textY);
+ }
+ }
+
+ /**
+ * 缁樺埗瀹炵嚎绾挎潯涓棿鍔犱笂铏氱嚎铏氱嚎鐧借壊锛屽疄绾块粦鑹茬殑椋庢牸
+ * @param g2d Graphics2D瀵硅薄
+ * @param points 璺緞鐐瑰垪琛�
+ * @param scale 褰撳墠缂╂斁姣斾緥
+ */
+ public static void drawRailwayPath(Graphics2D g2d, List<Point2D.Double> points, double scale) {
+ if (points == null || points.size() < 2) {
+ return;
+ }
+
+ // 1. 缁樺埗搴曞眰榛戣壊瀹炵嚎
+ float baseWidth = (float)(3.0 / Math.max(0.5, scale)); // 鍩虹瀹藉害
+ g2d.setColor(Color.BLACK);
+ g2d.setStroke(new BasicStroke(baseWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+
+ Path2D.Double path = new Path2D.Double();
+ path.moveTo(points.get(0).x, points.get(0).y);
+ for (int i = 1; i < points.size(); i++) {
+ path.lineTo(points.get(i).x, points.get(i).y);
+ }
+ g2d.draw(path);
+
+ // 2. 缁樺埗椤跺眰鐧借壊铏氱嚎
+ float dashWidth = baseWidth * 0.4f; // 铏氱嚎瀹藉害姣斿疄绾跨粏涓�浜�
+ float dashLen = baseWidth * 2.0f; // 铏氱嚎娈甸暱搴�
+ float dashSpace = baseWidth * 2.0f; // 铏氱嚎闂撮殧
+ float[] dashPattern = {dashLen, dashSpace};
+
+ g2d.setColor(Color.WHITE);
+ g2d.setStroke(new BasicStroke(dashWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 10.0f, dashPattern, 0.0f));
+ g2d.draw(path);
+ }
+}
+
diff --git a/src/zhuye/daohangyulan.java b/src/zhuye/daohangyulan.java
index edafba9..42e0876 100644
--- a/src/zhuye/daohangyulan.java
+++ b/src/zhuye/daohangyulan.java
@@ -19,6 +19,7 @@
import dikuai.Dikuai;
import gecaoji.Gecaoji;
import gecaoji.lujingdraw;
+import publicway.buttonset;
import zhangaiwu.Obstacledge;
/**
diff --git a/src/zhuye/zijian.java b/src/zhuye/zijian.java
index dac3806..4cc9648 100644
--- a/src/zhuye/zijian.java
+++ b/src/zhuye/zijian.java
@@ -7,6 +7,9 @@
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
+
+import publicway.buttonset;
+
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
--
Gitblit v1.10.0