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 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 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); }); }; // 启动往返路径绘制,传递是否为手持设备模式 boolean isHandheld = "handheld".equals(method); if (!shouye.startReturnPathDrawing(finishCallback, isHandheld)) { 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 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); } }