.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" } 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 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> 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 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; 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 + '\'' + 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; } // è·åå°å管çå¯¹è¯æ¡çç¶çªå£ï¼ä¸»çªå£ï¼ï¼ä½ä¸ºç»å¶é¡µé¢çç¶çªå£ 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); } } } 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; 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()) { // å¦ææ²¡æä¼åè·¯å¾ï¼ä½¿ç¨åå§åæ ç¹ï¼X,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 è·åè·¯å¾ç¹ï¼å·²ç»æ¯X,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); } } 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) { 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; } } 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) { 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; /** * 导èªé¢è§åè½ç±» 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); } } 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 { 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; /** * çæå²èè·¯å¾é¡µé¢ src/publicway/Fuzhibutton.java
ÎļþÃû´Ó src/zhuye/Fuzhibutton.java ÐÞ¸Ä @@ -1,4 +1,4 @@ package zhuye; package publicway; import javax.swing.*; import java.awt.*; 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; /** * è§£æGNGGAæ°æ®å¹¶è½¬æ¢ä¸ºXYåæ * @param gnggaData $GNGGAæ°æ® * @return double[]{x, y} ç¸å¯¹åæ ï¼å¦æè§£æå¤±è´¥ææ åºç«æ°æ®è¿ånull */ public static double[] processGNGGAToXY(String gnggaData) { ensureBaseStationLoaded(); if (!baseStationLoaded) { return null; } return processGNGGAToXY(gnggaData, cachedBaseLat, cachedBaseLon); } /** * è§£æ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; } // ç®åçè§£æé»è¾ï¼æåç»çº¬åº¦ // æ ¼å¼: $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(); } } src/publicway/Lookbutton.java
ÎļþÃû´Ó src/zhuye/Lookbutton.java ÐÞ¸Ä @@ -1,4 +1,4 @@ package zhuye; package publicway; import javax.swing.*; import java.awt.*; @@ -105,3 +105,10 @@ } } src/publicway/buttonset.java
ÎļþÃû´Ó src/zhuye/buttonset.java ÐÞ¸Ä @@ -1,4 +1,4 @@ package zhuye; package publicway; import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; 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; 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; /** * ç³»ç»è°è¯å¯¹è¯æ¡ã 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å¤çå¹¶è·åXYåæ double[] xy = Gpstoxuzuobiao.processGNGGAToXY(message); if (xy != null) { // è¿éå¯ä»¥å°XYåæ ä¼ éç»å ¶ä»æ¹æ³ä½¿ç¨ // 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("æ¶å°äºä¸²å£æ°æ®(" + sequence + ")ï¼" + message); // 使ç¨Gpstoxuzuobiaoå¤çå¹¶è·åXYåæ double[] xy = Gpstoxuzuobiao.processGNGGAToXY(message); if (xy != null) { // è¿éå¯ä»¥å°XYåæ ä¼ éç»å ¶ä»æ¹æ³ä½¿ç¨ // System.out.println("Serial GNGGA -> XY: " + xy[0] + ", " + xy[1]); } Coordinate.dellchuankougngga(message); int count = Coordinate.coordinates.size(); System.out.println("savenum:" + count); 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; // æ ¹æ®æ¹åè°æ´æ£è´ if ("S".equalsIgnoreCase(direction) || "W".equalsIgnoreCase(direction)) { decimal = -decimal; } return decimal; } catch (Exception e) { throw new IllegalArgumentException("åæ æ ¼å¼è§£æé误: " + dmm, e); } return Gpstoxuzuobiao.parseDMToDecimal(dmm, direction); } private static String bytesToHex(byte[] bytes) { 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; /** 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 @@ /** * å¨å°å¾ä¸å®æ¶é¢è§æ£å¨ç»å¶çéç¢ç©ã */ 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) { 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; 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); 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<>(); } } 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寸ç«å±ï¼ä½¿ç¨ç¬ç«çMapRendererè¿è¡ç»å¶ */ @@ -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); } // åå§åå¯¹è¯æ¡å¼ç¨ä¸ºnullï¼å»¶è¿å建 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; } // è§£æåæ 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) { 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å // 注æï¼lineWidthå·²ç»æ¯ä¸çåæ ç³»ä¸ç宽度(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); } } src/zhuye/daohangyulan.java
@@ -19,6 +19,7 @@ import dikuai.Dikuai; import gecaoji.Gecaoji; import gecaoji.lujingdraw; import publicway.buttonset; import zhangaiwu.Obstacledge; /** 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;