package home; import javax.swing.*; import javax.swing.table.DefaultTableModel; import java.awt.*; import java.util.Locale; import java.util.ResourceBundle; public class QuickCalculationPanel extends JPanel { private static final long serialVersionUID = 1L; private ResourceBundle messages; private JTextField pointATextField; private JTextField pointBTextField; private JTextField xyToLonLatTextField; private JTextField lonLatToXYTextField; private JTextField point1RawTextField; private JTextField point2RawTextField; private JTextField distanceResultTextField; private JTable nmeaTable; private DefaultTableModel tableModel; private JTextField result1TextField; private JTextField result2TextField; // 构造函数 public QuickCalculationPanel(ResourceBundle messages) { this.messages = messages; setLayout(new BorderLayout()); setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); setBackground(Color.WHITE); initUI(); } // 初始化用户界面 private void initUI() { JPanel mainPanel = new JPanel(); mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); mainPanel.setBackground(Color.WHITE); // 创建带标题边框的面板 mainPanel.add(createPointInputPanel()); mainPanel.add(Box.createVerticalStrut(10)); mainPanel.add(createCoordinateConversionPanel()); mainPanel.add(Box.createVerticalStrut(10)); mainPanel.add(createDistanceCalculationPanel()); mainPanel.add(Box.createVerticalStrut(10)); mainPanel.add(createNMEATablePanel()); JScrollPane scrollPane = new JScrollPane(mainPanel); scrollPane.setBorder(BorderFactory.createEmptyBorder()); add(scrollPane, BorderLayout.CENTER); } /** * 创建点输入面板 */ private JPanel createPointInputPanel() { JPanel panel = createTitledPanel(getMessage("POINT_INPUT_SECTION", "点输入区域")); panel.setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets(5, 5, 5, 5); // 设置组件间距为5像素 // 点A输入 gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 0; JLabel pointALabel = new JLabel(getMessage("POINT_A", "点A") + ":"); panel.add(pointALabel, gbc); gbc.gridx = 1; gbc.weightx = 1.0; pointATextField = new JTextField(); pointATextField.setPreferredSize(new Dimension(200, 25)); // 设置文本框长度为300像素 pointATextField.setToolTipText(getMessage("POINT_A1", "格式: 经度,纬度,本地X,本地Y")); panel.add(pointATextField, gbc); // 点B输入 gbc.gridx = 0; gbc.gridy = 1; gbc.weightx = 0; JLabel pointBLabel = new JLabel(getMessage("POINT_B", "点B") + ":"); panel.add(pointBLabel, gbc); gbc.gridx = 1; gbc.weightx = 1.0; pointBTextField = new JTextField(); pointBTextField.setPreferredSize(new Dimension(200, 25)); // 设置文本框长度为300像素 pointBTextField.setToolTipText(getMessage("POINT_B1", "格式: 经度,纬度,本地X,本地Y")); panel.add(pointBTextField, gbc); return panel; } /** * 创建坐标转换面板 */ private JPanel createCoordinateConversionPanel() { JPanel panel = createTitledPanel(getMessage("COORDINATE_CONVERSION", "坐标转换")); panel.setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets(5, 5, 5, 5); // 设置组件间距为5像素 // XY转经纬度 gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 0; JLabel xyToLonLatLabel = new JLabel(getMessage("XY_TO_LONLAT", "XY转经纬度") + ":"); panel.add(xyToLonLatLabel, gbc); gbc.gridx = 1; gbc.weightx = 1.0; xyToLonLatTextField = new JTextField(); xyToLonLatTextField.setPreferredSize(new Dimension(200, 25)); // 设置文本框长度为300像素 xyToLonLatTextField.setToolTipText(getMessage("XY_TO_LONLAT1", "输入X,Y坐标")); panel.add(xyToLonLatTextField, gbc); gbc.gridx = 2; gbc.weightx = 0; JButton convertXYButton = createStyledButton(getMessage("CONVERT", "转换")); convertXYButton.addActionListener(e -> convertXYToLonLat()); panel.add(convertXYButton, gbc); // 转换结果1 gbc.gridx = 0; gbc.gridy = 1; gbc.weightx = 0; JLabel result1Label = new JLabel(getMessage("RESULT", "结果") + ":"); panel.add(result1Label, gbc); gbc.gridx = 1; gbc.weightx = 1.0; result1TextField = new JTextField(); result1TextField.setPreferredSize(new Dimension(200, 25)); // 设置文本框长度为300像素 result1TextField.setEditable(false); panel.add(result1TextField, gbc); // 经纬度转XY gbc.gridx = 0; gbc.gridy = 2; gbc.weightx = 0; JLabel lonLatToXYLabel = new JLabel(getMessage("LONLAT_TO_XY", "经纬度转XY") + ":"); panel.add(lonLatToXYLabel, gbc); gbc.gridx = 1; gbc.weightx = 1.0; lonLatToXYTextField = new JTextField(); lonLatToXYTextField.setPreferredSize(new Dimension(200, 25)); // 设置文本框长度为300像素 lonLatToXYTextField.setToolTipText(getMessage("LONLAT_TO_XY1", "输入经度,纬度")); panel.add(lonLatToXYTextField, gbc); gbc.gridx = 2; gbc.weightx = 0; JButton convertLonLatButton = createStyledButton(getMessage("CONVERT", "转换")); convertLonLatButton.addActionListener(e -> convertLonLatToXY()); panel.add(convertLonLatButton, gbc); // 转换结果2 gbc.gridx = 0; gbc.gridy = 3; gbc.weightx = 0; JLabel result2Label = new JLabel(getMessage("RESULT", "结果") + ":"); panel.add(result2Label, gbc); gbc.gridx = 1; gbc.weightx = 1.0; result2TextField = new JTextField(); result2TextField.setPreferredSize(new Dimension(200, 25)); // 设置文本框长度为300像素 result2TextField.setEditable(false); panel.add(result2TextField, gbc); return panel; } /** * 创建距离计算面板 */ private JPanel createDistanceCalculationPanel() { JPanel panel = createTitledPanel(getMessage("DISTANCE_CALCULATION", "距离计算")); panel.setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets(5, 5, 5, 5); // 设置组件间距为5像素 // 原始点1输入 gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 0; JLabel point1Label = new JLabel(getMessage("POINT1_RAW", "原始点1") + ":"); panel.add(point1Label, gbc); gbc.gridx = 1; gbc.weightx = 1.0; point1RawTextField = new JTextField(); point1RawTextField.setPreferredSize(new Dimension(200, 25)); // 设置文本框长度为300像素 panel.add(point1RawTextField, gbc); // 原始点2输入 gbc.gridx = 0; gbc.gridy = 1; gbc.weightx = 0; JLabel point2Label = new JLabel(getMessage("POINT2_RAW", "原始点2") + ":"); panel.add(point2Label, gbc); gbc.gridx = 1; gbc.weightx = 1.0; point2RawTextField = new JTextField(); point2RawTextField.setPreferredSize(new Dimension(200, 25)); // 设置文本框长度为300像素 panel.add(point2RawTextField, gbc); // 计算按钮和结果 gbc.gridx = 0; gbc.gridy = 2; gbc.weightx = 0; JButton calculateButton = createStyledButton(getMessage("CALCULATE_DISTANCE", "计算距离")); calculateButton.addActionListener(e -> calculateDistance()); panel.add(calculateButton, gbc); gbc.gridx = 1; gbc.weightx = 1.0; distanceResultTextField = new JTextField(); distanceResultTextField.setPreferredSize(new Dimension(200, 25)); // 设置文本框长度为300像素 distanceResultTextField.setEditable(false); panel.add(distanceResultTextField, gbc); return panel; } /** * 创建NMEA表格面板 */ private JPanel createNMEATablePanel() { JPanel panel = createTitledPanel(getMessage("NMEA_DATA_TABLE", "NMEA数据表")); panel.setLayout(new BorderLayout()); // 创建表格列标题 String[] columns = { getMessage("FIELD_NAME", "字段名"), getMessage("POINT1", "点1"), getMessage("POINT2", "点2") }; // 定义表格行名称 String[] rowNames = { getMessage("MSG_ID", "消息ID"), getMessage("UTC_TIME", "UTC时间"), getMessage("LATITUDE", "纬度"), getMessage("LAT_HEMISPHERE", "纬度半球"), getMessage("LONGITUDE", "经度"), getMessage("LON_HEMISPHERE", "经度半球"), getMessage("QUALITY", "质量"), getMessage("NUM_SATELLITES", "卫星数量"), getMessage("HDOP", "HDOP"), getMessage("ALTITUDE", "海拔"), getMessage("ALTITUDE_UNIT", "海拔单位"), getMessage("GEOID_HEIGHT", "大地水准面高度"), getMessage("GEOID_HEIGHT_UNIT", "大地水准面高度单位"), getMessage("DIFF_TIME", "差分时间"), getMessage("CHECKSUM", "校验和"), getMessage("DEVICE_ID", "设备ID"), getMessage("DEVICE_POWER", "设备功率"), getMessage("SIGNAL_STRENGTH", "信号强度"), getMessage("RESERVED1", "保留1"), getMessage("RESERVED2", "保留2"), getMessage("RESERVED3", "保留3") }; // 初始化表格模型 tableModel = new DefaultTableModel(columns, 0); // 添加行数据 for (String rowName : rowNames) { tableModel.addRow(new Object[]{rowName, "", ""}); } // 创建表格并禁用编辑 nmeaTable = new JTable(tableModel); nmeaTable.setEnabled(false); nmeaTable.setRowHeight(25); nmeaTable.getTableHeader().setReorderingAllowed(false); JScrollPane scrollPane = new JScrollPane(nmeaTable); scrollPane.setPreferredSize(new Dimension(600, 300)); panel.add(scrollPane, BorderLayout.CENTER); return panel; } /** * 创建带标题边框的面板 */ private JPanel createTitledPanel(String title) { JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createTitledBorder( BorderFactory.createLineBorder(Color.GRAY), title)); panel.setBackground(Color.WHITE); return panel; } /** * 创建样式按钮(参考ExtensionPanel的按钮样式) */ private JButton createStyledButton(String text) { JButton button = new JButton(text); // 设置按钮大小为80×25(宽×高) button.setPreferredSize(new Dimension(80, 25)); button.setMinimumSize(new Dimension(80, 25)); button.setMaximumSize(new Dimension(100, 25)); // 设置按钮样式(参考ExtensionPanel) button.setBackground(new Color(0, 120, 215)); button.setForeground(Color.WHITE); button.setFocusPainted(false); button.setBorderPainted(false); button.setFont(button.getFont().deriveFont(Font.BOLD, 12f)); // 添加鼠标交互效果(参考ExtensionPanel) button.addMouseListener(new java.awt.event.MouseAdapter() { public void mouseEntered(java.awt.event.MouseEvent evt) { button.setBackground(new Color(0, 150, 255)); // 悬停时变亮 button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } public void mouseExited(java.awt.event.MouseEvent evt) { button.setBackground(new Color(0, 120, 215)); // 恢复正常颜色 button.setCursor(Cursor.getDefaultCursor()); } public void mousePressed(java.awt.event.MouseEvent evt) { // 点击效果 - 颜色变深并有轻微缩小效果 button.setBackground(new Color(0, 80, 160)); button.setBounds(button.getX() + 1, button.getY() + 1, 78, 23); } public void mouseReleased(java.awt.event.MouseEvent evt) { // 恢复原状 button.setBackground(new Color(0, 150, 255)); button.setBounds(button.getX() - 1, button.getY() - 1, 80, 25); } }); return button; } /** * 获取消息文本,如果资源束中没有则使用默认值 */ private String getMessage(String key, String defaultValue) { try { return messages.getString(key); } catch (Exception e) { return defaultValue; } } // XY坐标转经纬度 private void convertXYToLonLat() { String xy = xyToLonLatTextField.getText().trim(); if (xy.isEmpty()) { JOptionPane.showMessageDialog(this, getMessage("FIELD_REQUIRED", "此字段为必填项"), getMessage("VALIDATION_ERROR", "验证错误"), JOptionPane.ERROR_MESSAGE); return; } // 获取A点数据 String[] pointA = pointATextField.getText().split(",|,"); if (pointA.length < 4) { JOptionPane.showMessageDialog(this, String.format(getMessage("INVALID_POINT_FORMAT2", "点A格式无效"), getMessage("POINT_A", "点A")), getMessage("VALIDATION_ERROR", "验证错误"), JOptionPane.ERROR_MESSAGE); return; } // 获取B点数据 String[] pointB = pointBTextField.getText().split(",|,"); if (pointB.length < 4) { JOptionPane.showMessageDialog(this, String.format(getMessage("INVALID_POINT_FORMAT3", "点B格式无效"), getMessage("POINT_B", "点B")), getMessage("VALIDATION_ERROR", "验证错误"), JOptionPane.ERROR_MESSAGE); return; } try { // 解析输入数据 double latA = Double.parseDouble(pointA[1]); double lonA = Double.parseDouble(pointA[0]); double localXA = Double.parseDouble(pointA[2]); double localYA = Double.parseDouble(pointA[3]); double latB = Double.parseDouble(pointB[1]); double lonB = Double.parseDouble(pointB[0]); double localXB = Double.parseDouble(pointB[2]); double localYB = Double.parseDouble(pointB[3]); // 检查经纬度是否为零 if (latA == 0 || lonA == 0 || latB == 0 || lonB == 0) { JOptionPane.showMessageDialog(this, String.format(getMessage("ZERO_LATLON", "经纬度不能为零"), getMessage("POINT_A", "点A") + "/" + getMessage("POINT_B", "点B")), getMessage("VALIDATION_ERROR", "验证错误"), JOptionPane.ERROR_MESSAGE); return; } String[] xyParts = xy.split(",|,"); if (xyParts.length < 2) { JOptionPane.showMessageDialog(this, getMessage("XY_TO_LONLAT12", "请输入X,Y坐标"), getMessage("VALIDATION_ERROR", "验证错误"), JOptionPane.ERROR_MESSAGE); return; } String localX = xyParts[0]; String localY = xyParts[1]; // 调用转换方法 String[] result = XyToLatLngConverter.convertLocalToGlobalCoordinates( localX, localY, latA, lonA, latB, lonB, localXA, localYA, localXB, localYB ); // 显示转换结果(十进制经纬度) result1TextField.setText(result[3] + "," + result[2]); } catch (NumberFormatException e) { JOptionPane.showMessageDialog(this, getMessage("INVALID_NUMBER", "无效数字格式"), getMessage("VALIDATION_ERROR", "验证错误"), JOptionPane.ERROR_MESSAGE); } catch (Exception e) { JOptionPane.showMessageDialog(this, String.format(getMessage("SAVE_FENCE_ERROR", "保存围栏错误: %s"), e.getMessage()), getMessage("ERROR", "错误"), JOptionPane.ERROR_MESSAGE); } } // 经纬度转XY坐标 private void convertLonLatToXY() { String lonlat = lonLatToXYTextField.getText().trim(); if (lonlat.isEmpty()) { JOptionPane.showMessageDialog(this, getMessage("FIELD_REQUIRED", "此字段为必填项"), getMessage("VALIDATION_ERROR", "验证错误"), JOptionPane.ERROR_MESSAGE); return; } try { // 解析当前点的经纬度 String[] lonLatParts = lonlat.split("[,,]"); if (lonLatParts.length < 2) { JOptionPane.showMessageDialog(this, getMessage("INVALID_LONLAT_FORMAT", "经纬度格式无效"), getMessage("VALIDATION_ERROR", "验证错误"), JOptionPane.ERROR_MESSAGE); return; } String jd1 = lonLatParts[0].trim(); String wd1 = lonLatParts[1].trim(); // 解析A点数据 String[] pointA = pointATextField.getText().split("[,,]"); if (pointA.length < 4) { JOptionPane.showMessageDialog(this, String.format(getMessage("INVALID_POINT_FORMAT", "点格式无效"), getMessage("POINT_A", "点A")), getMessage("VALIDATION_ERROR", "验证错误"), JOptionPane.ERROR_MESSAGE); return; } double latA = Double.parseDouble(pointA[1].trim()); double lonA = Double.parseDouble(pointA[0].trim()); double localXA = Double.parseDouble(pointA[2].trim()); double localYA = Double.parseDouble(pointA[3].trim()); // 解析B点数据 String[] pointB = pointBTextField.getText().split("[,,]"); if (pointB.length < 4) { JOptionPane.showMessageDialog(this, String.format(getMessage("INVALID_POINT_FORMAT", "点格式无效"), getMessage("POINT_B", "点B")), getMessage("VALIDATION_ERROR", "验证错误"), JOptionPane.ERROR_MESSAGE); return; } double latB = Double.parseDouble(pointB[1].trim()); double lonB = Double.parseDouble(pointB[0].trim()); double localXB = Double.parseDouble(pointB[2].trim()); double localYB = Double.parseDouble(pointB[3].trim()); // 检查经纬度是否为零 if (latA == 0 || lonA == 0 || latB == 0 || lonB == 0) { JOptionPane.showMessageDialog(this, String.format(getMessage("ZERO_LATLON", "经纬度不能为零"), getMessage("POINT_A", "点A") + "/" + getMessage("POINT_B", "点B")), getMessage("VALIDATION_ERROR", "验证错误"), JOptionPane.ERROR_MESSAGE); return; } // 调用转换方法 int[] xyResult = CoordinateTranslator.gps_xy( new String[]{String.valueOf(lonA), String.valueOf(latA), String.valueOf(localXA), String.valueOf(localYA)}, new String[]{String.valueOf(lonB), String.valueOf(latB), String.valueOf(localXB), String.valueOf(localYB)}, jd1, wd1 ); // 显示转换结果 (X, Y) result2TextField.setText(xyResult[1] + ", " + xyResult[0]); } catch (NumberFormatException e) { JOptionPane.showMessageDialog(this, getMessage("INVALID_NUMBER", "无效数字格式"), getMessage("VALIDATION_ERROR", "验证错误"), JOptionPane.ERROR_MESSAGE); } catch (Exception e) { JOptionPane.showMessageDialog(this, String.format(getMessage("CONVERSION_ERROR", "转换错误: %s"), e.getMessage()), getMessage("ERROR", "错误"), JOptionPane.ERROR_MESSAGE); } } /** * 计算两点间距离 */ private void calculateDistance() { String point1 = point1RawTextField.getText().trim(); String point2 = point2RawTextField.getText().trim(); if (point1.isEmpty() || point2.isEmpty()) { JOptionPane.showMessageDialog(this, getMessage("FIELD_REQUIRED", "此字段为必填项"), getMessage("VALIDATION_ERROR", "验证错误"), JOptionPane.ERROR_MESSAGE); return; } parseNMEAData(point1, 1); parseNMEAData(point2, 2); try { // 解析NMEA数据获取经纬度 String[] fields1 = point1.split(","); String[] fields2 = point2.split(","); // 确保有足够字段 (纬度在索引2,经度在索引4) if (fields1.length < 6 || fields2.length < 6) { JOptionPane.showMessageDialog(this, getMessage("INVALID_NMEA_FORMAT", "NMEA数据格式无效"), getMessage("VALIDATION_ERROR", "验证错误"), JOptionPane.ERROR_MESSAGE); return; } // 提取点1的经纬度 double lat1 = Double.parseDouble(fields1[2]); double lon1 = Double.parseDouble(fields1[4]); // 提取点2的经纬度 double lat2 = Double.parseDouble(fields2[2]); double lon2 = Double.parseDouble(fields2[4]); // 使用CoordinateTranslator计算距离 (厘米) int distanceCm = CoordinateTranslator.distance2(lon1, lat1, lon2, lat2); // 转换为米并保留两位小数 double distanceMeters = distanceCm / 100.0; String formattedDistance = String.format(Locale.US, "%.2f %s", distanceMeters, getMessage("METERS", "米")); // 显示计算结果 distanceResultTextField.setText(formattedDistance); } catch (NumberFormatException e) { JOptionPane.showMessageDialog(this, getMessage("INVALID_NUMBER", "无效数字格式"), getMessage("VALIDATION_ERROR", "验证错误"), JOptionPane.ERROR_MESSAGE); } catch (Exception e) { JOptionPane.showMessageDialog(this, String.format(getMessage("CALCULATION_ERROR", "计算错误: %s"), e.getMessage()), getMessage("ERROR", "错误"), JOptionPane.ERROR_MESSAGE); } } /** * 解析NMEA数据到表格 */ private void parseNMEAData(String nmea, int pointIndex) { String[] fields = nmea.split(","); int column = pointIndex; // 填充表格数据 for (int i = 0; i < Math.min(fields.length, tableModel.getRowCount()); i++) { tableModel.setValueAt(fields[i], i, column); } } }