| | |
| | | # å²èæºå°åéç¢ç©é
ç½®æä»¶ |
| | | # çææ¶é´ï¼2025-12-17T12:03:32.881913900 |
| | | # çææ¶é´ï¼2025-12-17T13:28:00.386604500 |
| | | # åæ ç³»ï¼WGS84ï¼åº¦åæ ¼å¼ï¼ |
| | | |
| | | # ============ å°ååºåç«é
ç½® ============ |
| | |
| | | # æ ¼å¼ï¼plot.[å°åç¼å·].obstacle.[éç¢ç©åç§°].originalCoords=[åæ ä¸²] |
| | | # æ ¼å¼ï¼plot.[å°åç¼å·].obstacle.[éç¢ç©åç§°].xyCoords=[åæ ä¸²] |
| | | |
| | | # --- å°åLAND1çéç¢ç© --- |
| | | plot.LAND1.obstacle.éç¢ç©1.shape=1 |
| | | plot.LAND1.obstacle.éç¢ç©1.originalCoords=0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E;0.000000,N;0.000000,E |
| | | plot.LAND1.obstacle.éç¢ç©1.xyCoords=81.22,-22.17;81.21,-22.17;81.19,-22.18;81.20,-22.19;81.18,-22.21;81.07,-22.54;80.94,-23.05;80.44,-23.54;79.84,-23.83;79.27,-24.19;78.63,-24.52;78.11,-24.79;77.69,-25.22;77.43,-25.75;77.15,-26.45;77.08,-27.23;77.10,-27.92;77.08,-28.67;77.08,-29.32;77.15,-29.97;77.27,-30.57;77.53,-31.19;77.92,-31.45;78.22,-31.73;78.92,-31.87;79.65,-31.77;80.35,-31.60;80.90,-31.17;81.27,-30.63;81.70,-30.11;81.90,-29.48;81.95,-28.92;81.90,-28.38;81.73,-27.83;81.73,-27.30;81.52,-26.81;81.42,-26.24;81.26,-25.76;81.27,-25.25;81.04,-25.07;81.11,-24.98;81.11,-25.00;81.12,-25.00 |
| | | |
| | |
| | | #Dikuai Properties |
| | | #Wed Dec 17 12:03:32 CST 2025 |
| | | #Wed Dec 17 13:50:43 CST 2025 |
| | | LAND1.angleThreshold=-1 |
| | | LAND1.baseStationCoordinates=3949.90238860,N,11616.75692000,E |
| | | LAND1.boundaryCoordinates=77.19,-32.68;80.71,-54.97;80.99,-55.90;83.54,-56.46;85.04,-55.55;85.94,-53.74;83.24,-35.82;84.55,-34.54;94.02,-31.92;94.10,-31.11;90.88,-20.39;90.35,-19.53;88.33,-19.00;84.12,-19.47;78.92,-22.36;76.63,-25.55;76.93,-29.84;77.06,-31.26;77.19,-32.68 |
| | |
| | | LAND1.mowingPattern=å¹³è¡çº¿ |
| | | LAND1.mowingTrack= |
| | | LAND1.mowingWidth=40 |
| | | LAND1.plannedPath=77.17,-29.65;81.28,-55.71;81.70,-55.80;76.93,-25.57;77.26,-25.10;82.12,-55.89;82.54,-55.98;77.59,-24.64;77.92,-24.18;82.96,-56.08;83.38,-56.17;78.25,-23.72;78.59,-23.25;83.76,-56.03;84.13,-55.81;78.92,-22.79;79.27,-22.45;84.50,-55.58;84.87,-55.33;79.64,-22.24;80.01,-22.04;85.18,-54.72;85.48,-54.10;80.39,-21.83;80.76,-21.62;83.00,-35.83;83.34,-35.38;81.13,-21.42;81.50,-21.21;83.69,-35.03;84.04,-34.69;81.88,-21.00;82.25,-20.80;84.39,-34.35;84.77,-34.22;82.62,-20.59;82.99,-20.38;85.16,-34.11;85.55,-34.00;83.37,-20.18;83.74,-19.97;85.94,-33.90;86.32,-33.79;84.11,-19.76;84.50,-19.68;86.71,-33.68;87.10,-33.57;84.90,-19.63;85.30,-19.59;87.49,-33.47;87.88,-33.36;85.70,-19.55;86.09,-19.50;88.26,-33.25;88.65,-33.15;86.49,-19.46;86.89,-19.41;89.04,-33.04;89.43,-32.93;87.29,-19.37;87.69,-19.32;89.82,-32.82;90.20,-32.72;88.08,-19.28;88.49,-19.30;90.59,-32.61;90.98,-32.50;88.91,-19.41;89.34,-19.52;91.37,-32.39;91.76,-32.29;89.76,-19.63;90.18,-19.74;92.14,-32.18;92.53,-32.07;90.76,-20.88;91.62,-23.72;92.92,-31.96;93.31,-31.86;92.47,-26.56;93.33,-29.40;93.70,-31.75 |
| | | LAND1.plannedPath=77.45,-31.44;81.28,-55.71;81.70,-55.80;77.91,-31.78;78.05,-31.91;78.17,-31.98;78.35,-32.01;82.12,-55.89;82.54,-55.98;78.77,-32.09;78.95,-32.12;79.17,-32.09;82.96,-56.08;83.38,-56.17;79.57,-32.03;79.96,-31.95;83.76,-56.03;84.13,-55.81;80.35,-31.86;80.50,-31.80;80.72,-31.63;84.50,-55.58;84.87,-55.33;81.08,-31.34;81.41,-30.87;85.18,-54.72;85.48,-54.10;81.75,-30.45;81.89,-30.27;81.94,-30.19;82.05,-29.82;83.00,-35.83;83.34,-35.38;81.43,-23.31;81.47,-22.18;81.43,-22.04;81.32,-21.94;81.21,-21.92;81.13,-21.42;81.50,-21.21;83.69,-35.03;84.04,-34.69;81.88,-21.00;82.25,-20.80;84.39,-34.35;84.77,-34.22;82.62,-20.59;82.99,-20.38;85.16,-34.11;85.55,-34.00;83.37,-20.18;83.74,-19.97;85.94,-33.90;86.32,-33.79;84.11,-19.76;84.50,-19.68;86.71,-33.68;87.10,-33.57;84.90,-19.63;85.30,-19.59;87.49,-33.47;87.88,-33.36;85.70,-19.55;86.09,-19.50;88.26,-33.25;88.65,-33.15;86.49,-19.46;86.89,-19.41;89.04,-33.04;89.43,-32.93;87.29,-19.37;87.69,-19.32;89.82,-32.82;90.20,-32.72;88.08,-19.28;88.49,-19.30;90.59,-32.61;90.98,-32.50;88.91,-19.41;89.34,-19.52;91.37,-32.39;91.76,-32.29;89.76,-19.63;90.18,-19.74;92.14,-32.18;92.53,-32.07;90.76,-20.88;91.62,-23.72;92.92,-31.96;93.31,-31.86;92.47,-26.56;93.33,-29.40;93.70,-31.75;81.40,-22.00;81.27,-21.93;81.21,-21.92;81.08,-21.96;80.98,-22.05;79.85,-23.55;79.64,-22.24;80.01,-22.04;80.22,-23.37;80.58,-23.05;80.39,-21.83;80.76,-21.62;80.87,-22.34;77.96,-24.59;77.63,-24.92;77.59,-24.64;77.92,-24.18;77.99,-24.57;78.36,-24.38;78.25,-23.72;78.59,-23.25;78.73,-24.19;79.11,-23.99;78.92,-22.79;79.27,-22.45;79.48,-23.76;77.99,-24.57;77.93,-24.62;77.51,-25.05;77.47,-25.11;77.20,-25.66;77.01,-26.12;76.93,-25.57;77.26,-25.10;77.31,-25.43 |
| | | LAND1.returnPointCoordinates=-1 |
| | | LAND1.updateTime=2025-12-17 12\:03\:32 |
| | | LAND1.updateTime=2025-12-17 13\:50\:43 |
| | | LAND1.userId=-1 |
| | |
| | | #Mower Configuration Properties - Updated |
| | | #Wed Dec 17 12:04:00 CST 2025 |
| | | #Wed Dec 17 14:56:25 CST 2025 |
| | | appVersion=-1 |
| | | currentWorkLandNumber=LAND1 |
| | | cuttingWidth=200 |
| | | firmwareVersion=-1 |
| | | handheldMarkerId= |
| | | idleTrailDurationSeconds=60 |
| | | mapScale=15.31 |
| | | mapScale=12.92 |
| | | mowerId=1234 |
| | | serialAutoConnect=true |
| | | serialBaudRate=115200 |
| | |
| | | |
| | | // å°åç¼å· |
| | | contentPanel.add(createCardInfoItem("å°åç¼å·:", getDisplayValue(dikuai.getLandNumber(), "æªç¥"))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); |
| | | |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | |
| | | // æ·»å æ¶é´ |
| | | contentPanel.add(createCardInfoItem("æ·»å æ¶é´:", getDisplayValue(dikuai.getCreateTime(), "æªç¥"))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); |
| | | |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | |
| | | // å°åé¢ç§¯ |
| | | String landArea = dikuai.getLandArea(); |
| | | if (landArea != null && !landArea.equals("-1")) { |
| | |
| | | landArea = "æªç¥"; |
| | | } |
| | | contentPanel.add(createCardInfoItem("å°åé¢ç§¯:", landArea)); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); |
| | | |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | |
| | | // è¿åç¹åæ ï¼å¸¦ä¿®æ¹æé®ï¼ |
| | | contentPanel.add(createCardInfoItemWithButton("è¿åç¹åæ :", |
| | | getDisplayValue(dikuai.getReturnPointCoordinates(), "æªè®¾ç½®"), |
| | | contentPanel.add(createCardInfoItemWithButton("è¿åç¹åæ :", |
| | | getDisplayValue(dikuai.getReturnPointCoordinates(), "æªè®¾ç½®"), |
| | | "ä¿®æ¹", e -> editReturnPoint(dikuai))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); |
| | | |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | |
| | | // å°åè¾¹çåæ ï¼å¸¦æ¾ç¤ºé¡¶ç¹æé®ï¼ |
| | | JPanel boundaryPanel = createBoundaryInfoItem(dikuai, |
| | | getTruncatedValue(dikuai.getBoundaryCoordinates(), 12, "æªè®¾ç½®")); |
| | |
| | | () -> editBoundaryCoordinates(dikuai), |
| | | "ç¹å»æ¥ç/ç¼è¾å°åè¾¹çåæ "); |
| | | contentPanel.add(boundaryPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); |
| | | |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | |
| | | ObstacleSummary obstacleSummary = getObstacleSummaryFromCache(dikuai.getLandNumber()); |
| | | JPanel obstaclePanel = createCardInfoItemWithButton("éç¢ç©:", |
| | | obstacleSummary.buildDisplayValue(), |
| | | "æ°å¢", |
| | | e -> addNewObstacle(dikuai)); |
| | | setInfoItemTooltip(obstaclePanel, obstacleSummary.buildTooltip()); |
| | | // 让éç¢ç©æ é¢å¯ç¹å»ï¼æå¼éç¢ç©ç®¡çé¡µé¢ |
| | | configureInteractiveLabel(getInfoItemTitleLabel(obstaclePanel), |
| | | () -> showObstacleManagementPage(dikuai), |
| | | "ç¹å»æ¥ç/管çéç¢ç©"); |
| | | contentPanel.add(obstaclePanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | |
| | | // è·¯å¾åæ ï¼å¸¦æ¥çæé®ï¼ |
| | | JPanel pathPanel = createCardInfoItemWithButton("è·¯å¾åæ :", |
| | | getTruncatedValue(dikuai.getPlannedPath(), 12, "æªè®¾ç½®"), |
| | | JPanel pathPanel = createCardInfoItemWithButton("è·¯å¾åæ :", |
| | | getTruncatedValue(dikuai.getPlannedPath(), 12, "æªè®¾ç½®"), |
| | | "å¤å¶", e -> copyCoordinatesAction("è·¯å¾åæ ", dikuai.getPlannedPath())); |
| | | setInfoItemTooltip(pathPanel, dikuai.getPlannedPath()); |
| | | configureInteractiveLabel(getInfoItemTitleLabel(pathPanel), |
| | | () -> editPlannedPath(dikuai), |
| | | "ç¹å»æ¥ç/ç¼è¾è·¯å¾åæ "); |
| | | contentPanel.add(pathPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | |
| | | JPanel baseStationPanel = createCardInfoItemWithButton("åºç«åæ :", |
| | | getTruncatedValue(dikuai.getBaseStationCoordinates(), 12, "æªè®¾ç½®"), |
| | |
| | | () -> editBaseStationCoordinates(dikuai), |
| | | "ç¹å»æ¥ç/ç¼è¾åºç«åæ "); |
| | | contentPanel.add(baseStationPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | |
| | | JPanel boundaryOriginalPanel = createCardInfoItemWithButton("è¾¹çåå§åæ :", |
| | | getTruncatedValue(dikuai.getBoundaryOriginalCoordinates(), 12, "æªè®¾ç½®"), |
| | |
| | | () -> editBoundaryOriginalCoordinates(dikuai), |
| | | "ç¹å»æ¥ç/ç¼è¾è¾¹çåå§åæ "); |
| | | contentPanel.add(boundaryOriginalPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | |
| | | JPanel mowingPatternPanel = createCardInfoItemWithButton("å²è模å¼:", |
| | | getTruncatedValue(dikuai.getMowingPattern(), 12, "æªè®¾ç½®"), |
| | |
| | | () -> editMowingPattern(dikuai), |
| | | "ç¹å»æ¥ç/ç¼è¾å²è模å¼"); |
| | | contentPanel.add(mowingPatternPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | |
| | | String mowingWidthValue = dikuai.getMowingWidth(); |
| | | String widthSource = null; |
| | |
| | | "ç¼è¾", e -> editMowingWidth(dikuai)); |
| | | setInfoItemTooltip(mowingWidthPanel, widthSource); |
| | | contentPanel.add(mowingWidthPanel); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 20))); |
| | | contentPanel.add(Box.createRigidArea(new Dimension(0, 15))); |
| | | |
| | | JPanel completedTrackPanel = createCardInfoItemWithButton("已宿å²èè·¯å¾:", |
| | | getTruncatedValue(dikuai.getMowingTrack(), 12, "æªè®°å½"), |
| | |
| | | private JPanel createCardInfoItemWithButton(String label, String value, String buttonText, ActionListener listener) { |
| | | JPanel itemPanel = new JPanel(new BorderLayout()); |
| | | itemPanel.setBackground(CARD_BACKGROUND); |
| | | itemPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 20)); |
| | | // å¢å é«åº¦ä»¥ç¡®ä¿æé®å®æ´æ¾ç¤ºï¼æé®é«åº¦çº¦24-28åç´ ï¼å ä¸ä¸ä¸è¾¹è·ï¼ |
| | | itemPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 35)); |
| | | itemPanel.setPreferredSize(new Dimension(Integer.MAX_VALUE, 30)); |
| | | itemPanel.setMinimumSize(new Dimension(0, 28)); |
| | | |
| | | JLabel labelComp = new JLabel(label); |
| | | labelComp.setFont(new Font("微软é
é»", Font.PLAIN, 14)); |
| | |
| | | |
| | | JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0)); |
| | | rightPanel.setBackground(CARD_BACKGROUND); |
| | | // æ·»å åç´å
è¾¹è·ä»¥ç¡®ä¿æé®ä¸è¢«è£åª |
| | | rightPanel.setBorder(BorderFactory.createEmptyBorder(2, 0, 2, 0)); |
| | | |
| | | JLabel valueComp = new JLabel(value); |
| | | valueComp.setFont(new Font("微软é
é»", Font.PLAIN, 14)); |
| | | valueComp.setForeground(TEXT_COLOR); |
| | | |
| | | JButton button = createSmallButton(buttonText); |
| | | button.addActionListener(listener); |
| | | JButton button = createSmallLinkButton(buttonText, listener); |
| | | |
| | | rightPanel.add(valueComp); |
| | | rightPanel.add(button); |
| | |
| | | private JPanel createBoundaryInfoItem(Dikuai dikuai, String displayValue) { |
| | | JPanel itemPanel = new JPanel(new BorderLayout()); |
| | | itemPanel.setBackground(CARD_BACKGROUND); |
| | | int rowHeight = Math.max(36, BOUNDARY_TOGGLE_ICON_SIZE + 12); |
| | | // å¢å é«åº¦ä»¥ç¡®ä¿æé®ä¸è¾¹ç¼å®æ´æ¾ç¤ºï¼æé®é«åº¦56ï¼å ä¸ä¸ä¸è¾¹è·ï¼ |
| | | int rowHeight = Math.max(60, BOUNDARY_TOGGLE_ICON_SIZE + 16); |
| | | Dimension rowDimension = new Dimension(Integer.MAX_VALUE, rowHeight); |
| | | itemPanel.setMaximumSize(rowDimension); |
| | | itemPanel.setPreferredSize(rowDimension); |
| | | itemPanel.setMinimumSize(new Dimension(0, 32)); |
| | | itemPanel.setMinimumSize(new Dimension(0, 56)); |
| | | |
| | | JLabel labelComp = new JLabel("å°åè¾¹ç:"); |
| | | labelComp.setFont(new Font("微软é
é»", Font.PLAIN, 14)); |
| | | labelComp.setForeground(LIGHT_TEXT); |
| | | |
| | | int verticalPadding = Math.max(0, (rowHeight - BOUNDARY_TOGGLE_ICON_SIZE) / 2); |
| | | // ç¡®ä¿æé®æè¶³å¤çä¸ä¸è¾¹è·ï¼é¿å
ä¸è¾¹ç¼è¢«è£åª |
| | | int verticalPadding = Math.max(2, (rowHeight - BOUNDARY_TOGGLE_ICON_SIZE) / 2); |
| | | JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0)); |
| | | rightPanel.setBackground(CARD_BACKGROUND); |
| | | rightPanel.setBorder(BorderFactory.createEmptyBorder(verticalPadding, 0, verticalPadding, 0)); |
| | |
| | | } |
| | | |
| | | Window owner = SwingUtilities.getWindowAncestor(this); |
| | | |
| | | // è·åå°å管çå¯¹è¯æ¡ï¼åå¤å¨æå¼è·¯å¾è§å页颿¶å
³é |
| | | Window managementWindow = null; |
| | | if (owner instanceof JDialog) { |
| | | managementWindow = owner; |
| | | } |
| | | |
| | | // è·åå°ååºæ¬æ°æ® |
| | | String baseStationValue = prepareCoordinateForEditor(dikuai.getBaseStationCoordinates()); |
| | |
| | | callback |
| | | ); |
| | | |
| | | // å
³éå°å管çé¡µé¢ |
| | | if (managementWindow != null) { |
| | | managementWindow.dispose(); |
| | | } |
| | | |
| | | dialog.setVisible(true); |
| | | } |
| | | |
| | |
| | | return value; |
| | | } |
| | | |
| | | /** |
| | | * å建类似äºé¾æ¥çå°æé® |
| | | */ |
| | | private JButton createSmallLinkButton(String text, ActionListener listener) { |
| | | JButton btn = new JButton(text); |
| | | btn.setFont(new Font("微软é
é»", Font.PLAIN, 11)); |
| | | btn.setForeground(PRIMARY_COLOR); |
| | | btn.setBorder(BorderFactory.createCompoundBorder( |
| | | BorderFactory.createLineBorder(PRIMARY_COLOR, 1, true), |
| | | BorderFactory.createEmptyBorder(2, 6, 2, 6) |
| | | )); |
| | | btn.setContentAreaFilled(false); |
| | | btn.setFocusPainted(false); |
| | | btn.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
| | | btn.addMouseListener(new MouseAdapter() { |
| | | public void mouseEntered(MouseEvent e) { btn.setOpaque(true); btn.setBackground(new Color(230, 250, 240)); } |
| | | public void mouseExited(MouseEvent e) { btn.setOpaque(false); } |
| | | }); |
| | | if (listener != null) { |
| | | btn.addActionListener(listener); |
| | | } |
| | | return btn; |
| | | } |
| | | |
| | | private JButton createSmallButton(String text) { |
| | | return createSmallButton(text, PRIMARY_COLOR, PRIMARY_DARK); |
| | | return createSmallLinkButton(text, null); |
| | | } |
| | | |
| | | private JButton createSmallButton(String text, Color backgroundColor, Color hoverColor) { |
| | | JButton button = new JButton(text); |
| | | button.setFont(new Font("微软é
é»", Font.PLAIN, 12)); |
| | | // 对äºéè¦ä¸åé¢è²çæé®ï¼ä½¿ç¨å®å¿é£æ ¼ |
| | | Color baseColor = backgroundColor == null ? PRIMARY_COLOR : backgroundColor; |
| | | Color hover = hoverColor == null ? baseColor : hoverColor; |
| | | button.setBackground(baseColor); |
| | | button.setForeground(WHITE); |
| | | button.setBorder(BorderFactory.createEmptyBorder(2, 10, 2, 10)); |
| | | button.setMargin(new Insets(0, 0, 0, 0)); |
| | | button.setFocusPainted(false); |
| | | button.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
| | | |
| | | button.addMouseListener(new MouseAdapter() { |
| | | public void mouseEntered(MouseEvent e) { |
| | | button.setBackground(hover); |
| | | } |
| | | public void mouseExited(MouseEvent e) { |
| | | button.setBackground(baseColor); |
| | | } |
| | | }); |
| | | |
| | | return button; |
| | | return createStyledButton(text, baseColor, true); |
| | | } |
| | | |
| | | private JButton createActionButton(String text, Color color) { |
| | | JButton button = new JButton(text); |
| | | button.setFont(new Font("微软é
é»", Font.PLAIN, 14)); |
| | | button.setBackground(color); |
| | | button.setForeground(WHITE); |
| | | button.setBorder(BorderFactory.createEmptyBorder(8, 16, 8, 16)); |
| | | button.setFocusPainted(false); |
| | | button.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
| | | return createStyledButton(text, color, true); // å®å¿é£æ ¼ |
| | | } |
| | | |
| | | // æ¬åææ |
| | | button.addMouseListener(new MouseAdapter() { |
| | | public void mouseEntered(MouseEvent e) { |
| | | if (color == RED_COLOR) { |
| | | button.setBackground(RED_DARK); |
| | | /** |
| | | * å建ç°ä»£é£æ ¼æé® (å®å¿/è½®å») |
| | | */ |
| | | private JButton createStyledButton(String text, Color baseColor, boolean filled) { |
| | | JButton btn = new JButton(text) { |
| | | @Override |
| | | protected void paintComponent(Graphics g) { |
| | | Graphics2D g2 = (Graphics2D) g.create(); |
| | | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
| | | |
| | | boolean isPressed = getModel().isPressed(); |
| | | boolean isRollover = getModel().isRollover(); |
| | | |
| | | if (filled) { |
| | | if (isPressed) g2.setColor(baseColor.darker()); |
| | | else if (isRollover) g2.setColor(baseColor.brighter()); |
| | | else g2.setColor(baseColor); |
| | | g2.fillRoundRect(0, 0, getWidth(), getHeight(), 8, 8); |
| | | g2.setColor(Color.WHITE); |
| | | } else { |
| | | button.setBackground(PRIMARY_DARK); |
| | | g2.setColor(CARD_BACKGROUND); // èæ¯ |
| | | g2.fillRoundRect(0, 0, getWidth(), getHeight(), 8, 8); |
| | | |
| | | if (isPressed) g2.setColor(baseColor.darker()); |
| | | else if (isRollover) g2.setColor(baseColor); |
| | | else g2.setColor(new Color(200, 200, 200)); // é»è®¤è¾¹æ¡ç° |
| | | |
| | | g2.setStroke(new BasicStroke(1.2f)); |
| | | g2.drawRoundRect(0, 0, getWidth()-1, getHeight()-1, 8, 8); |
| | | g2.setColor(isRollover ? baseColor : TEXT_COLOR); |
| | | } |
| | | |
| | | FontMetrics fm = g2.getFontMetrics(); |
| | | int x = (getWidth() - fm.stringWidth(getText())) / 2; |
| | | int y = (getHeight() - fm.getHeight()) / 2 + fm.getAscent(); |
| | | g2.drawString(getText(), x, y); |
| | | |
| | | g2.dispose(); |
| | | } |
| | | public void mouseExited(MouseEvent e) { |
| | | if (color == RED_COLOR) { |
| | | button.setBackground(RED_COLOR); |
| | | } else { |
| | | button.setBackground(PRIMARY_COLOR); |
| | | } |
| | | } |
| | | }); |
| | | |
| | | return button; |
| | | }; |
| | | btn.setFocusPainted(false); |
| | | btn.setContentAreaFilled(false); |
| | | btn.setBorderPainted(false); |
| | | btn.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
| | | btn.setFont(new Font("微软é
é»", Font.BOLD, 12)); |
| | | return btn; |
| | | } |
| | | |
| | | private JButton createDeleteButton() { |
| | | JButton button = new JButton("å é¤"); |
| | | button.setFont(new Font("微软é
é»", Font.PLAIN, 12)); |
| | | button.setBackground(RED_COLOR); |
| | | button.setForeground(WHITE); |
| | | button.setBorder(BorderFactory.createEmptyBorder(6, 12, 6, 12)); |
| | | button.setFocusPainted(false); |
| | | button.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
| | | |
| | | JButton button = createStyledButton("å é¤", RED_COLOR, false); // è½®å»é£æ ¼ |
| | | ImageIcon deleteIcon = loadIcon("image/delete.png", 16, 16); |
| | | if (deleteIcon != null) { |
| | | button.setIcon(deleteIcon); |
| | | button.setIconTextGap(6); |
| | | } |
| | | |
| | | // æ¬åææ |
| | | button.addMouseListener(new MouseAdapter() { |
| | | public void mouseEntered(MouseEvent e) { |
| | | button.setBackground(RED_DARK); |
| | | } |
| | | public void mouseExited(MouseEvent e) { |
| | | button.setBackground(RED_COLOR); |
| | | } |
| | | }); |
| | | |
| | | return button; |
| | | } |
| | | |
| | | 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 MouseAdapter() { |
| | | public void mouseEntered(MouseEvent e) { |
| | | button.setBackground(PRIMARY_DARK); |
| | | } |
| | | |
| | | public void mouseExited(MouseEvent e) { |
| | | button.setBackground(PRIMARY_COLOR); |
| | | } |
| | | }); |
| | | |
| | | return button; |
| | | return createStyledButton(text, PRIMARY_COLOR, true); // å®å¿é£æ ¼ |
| | | } |
| | | |
| | | private JButton createWorkToggleButton(Dikuai dikuai) { |
| | |
| | | boolean showBoundaryPoints = sanitizedLandNumber != null && boundaryPointVisibility.getOrDefault(sanitizedLandNumber, false); |
| | | renderer.setBoundaryPointsVisible(showBoundaryPoints); |
| | | renderer.setBoundaryPointSizeScale(showBoundaryPoints ? 0.5d : 1.0d); |
| | | // éåºé¢è§åï¼ä¸æ¾ç¤ºéç¢ç©ç¹ï¼éç¢ç©ç¹åªå¨é¢è§æ¶æ¾ç¤ºï¼ |
| | | renderer.setObstaclePointsVisible(false); |
| | | } |
| | | shouye.refreshMowingIndicators(); |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ¾ç¤ºéç¢ç©ç®¡çé¡µé¢ |
| | | */ |
| | | private void showObstacleManagementPage(Dikuai dikuai) { |
| | | if (dikuai == null) { |
| | | return; |
| | | } |
| | | Window owner = SwingUtilities.getWindowAncestor(this); |
| | | |
| | | // è·åå°å管çå¯¹è¯æ¡ï¼åå¤å¨æå¼éç¢ç©ç®¡ç页颿¶å
³é |
| | | Window managementWindow = null; |
| | | if (owner instanceof JDialog) { |
| | | managementWindow = owner; |
| | | } |
| | | |
| | | ObstacleManagementPage managementPage = new ObstacleManagementPage(owner, dikuai); |
| | | |
| | | // å
³éå°å管çé¡µé¢ |
| | | if (managementWindow != null) { |
| | | managementWindow.dispose(); |
| | | } |
| | | |
| | | managementPage.setVisible(true); |
| | | } |
| | | |
| | | private void addNewObstacle(Dikuai dikuai) { |
| | | if (dikuai == null) { |
| | | JOptionPane.showMessageDialog(this, "æªæ¾å°å½åå°åï¼æ æ³æ°å¢éç¢ç©", "æç¤º", JOptionPane.WARNING_MESSAGE); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package dikuai; |
| | | |
| | | import javax.swing.*; |
| | | import javax.swing.border.EmptyBorder; |
| | | import javax.swing.plaf.basic.BasicScrollBarUI; |
| | | import java.awt.*; |
| | | import java.awt.event.MouseAdapter; |
| | | import java.awt.event.MouseEvent; |
| | | import java.awt.geom.RoundRectangle2D; |
| | | import java.io.File; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | import zhangaiwu.Obstacledge; |
| | | import zhuye.Shouye; |
| | | |
| | | /** |
| | | * éç¢ç©ç®¡çé¡µé¢ - UIä¼åç |
| | | */ |
| | | public class ObstacleManagementPage 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_HOVER = new Color(60, 179, 113); // æ¬å绿 |
| | | private static final Color DANGER_COLOR = new Color(220, 53, 69); // è¦å红 |
| | | private static final Color DANGER_HOVER = new Color(200, 35, 51); |
| | | |
| | | private static final Color TEXT_PRIMARY = new Color(33, 37, 41); // ä¸»è¦æå |
| | | private static final Color TEXT_SECONDARY = new Color(108, 117, 125); // æ¬¡è¦æå |
| | | |
| | | private static final Color BG_MAIN = new Color(248, 249, 250); // æ´ä½èæ¯ (ç°ç½) |
| | | private static final Color BG_CARD = Color.WHITE; // å¡çèæ¯ |
| | | private static final Color BG_INPUT = new Color(241, 243, 245); // è¾å
¥æ¡èæ¯ |
| | | |
| | | private static final Color BORDER_LIGHT = new Color(233, 236, 239); // æµ
è¾¹æ¡ |
| | | |
| | | private final Dikuai dikuai; |
| | | private JPanel cardsPanel; |
| | | private JScrollPane scrollPane; |
| | | |
| | | public ObstacleManagementPage(Window owner, Dikuai dikuai) { |
| | | super(owner, "éç¢ç©ç®¡ç", Dialog.ModalityType.APPLICATION_MODAL); |
| | | this.dikuai = dikuai; |
| | | |
| | | initializeWindow(); |
| | | initializeUI(); |
| | | |
| | | // ç¡®ä¿å¸å±å®æååæ¾ç¤ºæ°æ® |
| | | SwingUtilities.invokeLater(this::loadAndDisplayObstacles); |
| | | } |
| | | |
| | | private void initializeWindow() { |
| | | setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); |
| | | setLayout(new BorderLayout()); |
| | | getContentPane().setBackground(BG_MAIN); |
| | | setSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT)); |
| | | setResizable(false); |
| | | setLocationRelativeTo(getOwner()); |
| | | } |
| | | |
| | | private void initializeUI() { |
| | | // 1. é¡¶é¨å¯¼èªæ |
| | | add(createHeader(), BorderLayout.NORTH); |
| | | |
| | | // 2. ä¸é´æ»å¨åºå |
| | | cardsPanel = new JPanel(); |
| | | cardsPanel.setLayout(new BoxLayout(cardsPanel, BoxLayout.Y_AXIS)); |
| | | cardsPanel.setBackground(BG_MAIN); |
| | | cardsPanel.setBorder(new EmptyBorder(15, 15, 15, 15)); // å¤è¾¹è· |
| | | |
| | | scrollPane = new JScrollPane(cardsPanel); |
| | | scrollPane.setBorder(null); // æ è¾¹æ¡ |
| | | scrollPane.setBackground(BG_MAIN); |
| | | scrollPane.getVerticalScrollBar().setUnitIncrement(20); |
| | | |
| | | // èªå®ä¹æ»å¨æ¡æ ·å¼ |
| | | customizeScrollBar(scrollPane); |
| | | |
| | | add(scrollPane, BorderLayout.CENTER); |
| | | } |
| | | |
| | | /** |
| | | * åå»ºé¡¶é¨æ 颿 |
| | | */ |
| | | private JPanel createHeader() { |
| | | JPanel header = new JPanel(new BorderLayout()); |
| | | header.setBackground(Color.WHITE); |
| | | header.setPreferredSize(new Dimension(SCREEN_WIDTH, 60)); |
| | | header.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, BORDER_LIGHT)); // åºé¨ç»çº¿ |
| | | |
| | | // å·¦ä¾§ä¿¡æ¯ |
| | | JPanel infoPanel = new JPanel(new GridLayout(2, 1)); |
| | | infoPanel.setBackground(Color.WHITE); |
| | | infoPanel.setBorder(new EmptyBorder(8, 20, 8, 0)); |
| | | |
| | | String landName = getDisplayValue(dikuai.getLandName(), "æªç¥å°å"); |
| | | String landNumber = getDisplayValue(dikuai.getLandNumber(), "--"); |
| | | |
| | | JLabel titleLabel = new JLabel(landName); |
| | | titleLabel.setFont(new Font("微软é
é»", Font.BOLD, 16)); |
| | | titleLabel.setForeground(TEXT_PRIMARY); |
| | | |
| | | JLabel subTitleLabel = new JLabel("ç¼å·: " + landNumber); |
| | | subTitleLabel.setFont(new Font("微软é
é»", Font.PLAIN, 12)); |
| | | subTitleLabel.setForeground(TEXT_SECONDARY); |
| | | |
| | | infoPanel.add(titleLabel); |
| | | infoPanel.add(subTitleLabel); |
| | | |
| | | header.add(infoPanel, BorderLayout.CENTER); |
| | | |
| | | // å³ä¾§å
³éæé® |
| | | JButton closeBtn = new JButton("â"); |
| | | closeBtn.setFont(new Font("Arial", Font.BOLD, 18)); |
| | | closeBtn.setForeground(TEXT_SECONDARY); |
| | | closeBtn.setBorder(new EmptyBorder(0, 15, 0, 20)); |
| | | closeBtn.setContentAreaFilled(false); |
| | | closeBtn.setFocusPainted(false); |
| | | closeBtn.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
| | | closeBtn.addMouseListener(new MouseAdapter() { |
| | | public void mouseEntered(MouseEvent e) { closeBtn.setForeground(TEXT_PRIMARY); } |
| | | public void mouseExited(MouseEvent e) { closeBtn.setForeground(TEXT_SECONDARY); } |
| | | }); |
| | | closeBtn.addActionListener(e -> dispose()); |
| | | |
| | | header.add(closeBtn, BorderLayout.EAST); |
| | | |
| | | return header; |
| | | } |
| | | |
| | | /** |
| | | * èªå®ä¹æ»å¨æ¡æ ·å¼ï¼æå¹³åãç»æ¡ï¼ |
| | | */ |
| | | private void customizeScrollBar(JScrollPane scrollPane) { |
| | | scrollPane.getVerticalScrollBar().setUI(new BasicScrollBarUI() { |
| | | @Override |
| | | protected void configureScrollBarColors() { |
| | | this.thumbColor = new Color(200, 200, 200); |
| | | this.trackColor = BG_MAIN; |
| | | } |
| | | |
| | | @Override |
| | | protected JButton createDecreaseButton(int orientation) { |
| | | return createZeroButton(); |
| | | } |
| | | |
| | | @Override |
| | | protected JButton createIncreaseButton(int orientation) { |
| | | return createZeroButton(); |
| | | } |
| | | |
| | | private JButton createZeroButton() { |
| | | JButton jbutton = new JButton(); |
| | | jbutton.setPreferredSize(new Dimension(0, 0)); |
| | | jbutton.setMinimumSize(new Dimension(0, 0)); |
| | | jbutton.setMaximumSize(new Dimension(0, 0)); |
| | | return jbutton; |
| | | } |
| | | |
| | | @Override |
| | | protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { |
| | | Graphics2D g2 = (Graphics2D) g.create(); |
| | | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
| | | g2.setColor(thumbColor); |
| | | g2.fillRoundRect(thumbBounds.x + 2, thumbBounds.y, thumbBounds.width - 4, thumbBounds.height, 6, 6); |
| | | g2.dispose(); |
| | | } |
| | | }); |
| | | scrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(8, 0)); |
| | | } |
| | | |
| | | /** |
| | | * å è½½æ°æ® |
| | | */ |
| | | private void loadAndDisplayObstacles() { |
| | | cardsPanel.removeAll(); |
| | | |
| | | List<Obstacledge.Obstacle> obstacles = loadObstacles(); |
| | | |
| | | if (obstacles.isEmpty()) { |
| | | showEmptyState(); |
| | | } else { |
| | | for (Obstacledge.Obstacle obstacle : obstacles) { |
| | | cardsPanel.add(createObstacleCard(obstacle)); |
| | | cardsPanel.add(Box.createVerticalStrut(15)); // å¡çé´è· |
| | | } |
| | | } |
| | | |
| | | cardsPanel.revalidate(); |
| | | cardsPanel.repaint(); |
| | | } |
| | | |
| | | private void showEmptyState() { |
| | | JPanel emptyPanel = new JPanel(); |
| | | emptyPanel.setLayout(new BoxLayout(emptyPanel, BoxLayout.Y_AXIS)); |
| | | emptyPanel.setBackground(BG_MAIN); |
| | | emptyPanel.setAlignmentX(Component.CENTER_ALIGNMENT); |
| | | |
| | | JLabel iconLabel = new JLabel("ð²"); // ç®åçå ä½ç¬¦ï¼å¦ææå¾çæ´å¥½ |
| | | iconLabel.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 48)); |
| | | iconLabel.setAlignmentX(Component.CENTER_ALIGNMENT); |
| | | |
| | | JLabel textLabel = new JLabel("ææ éç¢ç©æ°æ®"); |
| | | textLabel.setFont(new Font("微软é
é»", Font.PLAIN, 14)); |
| | | textLabel.setForeground(TEXT_SECONDARY); |
| | | textLabel.setAlignmentX(Component.CENTER_ALIGNMENT); |
| | | |
| | | emptyPanel.add(Box.createVerticalStrut(50)); |
| | | emptyPanel.add(iconLabel); |
| | | emptyPanel.add(Box.createVerticalStrut(10)); |
| | | emptyPanel.add(textLabel); |
| | | |
| | | cardsPanel.add(emptyPanel); |
| | | } |
| | | |
| | | /** |
| | | * å建å个éç¢ç©å¡ç |
| | | */ |
| | | private JPanel createObstacleCard(Obstacledge.Obstacle obstacle) { |
| | | // å¡çå®¹å¨ - 使ç¨åè§ç©å½¢èæ¯ |
| | | JPanel card = new JPanel() { |
| | | @Override |
| | | protected void paintComponent(Graphics g) { |
| | | Graphics2D g2 = (Graphics2D) g.create(); |
| | | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
| | | g2.setColor(BG_CARD); |
| | | // ç»å¶åè§ç©å½¢èæ¯ |
| | | g2.fillRoundRect(0, 0, getWidth() - 1, getHeight() - 1, 12, 12); |
| | | // ç»å¶æ·¡æ·¡çè¾¹æ¡ |
| | | g2.setColor(BORDER_LIGHT); |
| | | g2.drawRoundRect(0, 0, getWidth() - 1, getHeight() - 1, 12, 12); |
| | | g2.dispose(); |
| | | } |
| | | }; |
| | | card.setLayout(new BoxLayout(card, BoxLayout.Y_AXIS)); |
| | | card.setOpaque(false); // éæä»¥æ¾ç¤º paintComponent ææ |
| | | card.setBorder(new EmptyBorder(15, 15, 15, 15)); // å¡çå
é¨å
è¾¹è· |
| | | card.setMaximumSize(new Dimension(Integer.MAX_VALUE, 380)); // éå¶é«åº¦ï¼é²æ¢æä¼¸ |
| | | |
| | | // 1. å¡çæ é¢è¡ (åç§° + å½¢ç¶ + å é¤) |
| | | JPanel titleRow = new JPanel(new BorderLayout()); |
| | | titleRow.setOpaque(false); |
| | | |
| | | String name = (obstacle.getObstacleName() == null || obstacle.getObstacleName().isEmpty()) |
| | | ? "æªå½å" : obstacle.getObstacleName(); |
| | | JLabel nameLabel = new JLabel(name); |
| | | nameLabel.setFont(new Font("微软é
é»", Font.BOLD, 15)); |
| | | nameLabel.setForeground(TEXT_PRIMARY); |
| | | // æ·»å ä¸ä¸ªå°å¾æ è£
饰 |
| | | nameLabel.setIcon(new Icon() { |
| | | public void paintIcon(Component c, Graphics g, int x, int y) { |
| | | g.setColor(PRIMARY_COLOR); |
| | | g.fillOval(x, y + 4, 8, 8); |
| | | } |
| | | public int getIconWidth() { return 12; } |
| | | public int getIconHeight() { return 16; } |
| | | }); |
| | | |
| | | // æ·»å å½¢ç¶æ¾ç¤ºï¼å¨åç§°å³è¾¹ï¼ |
| | | JLabel shapeLabel = createShapeLabel(obstacle.getShape()); |
| | | |
| | | // 左侧颿¿ï¼åç§° + å½¢ç¶ |
| | | JPanel leftPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 0)); |
| | | leftPanel.setOpaque(false); |
| | | leftPanel.add(nameLabel); |
| | | leftPanel.add(shapeLabel); |
| | | |
| | | JButton deleteBtn = createIconButton("ð", DANGER_COLOR); // 使ç¨Unicodeå徿¡¶ |
| | | deleteBtn.addActionListener(e -> deleteObstacle(obstacle)); |
| | | |
| | | titleRow.add(leftPanel, BorderLayout.CENTER); |
| | | titleRow.add(deleteBtn, BorderLayout.EAST); |
| | | |
| | | card.add(titleRow); |
| | | card.add(Box.createVerticalStrut(12)); |
| | | |
| | | // åå²çº¿ |
| | | JSeparator sep = new JSeparator(); |
| | | sep.setForeground(BORDER_LIGHT); |
| | | sep.setMaximumSize(new Dimension(Integer.MAX_VALUE, 1)); |
| | | card.add(sep); |
| | | card.add(Box.createVerticalStrut(12)); |
| | | |
| | | // 2. åå§åæ åºå |
| | | String originalCoords = extractOriginalCoordinates(obstacle); |
| | | card.add(createDataField("åå§ç»çº¬åº¦æ°æ®", originalCoords, 2)); |
| | | card.add(Box.createVerticalStrut(10)); |
| | | |
| | | // 3. çæåæ åºå |
| | | String genCoords = extractObstacleCoordinates(obstacle); |
| | | JTextArea xyArea = createDataTextArea(genCoords, 3); // å¼ç¨ä»¥ä¾¿æ´æ° |
| | | JScrollPane scrollXY = new JScrollPane(xyArea); |
| | | scrollXY.setBorder(BorderFactory.createEmptyBorder()); // å¤é¨ç±Panelæä¾è¾¹æ¡ |
| | | |
| | | JPanel xyWrapper = createWrapperPanel("çæåæ (XYç±³)", scrollXY); |
| | | card.add(xyWrapper); |
| | | card.add(Box.createVerticalStrut(15)); |
| | | |
| | | // 4. æä½æé®è¡ |
| | | JPanel actionPanel = new JPanel(); |
| | | actionPanel.setLayout(new BoxLayout(actionPanel, BoxLayout.X_AXIS)); |
| | | actionPanel.setOpaque(false); |
| | | |
| | | JButton generateBtn = createStyledButton("éæ°çæåæ ", PRIMARY_COLOR, true); |
| | | generateBtn.addActionListener(e -> generateObstacleCoordinates(obstacle, xyArea)); |
| | | |
| | | JButton previewBtn = createStyledButton("é¢è§", TEXT_SECONDARY, false); |
| | | previewBtn.setPreferredSize(new Dimension(70, 36)); // ç¨å¾®çªä¸ç¹ |
| | | previewBtn.addActionListener(e -> previewObstacle(obstacle)); |
| | | |
| | | actionPanel.add(generateBtn); |
| | | actionPanel.add(Box.createHorizontalStrut(10)); |
| | | actionPanel.add(previewBtn); |
| | | |
| | | card.add(actionPanel); |
| | | |
| | | return card; |
| | | } |
| | | |
| | | /** |
| | | * è¾
婿¹æ³ï¼å建带æ é¢çæ°æ®å±ç¤ºå |
| | | */ |
| | | private JPanel createDataField(String title, String content, int rows) { |
| | | JTextArea area = createDataTextArea(content, rows); |
| | | JScrollPane scroll = new JScrollPane(area); |
| | | scroll.setBorder(BorderFactory.createEmptyBorder()); |
| | | return createWrapperPanel(title, scroll); |
| | | } |
| | | |
| | | private JPanel createWrapperPanel(String title, JComponent content) { |
| | | JPanel panel = new JPanel(new BorderLayout(0, 5)); |
| | | panel.setOpaque(false); |
| | | |
| | | JLabel label = new JLabel(title); |
| | | label.setFont(new Font("微软é
é»", Font.PLAIN, 12)); |
| | | label.setForeground(TEXT_SECONDARY); |
| | | |
| | | JPanel contentBox = new JPanel(new BorderLayout()); |
| | | contentBox.setBackground(BG_INPUT); |
| | | contentBox.setBorder(BorderFactory.createCompoundBorder( |
| | | BorderFactory.createLineBorder(new Color(230, 230, 230), 1), |
| | | BorderFactory.createEmptyBorder(5, 5, 5, 5) |
| | | )); |
| | | contentBox.add(content, BorderLayout.CENTER); |
| | | |
| | | panel.add(label, BorderLayout.NORTH); |
| | | panel.add(contentBox, BorderLayout.CENTER); |
| | | return panel; |
| | | } |
| | | |
| | | private JTextArea createDataTextArea(String text, int rows) { |
| | | JTextArea area = new JTextArea(text); |
| | | area.setRows(rows); |
| | | area.setEditable(false); |
| | | area.setLineWrap(true); |
| | | area.setWrapStyleWord(true); |
| | | area.setBackground(BG_INPUT); |
| | | area.setForeground(new Color(50, 50, 50)); |
| | | // 使ç¨ç宽å使¾ç¤ºæ°æ®ï¼çèµ·æ¥æ´ä¸ä¸ |
| | | area.setFont(new Font("Monospaced", Font.PLAIN, 12)); |
| | | return area; |
| | | } |
| | | |
| | | /** |
| | | * å建ç°ä»£é£æ ¼æé® (å®å¿æè½®å») |
| | | */ |
| | | private JButton createStyledButton(String text, Color baseColor, boolean filled) { |
| | | JButton btn = new JButton(text) { |
| | | @Override |
| | | protected void paintComponent(Graphics g) { |
| | | Graphics2D g2 = (Graphics2D) g.create(); |
| | | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
| | | |
| | | boolean isPressed = getModel().isPressed(); |
| | | boolean isRollover = getModel().isRollover(); |
| | | |
| | | if (filled) { |
| | | if (isPressed) g2.setColor(baseColor.darker()); |
| | | else if (isRollover) g2.setColor(new Color(baseColor.getRed(), baseColor.getGreen(), baseColor.getBlue(), 230)); // å¾®éæ |
| | | else g2.setColor(baseColor); |
| | | g2.fill(new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), 8, 8)); |
| | | g2.setColor(Color.WHITE); |
| | | } else { |
| | | g2.setColor(BG_CARD); // èæ¯ç½ |
| | | g2.fill(new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), 8, 8)); |
| | | |
| | | if (isPressed) g2.setColor(baseColor.darker()); |
| | | else if (isRollover) g2.setColor(baseColor); |
| | | else g2.setColor(new Color(200, 200, 200)); |
| | | |
| | | g2.setStroke(new BasicStroke(1.2f)); |
| | | g2.draw(new RoundRectangle2D.Double(0, 0, getWidth()-1, getHeight()-1, 8, 8)); |
| | | |
| | | g2.setColor(isRollover ? baseColor : TEXT_SECONDARY); |
| | | } |
| | | |
| | | FontMetrics fm = g2.getFontMetrics(); |
| | | int x = (getWidth() - fm.stringWidth(getText())) / 2; |
| | | int y = (getHeight() - fm.getHeight()) / 2 + fm.getAscent(); |
| | | g2.drawString(getText(), x, y); |
| | | |
| | | g2.dispose(); |
| | | } |
| | | }; |
| | | btn.setFont(new Font("微软é
é»", Font.BOLD, 12)); |
| | | btn.setFocusPainted(false); |
| | | btn.setContentAreaFilled(false); |
| | | btn.setBorderPainted(false); |
| | | btn.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
| | | btn.setMaximumSize(new Dimension(Integer.MAX_VALUE, 36)); |
| | | return btn; |
| | | } |
| | | |
| | | /** |
| | | * åå»ºå½¢ç¶æ ç¾ï¼æ¾ç¤ºåå½¢æå¤è¾¹å½¢å¾æ ï¼ |
| | | */ |
| | | private JLabel createShapeLabel(Obstacledge.ObstacleShape shape) { |
| | | JLabel shapeLabel = new JLabel(); |
| | | shapeLabel.setPreferredSize(new Dimension(24, 24)); |
| | | |
| | | if (shape == null) { |
| | | return shapeLabel; |
| | | } |
| | | |
| | | // æ ¹æ®å½¢ç¶å建ä¸åç徿 |
| | | Icon shapeIcon; |
| | | if (shape == Obstacledge.ObstacleShape.CIRCLE) { |
| | | // å形徿 |
| | | shapeIcon = new Icon() { |
| | | @Override |
| | | public void paintIcon(Component c, Graphics g, int x, int y) { |
| | | Graphics2D g2 = (Graphics2D) g.create(); |
| | | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
| | | g2.setColor(PRIMARY_COLOR); |
| | | g2.setStroke(new BasicStroke(2.0f)); |
| | | g2.drawOval(x + 2, y + 2, getIconWidth() - 4, getIconHeight() - 4); |
| | | g2.dispose(); |
| | | } |
| | | |
| | | @Override |
| | | public int getIconWidth() { |
| | | return 20; |
| | | } |
| | | |
| | | @Override |
| | | public int getIconHeight() { |
| | | return 20; |
| | | } |
| | | }; |
| | | shapeLabel.setToolTipText("åå½¢"); |
| | | } else if (shape == Obstacledge.ObstacleShape.POLYGON) { |
| | | // å¤è¾¹å½¢å¾æ ï¼å
è¾¹å½¢ï¼ |
| | | shapeIcon = new Icon() { |
| | | @Override |
| | | public void paintIcon(Component c, Graphics g, int x, int y) { |
| | | Graphics2D g2 = (Graphics2D) g.create(); |
| | | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
| | | g2.setColor(PRIMARY_COLOR); |
| | | g2.setStroke(new BasicStroke(2.0f)); |
| | | |
| | | // ç»å¶å
边形 |
| | | int centerX = x + getIconWidth() / 2; |
| | | int centerY = y + getIconHeight() / 2; |
| | | int radius = 8; |
| | | int[] xPoints = new int[6]; |
| | | int[] yPoints = new int[6]; |
| | | |
| | | for (int i = 0; i < 6; i++) { |
| | | double angle = Math.PI / 3.0 * i - Math.PI / 6.0; // ä»é¡¶é¨å¼å§ |
| | | xPoints[i] = centerX + (int) (radius * Math.cos(angle)); |
| | | yPoints[i] = centerY + (int) (radius * Math.sin(angle)); |
| | | } |
| | | |
| | | g2.drawPolygon(xPoints, yPoints, 6); |
| | | g2.dispose(); |
| | | } |
| | | |
| | | @Override |
| | | public int getIconWidth() { |
| | | return 20; |
| | | } |
| | | |
| | | @Override |
| | | public int getIconHeight() { |
| | | return 20; |
| | | } |
| | | }; |
| | | shapeLabel.setToolTipText("å¤è¾¹å½¢"); |
| | | } else { |
| | | return shapeLabel; // æªç¥å½¢ç¶ï¼è¿å空æ ç¾ |
| | | } |
| | | |
| | | shapeLabel.setIcon(shapeIcon); |
| | | return shapeLabel; |
| | | } |
| | | |
| | | /** |
| | | * åå»ºçº¯å¾æ æé®ï¼ç¨äºå é¤ï¼ |
| | | */ |
| | | private JButton createIconButton(String iconText, Color hoverColor) { |
| | | JButton btn = new JButton(iconText); |
| | | btn.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 16)); |
| | | btn.setForeground(TEXT_SECONDARY); |
| | | btn.setBorder(null); |
| | | btn.setContentAreaFilled(false); |
| | | btn.setFocusPainted(false); |
| | | btn.setCursor(new Cursor(Cursor.HAND_CURSOR)); |
| | | btn.setPreferredSize(new Dimension(30, 30)); |
| | | |
| | | btn.addMouseListener(new MouseAdapter() { |
| | | public void mouseEntered(MouseEvent e) { btn.setForeground(hoverColor); } |
| | | public void mouseExited(MouseEvent e) { btn.setForeground(TEXT_SECONDARY); } |
| | | }); |
| | | return btn; |
| | | } |
| | | |
| | | // ================================================================================= |
| | | // 以ä¸ä¸ºåæä¸å¡é»è¾ä»£ç ï¼ä¿æä¸åï¼ä»
è°æ´é¨å空å¼å¤æä»¥éé
æ°UI |
| | | // ================================================================================= |
| | | |
| | | private void previewObstacle(Obstacledge.Obstacle obstacle) { |
| | | if (obstacle == null) return; |
| | | |
| | | String landNumber = dikuai.getLandNumber(); |
| | | String landName = dikuai.getLandName(); |
| | | String boundary = dikuai.getBoundaryCoordinates(); |
| | | String obstacleCoords = extractObstacleCoordinates(obstacle); |
| | | |
| | | if (obstacleCoords == null || obstacleCoords.trim().isEmpty()) { |
| | | JOptionPane.showMessageDialog(this, "该éç¢ç©æ²¡æåæ æ°æ®", "æç¤º", JOptionPane.INFORMATION_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | List<Obstacledge.Obstacle> allObstacles = loadObstacles(); |
| | | String allObstaclesCoords = buildAllObstaclesCoordinates(allObstacles); |
| | | |
| | | setVisible(false); |
| | | |
| | | SwingUtilities.invokeLater(() -> { |
| | | Shouye shouye = Shouye.getInstance(); |
| | | if (shouye != null) { |
| | | shouye.startMowingPathPreview( |
| | | landNumber, |
| | | landName, |
| | | boundary, |
| | | allObstaclesCoords, |
| | | null, |
| | | () -> SwingUtilities.invokeLater(() -> setVisible(true)) |
| | | ); |
| | | } else { |
| | | JOptionPane.showMessageDialog(null, "æ æ³æå¼ä¸»é¡µé¢è¿è¡é¢è§", "æç¤º", JOptionPane.WARNING_MESSAGE); |
| | | setVisible(true); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | private String buildAllObstaclesCoordinates(List<Obstacledge.Obstacle> obstacles) { |
| | | if (obstacles == null || obstacles.isEmpty()) return null; |
| | | List<String> coordStrings = new ArrayList<>(); |
| | | for (Obstacledge.Obstacle obstacle : obstacles) { |
| | | String coords = extractObstacleCoordinates(obstacle); |
| | | if (coords != null && !coords.trim().isEmpty()) { |
| | | coordStrings.add(coords.trim()); |
| | | } |
| | | } |
| | | return coordStrings.isEmpty() ? null : String.join(" ", coordStrings); |
| | | } |
| | | |
| | | private String extractObstacleCoordinates(Obstacledge.Obstacle obstacle) { |
| | | if (obstacle == null) return ""; |
| | | String xy = obstacle.getXyCoordsString(); |
| | | return isMeaningfulValue(xy) ? xy.trim() : ""; |
| | | } |
| | | |
| | | private String extractOriginalCoordinates(Obstacledge.Obstacle obstacle) { |
| | | if (obstacle == null) return ""; |
| | | String original = obstacle.getOriginalCoordsString(); |
| | | return isMeaningfulValue(original) ? original.trim() : ""; |
| | | } |
| | | |
| | | private void generateObstacleCoordinates(Obstacledge.Obstacle obstacle, JTextArea coordArea) { |
| | | if (obstacle == null || coordArea == null) return; |
| | | |
| | | String originalCoords = extractOriginalCoordinates(obstacle); |
| | | if (!isMeaningfulValue(originalCoords)) { |
| | | JOptionPane.showMessageDialog(this, "æ åå§åæ æ°æ®ï¼æ æ³çæ", "æç¤º", JOptionPane.INFORMATION_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | String baseStation = dikuai.getBaseStationCoordinates(); |
| | | if (!isMeaningfulValue(baseStation)) { |
| | | JOptionPane.showMessageDialog(this, "å°åæªè®¾ç½®åºç«åæ ", "æç¤º", JOptionPane.WARNING_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | List<Obstacledge.DMCoordinate> originalCoordsList = obstacle.getOriginalCoordinates(); |
| | | if (originalCoordsList == null || originalCoordsList.isEmpty()) { |
| | | JOptionPane.showMessageDialog(this, "åå§åæ æ°æ®æ æ", "æç¤º", JOptionPane.INFORMATION_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | String[] baseParts = baseStation.split(","); |
| | | if (baseParts.length < 4) { |
| | | JOptionPane.showMessageDialog(this, "åºç«åæ æ ¼å¼ä¸æ£ç¡®", "é误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | double baseLat = parseDMToDecimal(baseParts[0].trim(), baseParts[1].trim()); |
| | | double baseLon = parseDMToDecimal(baseParts[2].trim(), baseParts[3].trim()); |
| | | |
| | | List<Obstacledge.XYCoordinate> xyCoords = new ArrayList<>(); |
| | | for (int i = 0; i < originalCoordsList.size(); i += 2) { |
| | | if (i + 1 >= originalCoordsList.size()) break; |
| | | double lat = originalCoordsList.get(i).toDecimalDegree(); |
| | | double lon = originalCoordsList.get(i + 1).toDecimalDegree(); |
| | | |
| | | if (Double.isFinite(lat) && Double.isFinite(lon)) { |
| | | double[] localXY = convertLatLonToLocal(lat, lon, baseLat, baseLon); |
| | | xyCoords.add(new Obstacledge.XYCoordinate(localXY[0], localXY[1])); |
| | | } |
| | | } |
| | | |
| | | if (xyCoords.isEmpty()) { |
| | | JOptionPane.showMessageDialog(this, "åæ è½¬æ¢å¤±è´¥", "é误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | obstacle.setXyCoordinates(xyCoords); |
| | | saveObstacleUpdate(obstacle, coordArea); |
| | | |
| | | } catch (Exception ex) { |
| | | ex.printStackTrace(); |
| | | JOptionPane.showMessageDialog(this, "é误: " + ex.getMessage(), "é误", JOptionPane.ERROR_MESSAGE); |
| | | } |
| | | } |
| | | |
| | | private void saveObstacleUpdate(Obstacledge.Obstacle obstacle, JTextArea coordArea) { |
| | | String landNumber = dikuai.getLandNumber(); |
| | | try { |
| | | File configFile = new File("Obstacledge.properties"); |
| | | Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager(); |
| | | if (configFile.exists()) manager.loadFromFile(configFile.getAbsolutePath()); |
| | | |
| | | Obstacledge.Plot plot = manager.getPlotById(landNumber.trim()); |
| | | if (plot != null) { |
| | | plot.removeObstacleByName(obstacle.getObstacleName()); |
| | | plot.addObstacle(obstacle); |
| | | manager.saveToFile(configFile.getAbsolutePath()); |
| | | |
| | | Dikuai.updateField(landNumber.trim(), "updateTime", getCurrentTime()); |
| | | Dikuai.saveToProperties(); |
| | | |
| | | coordArea.setText(obstacle.getXyCoordsString()); |
| | | JOptionPane.showMessageDialog(this, "åæ å·²çæå¹¶ä¿å", "æå", JOptionPane.INFORMATION_MESSAGE); |
| | | } |
| | | } catch (Exception ex) { |
| | | ex.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | 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; |
| | | } |
| | | } |
| | | |
| | | 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}; |
| | | } |
| | | |
| | | private void deleteObstacle(Obstacledge.Obstacle obstacle) { |
| | | if (obstacle == null) return; |
| | | String name = obstacle.getObstacleName(); |
| | | |
| | | int choice = JOptionPane.showConfirmDialog(this, |
| | | "ç¡®å®è¦å é¤éç¢ç© \"" + name + "\" åï¼", |
| | | "å é¤ç¡®è®¤", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); |
| | | |
| | | if (choice != JOptionPane.YES_OPTION) return; |
| | | |
| | | String landNumber = dikuai.getLandNumber(); |
| | | try { |
| | | File configFile = new File("Obstacledge.properties"); |
| | | Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager(); |
| | | if (configFile.exists()) manager.loadFromFile(configFile.getAbsolutePath()); |
| | | |
| | | Obstacledge.Plot plot = manager.getPlotById(landNumber.trim()); |
| | | if (plot != null && plot.removeObstacleByName(name)) { |
| | | manager.saveToFile(configFile.getAbsolutePath()); |
| | | Dikuai.updateField(landNumber.trim(), "updateTime", getCurrentTime()); |
| | | Dikuai.saveToProperties(); |
| | | Dikuaiguanli.notifyExternalCreation(landNumber.trim()); |
| | | |
| | | JOptionPane.showMessageDialog(this, "å 餿å"); |
| | | loadAndDisplayObstacles(); |
| | | } else { |
| | | JOptionPane.showMessageDialog(this, "å é¤å¤±è´¥ï¼æªæ¾å°è®°å½", "é误", JOptionPane.ERROR_MESSAGE); |
| | | } |
| | | } catch (Exception ex) { |
| | | ex.printStackTrace(); |
| | | JOptionPane.showMessageDialog(this, "å¼å¸¸: " + ex.getMessage(), "é误", JOptionPane.ERROR_MESSAGE); |
| | | } |
| | | } |
| | | |
| | | private String getCurrentTime() { |
| | | return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date()); |
| | | } |
| | | |
| | | private List<Obstacledge.Obstacle> loadObstacles() { |
| | | if (dikuai == null) return Collections.emptyList(); |
| | | String landNumber = dikuai.getLandNumber(); |
| | | if (landNumber == null || landNumber.trim().isEmpty()) return Collections.emptyList(); |
| | | |
| | | try { |
| | | File configFile = new File("Obstacledge.properties"); |
| | | if (!configFile.exists()) return Collections.emptyList(); |
| | | |
| | | Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager(); |
| | | if (!manager.loadFromFile(configFile.getAbsolutePath())) return Collections.emptyList(); |
| | | |
| | | Obstacledge.Plot plot = manager.getPlotById(landNumber.trim()); |
| | | return plot != null ? new ArrayList<>(plot.getObstacles()) : Collections.emptyList(); |
| | | } catch (Exception ex) { |
| | | ex.printStackTrace(); |
| | | return Collections.emptyList(); |
| | | } |
| | | } |
| | | |
| | | private String getDisplayValue(String value, String defaultValue) { |
| | | return (value != null && !value.equals("-1") && !value.trim().isEmpty()) ? value : defaultValue; |
| | | } |
| | | |
| | | private boolean isMeaningfulValue(String value) { |
| | | return value != null && !value.trim().isEmpty() && !"-1".equals(value.trim()); |
| | | } |
| | | } |
| | |
| | | |
| | | prevButton = createSecondaryButton("ä¸ä¸æ¥"); |
| | | nextButton = createPrimaryButton("ä¸ä¸æ¥", 16); |
| | | // 设置ä¸ä¸æ¥æé®å®½åº¦ä¸º300åç´ |
| | | nextButton.setPreferredSize(new Dimension(300, nextButton.getPreferredSize().height)); |
| | | nextButton.setMaximumSize(new Dimension(300, nextButton.getPreferredSize().height)); |
| | | saveButton = createPrimaryButton("ä¿å", 16); |
| | | saveButton.setVisible(false); |
| | | |
| | |
| | | package lujing; |
| | | |
| | | import javax.swing.*; |
| | | import javax.swing.SwingUtilities; |
| | | import java.awt.*; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | |
| | | buttonPanel.setBackground(BACKGROUND_COLOR); |
| | | |
| | | JButton generateBtn = createPrimaryFooterButton("çæå²èè·¯å¾"); |
| | | JButton previewBtn = createPrimaryFooterButton("é¢è§"); |
| | | JButton saveBtn = createPrimaryFooterButton("ä¿åè·¯å¾"); |
| | | JButton cancelBtn = createPrimaryFooterButton("åæ¶"); |
| | | |
| | | generateBtn.addActionListener(e -> generatePath(modeValue)); |
| | | previewBtn.addActionListener(e -> previewPath()); |
| | | saveBtn.addActionListener(e -> savePath()); |
| | | cancelBtn.addActionListener(e -> dispose()); |
| | | |
| | | buttonPanel.add(generateBtn); |
| | | buttonPanel.add(previewBtn); |
| | | buttonPanel.add(saveBtn); |
| | | buttonPanel.add(cancelBtn); |
| | | add(buttonPanel, BorderLayout.SOUTH); |
| | |
| | | } |
| | | |
| | | /** |
| | | * é¢è§è·¯å¾ |
| | | */ |
| | | private void previewPath() { |
| | | // å
ä¿åå½åè·¯å¾å°å°åï¼ä¸´æ¶ä¿åï¼ç¨äºé¢è§ï¼ |
| | | String pathNormalized = normalizeCoordinateInput(pathArea.getText()); |
| | | if (!"-1".equals(pathNormalized)) { |
| | | pathNormalized = pathNormalized |
| | | .replace("\r\n", ";") |
| | | .replace('\r', ';') |
| | | .replace('\n', ';') |
| | | .replaceAll(";+", ";") |
| | | .replaceAll("\\s*;\\s*", ";") |
| | | .trim(); |
| | | if (pathNormalized.isEmpty()) { |
| | | pathNormalized = "-1"; |
| | | } |
| | | } |
| | | |
| | | if ("-1".equals(pathNormalized)) { |
| | | JOptionPane.showMessageDialog(this, "请å
çæå²èè·¯å¾", "æç¤º", JOptionPane.INFORMATION_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | // 临æ¶ä¿åè·¯å¾å°å°å对象ï¼ä¸æä¹
åï¼ |
| | | if (saveCallback != null) { |
| | | saveCallback.savePlannedPath(dikuai, pathNormalized); |
| | | } |
| | | |
| | | // ä¿åå½å页é¢ç¶æï¼ç¨äºè¿åæ¶æ¢å¤ |
| | | String currentBaseStation = baseStationField.getText(); |
| | | String currentBoundary = boundaryArea.getText(); |
| | | String currentObstacle = obstacleArea.getText(); |
| | | String currentWidth = widthField.getText(); |
| | | String currentPath = pathArea.getText(); |
| | | |
| | | // è·åå°åä¿¡æ¯ |
| | | String landNumber = dikuai.getLandNumber(); |
| | | String landName = dikuai.getLandName(); |
| | | |
| | | // å¤çè¾¹çåæ ï¼ç¡®ä¿å鿝 effectively final |
| | | String boundaryInput = normalizeCoordinateInput(boundaryArea.getText()); |
| | | final String boundary; |
| | | if (!"-1".equals(boundaryInput)) { |
| | | String processed = boundaryInput.replace("\r\n", ";") |
| | | .replace('\r', ';') |
| | | .replace('\n', ';') |
| | | .replaceAll(";+", ";") |
| | | .replaceAll("\\s*;\\s*", ";") |
| | | .trim(); |
| | | if (processed.isEmpty()) { |
| | | boundary = dikuai.getBoundaryCoordinates(); |
| | | } else { |
| | | boundary = processed; |
| | | } |
| | | } else { |
| | | boundary = dikuai.getBoundaryCoordinates(); |
| | | } |
| | | |
| | | // å¤çéç¢ç©åæ ï¼ç¡®ä¿å鿝 effectively final |
| | | String obstaclesInput = normalizeCoordinateInput(obstacleArea.getText()); |
| | | final String obstacles; |
| | | if (!"-1".equals(obstaclesInput)) { |
| | | String processed = obstaclesInput.replace("\r\n", " ") |
| | | .replace('\r', ' ') |
| | | .replace('\n', ' ') |
| | | .replaceAll("\\s{2,}", " ") |
| | | .trim(); |
| | | if (processed.isEmpty()) { |
| | | obstacles = null; |
| | | } else { |
| | | obstacles = processed; |
| | | } |
| | | } else { |
| | | obstacles = null; |
| | | } |
| | | |
| | | // ä¿åæç»å¼å° final åéï¼ä»¥ä¾¿å¨ lambda ä¸ä½¿ç¨ |
| | | final String finalPathNormalized = pathNormalized; |
| | | final String finalLandNumber = landNumber; |
| | | final String finalLandName = landName; |
| | | |
| | | // å
³éè·¯å¾è§åé¡µé¢ |
| | | setVisible(false); |
| | | |
| | | // æå¼ä¸»é¡µé¢å¹¶æ¾ç¤ºè·¯å¾é¢è§ |
| | | SwingUtilities.invokeLater(() -> { |
| | | zhuye.Shouye shouye = zhuye.Shouye.getInstance(); |
| | | if (shouye != null) { |
| | | // æ¾ç¤ºè·¯å¾é¢è§ï¼å¹¶è®¾ç½®è¿ååè° |
| | | shouye.startMowingPathPreview( |
| | | finalLandNumber, |
| | | finalLandName, |
| | | boundary, |
| | | obstacles, |
| | | finalPathNormalized, |
| | | () -> { |
| | | // è¿ååè°ï¼éæ°æå¼è·¯å¾è§åé¡µé¢ |
| | | SwingUtilities.invokeLater(() -> { |
| | | setVisible(true); |
| | | // æ¢å¤ä¹åçç¶æ |
| | | baseStationField.setText(currentBaseStation); |
| | | boundaryArea.setText(currentBoundary); |
| | | obstacleArea.setText(currentObstacle); |
| | | widthField.setText(currentWidth); |
| | | pathArea.setText(currentPath); |
| | | }); |
| | | } |
| | | ); |
| | | } else { |
| | | // 妿䏻页é¢ä¸åå¨ï¼æç¤ºç¨æ·å¹¶éæ°æ¾ç¤ºè·¯å¾è§åé¡µé¢ |
| | | JOptionPane.showMessageDialog(null, "æ æ³æå¼ä¸»é¡µé¢è¿è¡é¢è§", "æç¤º", JOptionPane.WARNING_MESSAGE); |
| | | setVisible(true); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * ä¿åè·¯å¾ |
| | | */ |
| | | private void savePath() { |
| | |
| | | private static final Color GRASS_FILL_COLOR = new Color(144, 238, 144, 120); |
| | | private static final Color GRASS_BORDER_COLOR = new Color(60, 179, 113); |
| | | private static final Color BOUNDARY_POINT_COLOR = new Color(128, 0, 128); |
| | | private static final Color OBSTACLE_POINT_COLOR = new Color(255, 140, 0); // æ©è²ï¼ç¨äºåºåéç¢ç©ç¹ |
| | | private static final Color CIRCLE_SAMPLE_COLOR = new Color(220, 20, 60, 230); |
| | | private static final double CIRCLE_SAMPLE_SIZE = 0.54d; |
| | | private static final double BOUNDARY_POINT_MERGE_THRESHOLD = 0.05; |
| | |
| | | private String currentObstacleLandNumber; |
| | | private String boundaryName; |
| | | private boolean boundaryPointsVisible; |
| | | private boolean obstaclePointsVisible; |
| | | private double boundaryPointSizeScale = 1.0d; |
| | | private boolean previewSizingEnabled; |
| | | private String currentBoundaryLandNumber; |
| | |
| | | ); |
| | | } |
| | | |
| | | // ç»å¶éç¢ç©åæ ç¹ |
| | | if (obstaclePointsVisible && hasObstacles) { |
| | | List<Point2D.Double> obstaclePoints = Obstacledraw.getObstaclePoints(currentObstacles); |
| | | if (obstaclePoints != null && !obstaclePoints.isEmpty()) { |
| | | double markerScale = boundaryPointSizeScale * (previewSizingEnabled ? PREVIEW_BOUNDARY_MARKER_SCALE : 1.0d); |
| | | pointandnumber.drawBoundaryPoints( |
| | | g2d, |
| | | obstaclePoints, |
| | | scale, |
| | | BOUNDARY_POINT_MERGE_THRESHOLD, |
| | | OBSTACLE_POINT_COLOR, |
| | | markerScale |
| | | ); |
| | | } |
| | | } |
| | | |
| | | if (shouldRenderIdleTrail()) { |
| | | tuowei.draw(g2d, idleMowerTrail, scale); |
| | | } |
| | |
| | | obstacleBounds = null; |
| | | selectedObstacleName = null; |
| | | currentObstacleLandNumber = null; |
| | | obstaclePointsVisible = false; |
| | | } |
| | | |
| | | private List<Obstacledge.Obstacle> parseObstacles(String obstaclesData, String landNumber) { |
| | |
| | | visualizationPanel.repaint(); |
| | | } |
| | | |
| | | public void setObstaclePointsVisible(boolean visible) { |
| | | this.obstaclePointsVisible = visible; |
| | | visualizationPanel.repaint(); |
| | | } |
| | | |
| | | public void setBoundaryPointSizeScale(double sizeScale) { |
| | | double normalized = (Double.isFinite(sizeScale) && sizeScale > 0.0d) ? sizeScale : 1.0d; |
| | | if (Math.abs(boundaryPointSizeScale - normalized) < 1e-6) { |
| | |
| | | } |
| | | // å
³éæ¨¡å¼æ¶ä¸éè¦å任使ä½ï¼ç¨æ·å·²ç»å¯ä»¥èªç±ç§»å¨å°å¾ |
| | | |
| | | // æ´æ°å·¥å
·æç¤º |
| | | // æ´æ°å¾æ æ¾ç¤ºï¼éç»ä»¥åæ¢å¾æ ï¼ |
| | | if (visualizationPanel != null) { |
| | | visualizationPanel.repaint(); |
| | | } |
| | |
| | | |
| | | // å¯è§ååºå - 使ç¨MapRendererè¿è¡ç»å¶ |
| | | visualizationPanel = new JPanel() { |
| | | private ImageIcon gecaojiIcon = null; |
| | | private ImageIcon gecaojiIcon1 = null; // é»è®¤å¾æ |
| | | private ImageIcon gecaojiIcon2 = null; // 以å²èæºä¸ºä¸å¿æ¨¡å¼å¾æ |
| | | private static final int GECAOJI_ICON_X = 37; |
| | | private static final int GECAOJI_ICON_Y = 10; |
| | | private static final int GECAOJI_ICON_SIZE = 20; |
| | | |
| | | { |
| | | // å è½½å²èæºå¾æ ï¼å¤§å°20x20åç´ |
| | | gecaojiIcon = loadScaledIcon("image/gecaoji.png", GECAOJI_ICON_SIZE, GECAOJI_ICON_SIZE); |
| | | gecaojiIcon1 = loadScaledIcon("image/gecaojishijiao1.png", GECAOJI_ICON_SIZE, GECAOJI_ICON_SIZE); |
| | | gecaojiIcon2 = loadScaledIcon("image/gecaojishijiao2.png", GECAOJI_ICON_SIZE, GECAOJI_ICON_SIZE); |
| | | } |
| | | |
| | | /** |
| | |
| | | if (isMowerOutsideBoundary && warningIconVisible) { |
| | | // ç»å¶çº¢è²ä¸è§å½¢è¦å徿 ï¼å¸¦å¹å·ï¼ |
| | | drawWarningIcon(g, GECAOJI_ICON_X, GECAOJI_ICON_Y, GECAOJI_ICON_SIZE); |
| | | } else if (gecaojiIcon != null) { |
| | | // ç»å¶æ£å¸¸çå²èæºå¾æ |
| | | // æ°´å¹³æ¹åä¸é度æç¤ºå¨å¯¹é½ï¼x=37ï¼ |
| | | // åç´æ¹åä¸å«æç¶æå¾æ 对é½ï¼y=10ï¼é度æç¤ºå¨é¢æ¿é¡¶é¨è¾¹è·10åç´ ï¼ä½¿å¾æ ä¸å¿å¯¹é½ï¼ |
| | | g.drawImage(gecaojiIcon.getImage(), GECAOJI_ICON_X, GECAOJI_ICON_Y, null); |
| | | } else { |
| | | // æ ¹æ®æ¨¡å¼éæ©ä¸åç徿 |
| | | ImageIcon iconToDraw = centerOnMowerMode ? gecaojiIcon2 : gecaojiIcon1; |
| | | if (iconToDraw != null) { |
| | | // ç»å¶å²èæºå¾æ |
| | | // æ°´å¹³æ¹åä¸é度æç¤ºå¨å¯¹é½ï¼x=37ï¼ |
| | | // åç´æ¹åä¸å«æç¶æå¾æ 对é½ï¼y=10ï¼é度æç¤ºå¨é¢æ¿é¡¶é¨è¾¹è·10åç´ ï¼ä½¿å¾æ ä¸å¿å¯¹é½ï¼ |
| | | g.drawImage(iconToDraw.getImage(), GECAOJI_ICON_X, GECAOJI_ICON_Y, null); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | package zhuye; |
| | | |
| | | import java.awt.BasicStroke; |
| | | import java.awt.Color; |
| | | import java.awt.Cursor; |
| | | import java.awt.Dimension; |
| | | import java.awt.Font; |
| | | |
| | | import javax.swing.BorderFactory; |
| | | import java.awt.FontMetrics; |
| | | import java.awt.Graphics; |
| | | import java.awt.Graphics2D; |
| | | import java.awt.RenderingHints; |
| | | import java.awt.geom.RoundRectangle2D; |
| | | import javax.swing.JButton; |
| | | import javax.swing.border.EmptyBorder; |
| | | |
| | | /** |
| | | * æä¾ç»ä¸æé®æ ·å¼ç工忹æ³ã |
| | | * æä¾ç»ä¸æé®æ ·å¼ç工忹æ³ï¼ç¾åçï¼ã |
| | | * 飿 ¼ä¸ ObstacleManagementPage ä¿æä¸è´ï¼åè§ãæå¹³åãæé¯é½¿ã |
| | | */ |
| | | public final class buttonset { |
| | | |
| | | // é»è®¤æé®å°ºå¯¸ |
| | | private static final Dimension DEFAULT_SIZE = new Dimension(90, 36); |
| | | // åè§åå¾ |
| | | private static final int CORNER_RADIUS = 8; |
| | | |
| | | private buttonset() { |
| | | // å·¥å
·ç±»ä¸éè¦å®ä¾å |
| | | } |
| | | |
| | | /** |
| | | * å建带æç»ä¸æ ·å¼çæé®ï¼ä¾å¤ä¸ªçé¢å¤ç¨ã |
| | | * å建带æç°ä»£åç»ä¸æ ·å¼çæé®ï¼åè§ãæå¹³é£æ ¼ï¼ã |
| | | * |
| | | * @param text æé®æ¾ç¤ºææ¬ |
| | | * @param backgroundColor æé®èæ¯è² |
| | | * @param backgroundColor æé®ä¸»è²è°ï¼å¦æä¸ºnullï¼é»è®¤ä½¿ç¨ç°èè²ï¼ |
| | | * @return å·²åºç¨æ ·å¼ç JButton |
| | | */ |
| | | public static JButton createStyledButton(String text, Color backgroundColor) { |
| | | // ç¡®å®åºç¡é¢è²ï¼å¦ææªæå®å使ç¨é»è®¤çé¢èè² |
| | | final Color baseColor = backgroundColor != null ? backgroundColor : new Color(70, 130, 180); |
| | | |
| | | JButton button = new JButton(text); |
| | | // å建èªå®ä¹ç»å¶çæé® |
| | | JButton button = new JButton(text) { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @Override |
| | | protected void paintComponent(Graphics g) { |
| | | Graphics2D g2 = (Graphics2D) g.create(); |
| | | // å¼å¯æé¯é½¿ï¼ä½¿åè§åæåæ´å¹³æ» |
| | | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); |
| | | g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); |
| | | |
| | | // è·åæé®ç¶æ |
| | | boolean isPressed = getModel().isPressed(); |
| | | boolean isRollover = getModel().isRollover(); |
| | | int w = getWidth(); |
| | | int h = getHeight(); |
| | | |
| | | // 计ç®å½åèæ¯è² |
| | | if (isPressed) { |
| | | g2.setColor(baseColor.darker()); // æä¸åæ·± |
| | | } else if (isRollover) { |
| | | // æ¬åæ¶ç¨å¾®éææå亮ï¼å¢å äº¤äºæ |
| | | g2.setColor(new Color(baseColor.getRed(), baseColor.getGreen(), baseColor.getBlue(), 220)); |
| | | } else { |
| | | g2.setColor(baseColor); // æ£å¸¸é¢è² |
| | | } |
| | | |
| | | // ç»å¶åè§ç©å½¢èæ¯ |
| | | g2.fill(new RoundRectangle2D.Double(0, 0, w, h, CORNER_RADIUS, CORNER_RADIUS)); |
| | | |
| | | // 妿éè¦å¨æä¸æ¶æè½»å¾®çè¾¹æ¡ææï¼å¯éï¼å¢å ç«ä½æï¼ |
| | | if (isPressed) { |
| | | g2.setColor(new Color(0, 0, 0, 30)); |
| | | g2.draw(new RoundRectangle2D.Double(0.5, 0.5, w - 1, h - 1, CORNER_RADIUS, CORNER_RADIUS)); |
| | | } |
| | | |
| | | // ç»å¶æå |
| | | g2.setColor(Color.WHITE); |
| | | g2.setFont(getFont()); |
| | | FontMetrics fm = g2.getFontMetrics(); |
| | | |
| | | // ç²¾ç¡®è®¡ç®æåå±
ä¸ä½ç½® |
| | | int textX = (w - fm.stringWidth(getText())) / 2; |
| | | // 注æï¼yåæ æ¯åºçº¿ä½ç½®ï¼éè¦å ä¸ Ascent å¹¶åå»é«åº¦çä¸å |
| | | int textY = (h - fm.getHeight()) / 2 + fm.getAscent(); |
| | | |
| | | g2.drawString(getText(), textX, textY); |
| | | |
| | | g2.dispose(); |
| | | } |
| | | }; |
| | | |
| | | // åºç¡å±æ§è®¾ç½® |
| | | button.setFont(new Font("微软é
é»", Font.BOLD, 14)); |
| | | button.setBackground(baseColor); |
| | | button.setForeground(Color.WHITE); |
| | | button.setFocusPainted(false); |
| | | button.setBorder(BorderFactory.createEmptyBorder(8, 18, 8, 18)); |
| | | button.setFocusPainted(false); // å»é¤ç¦ç¹èçº¿æ¡ |
| | | button.setContentAreaFilled(false); // å»é¤é»è®¤èæ¯ç»å¶ï¼ç±paintComponentæ¥ç®¡ï¼ |
| | | button.setBorderPainted(false); // å»é¤é»è®¤è¾¹æ¡ |
| | | button.setCursor(new Cursor(Cursor.HAND_CURSOR)); // é¼ æ åææå |
| | | |
| | | // 设置å
è¾¹è·ï¼è½ç¶ä¸»è¦ç±paintComponentæ§å¶ï¼ä½å¯¹å¸å±è®¡ç®æå¸®å©ï¼ |
| | | button.setBorder(new EmptyBorder(8, 18, 8, 18)); |
| | | |
| | | Dimension preferred = button.getPreferredSize(); |
| | | int width = Math.max(preferred.width, DEFAULT_SIZE.width); |
| | | int height = Math.max(preferred.height, DEFAULT_SIZE.height); |
| | | Dimension adjustedSize = new Dimension(width, height); |
| | | |
| | | button.setPreferredSize(adjustedSize); |
| | | button.setMinimumSize(new Dimension(DEFAULT_SIZE.width, DEFAULT_SIZE.height)); |
| | | button.setMaximumSize(adjustedSize); |
| | | |
| | | button.addMouseListener(new java.awt.event.MouseAdapter() { |
| | | @Override |
| | | public void mouseEntered(java.awt.event.MouseEvent event) { |
| | | button.setBackground(brightenColor(baseColor)); |
| | | } |
| | | |
| | | @Override |
| | | public void mouseExited(java.awt.event.MouseEvent event) { |
| | | button.setBackground(baseColor); |
| | | } |
| | | }); |
| | | // 尺寸计ç®é»è¾ |
| | | Dimension preferred = button.getPreferredSize(); |
| | | // ç¡®ä¿å®½åº¦ä¸å°äºé»è®¤å¼ï¼ä¸æ ¹æ®æåé¿åº¦èªéåº |
| | | int width = Math.max(preferred.width + 20, DEFAULT_SIZE.width); |
| | | int height = Math.max(preferred.height, DEFAULT_SIZE.height); |
| | | |
| | | Dimension finalSize = new Dimension(width, height); |
| | | button.setPreferredSize(finalSize); |
| | | button.setMinimumSize(DEFAULT_SIZE); |
| | | button.setMaximumSize(finalSize); // 鲿¢å¨æäºå¸å±ä¸è¢«è¿åº¦æä¼¸ |
| | | |
| | | return button; |
| | | } |
| | | |
| | | private static Color brightenColor(Color color) { |
| | | if (color == null) { |
| | | return new Color(200, 200, 200); |
| | | } |
| | | int r = Math.min(255, color.getRed() + 30); |
| | | int g = Math.min(255, color.getGreen() + 30); |
| | | int b = Math.min(255, color.getBlue() + 30); |
| | | return new Color(r, g, b); |
| | | } |
| | | } |
| | | } |