| | |
| | | import zhangaiwu.yulanzhangaiwu; |
| | | import zhuye.buttonset; |
| | | import bianjie.bianjieguihua2; |
| | | import bianjie.ThreePointCircle; |
| | | |
| | | /** |
| | | * 障碍物新增/编辑对话框。设计语言参考 {@link AddDikuai},支持通过实地绘制采集障碍物坐标。 |
| | |
| | | boundaryStatusLabel.setVisible(false); |
| | | boundaryStatusLabel.setText(""); |
| | | } |
| | | // 对于圆形障碍物,隐藏生成边界按钮 |
| | | String shapeKey = formData.get("obstacleShape"); |
| | | boolean isCircle = "circle".equals(shapeKey); |
| | | if (isCircle && generateBoundaryButton != null) { |
| | | generateBoundaryButton.setVisible(false); |
| | | } |
| | | |
| | | String method = formData.get("drawingMethod"); |
| | | if (!isMeaningfulValue(method)) { |
| | |
| | | session.captureMessage = "无法根据采集的点生成圆,请确保选择了三个非共线的圆周点"; |
| | | return; |
| | | } |
| | | double[] radiusPoint = pickRadiusPoint(xyPoints, circle); |
| | | if (radiusPoint == null) { |
| | | session.captureSuccessful = false; |
| | | session.captureMessage = "采集的圆周点异常,无法生成圆"; |
| | | return; |
| | | } |
| | | // 使用计算出的半径生成一个真正的圆上点(圆心右侧的点),确保预览时计算的半径和结束绘制时的半径一致 |
| | | double radiusX = circle.centerX + circle.radius; |
| | | double radiusY = circle.centerY; |
| | | String result = String.format(Locale.US, "%.2f,%.2f;%.2f,%.2f", |
| | | circle.centerX, circle.centerY, radiusPoint[0], radiusPoint[1]); |
| | | circle.centerX, circle.centerY, radiusX, radiusY); |
| | | session.data.put("obstacleCoordinates", result); |
| | | session.captureSuccessful = true; |
| | | session.captureMessage = "已采集圆形障碍物,共 " + xyPoints.size() + " 个点"; |
| | |
| | | return; |
| | | } |
| | | String coords = formData.get("obstacleCoordinates"); |
| | | String shapeKey = formData.get("obstacleShape"); |
| | | boolean isCircle = "circle".equals(shapeKey); |
| | | |
| | | if (isMeaningfulValue(coords)) { |
| | | if (isCircle) { |
| | | // 对于圆形障碍物,显示圆心坐标和半径 |
| | | String statusText = parseCircleStatusText(coords); |
| | | drawingStatusLabel.setText(statusText); |
| | | } else { |
| | | // 对于多边形障碍物,显示点数 |
| | | int count = countCoordinatePairs(coords); |
| | | drawingStatusLabel.setText("已采集障碍物数据,点数:" + count); |
| | | } |
| | | if (!drawingInProgress && drawButton != null) { |
| | | drawButton.setText("重新绘制"); |
| | | drawButton.setEnabled(true); |
| | | } |
| | | // 绘制完成后显示"生成障碍物边界"按钮 |
| | | // 对于圆形障碍物,不显示"生成障碍物边界"按钮 |
| | | if (generateBoundaryButton != null) { |
| | | generateBoundaryButton.setVisible(true); |
| | | generateBoundaryButton.setVisible(!isCircle); |
| | | } |
| | | } else { |
| | | drawingStatusLabel.setText("尚未采集障碍物坐标"); |
| | |
| | | if (previewButton == null) { |
| | | return; |
| | | } |
| | | |
| | | // 对于圆形障碍物,不显示预览按钮 |
| | | String shapeKey = formData.get("obstacleShape"); |
| | | boolean isCircle = "circle".equals(shapeKey); |
| | | if (isCircle) { |
| | | previewButton.setVisible(false); |
| | | previewButton.setEnabled(false); |
| | | return; |
| | | } |
| | | |
| | | // 只有在生成边界后才显示预览按钮 |
| | | String generatedBoundary = formData.get("generatedBoundaryCoordinates"); |
| | | boolean hasGeneratedBoundary = isMeaningfulValue(generatedBoundary); |
| | |
| | | return count; |
| | | } |
| | | |
| | | /** |
| | | * 解析圆形障碍物坐标,生成状态文本 |
| | | * 格式:centerX,centerY;radiusX,radiusY |
| | | * 返回:当前采集圆形圆心坐标x,y,半径n米 |
| | | */ |
| | | private String parseCircleStatusText(String coords) { |
| | | if (!isMeaningfulValue(coords)) { |
| | | return "尚未采集圆形障碍物坐标"; |
| | | } |
| | | |
| | | try { |
| | | String[] pairs = coords.split(";"); |
| | | if (pairs.length < 2) { |
| | | return "圆形障碍物坐标格式错误"; |
| | | } |
| | | |
| | | // 解析圆心坐标 |
| | | String[] centerParts = pairs[0].trim().split(","); |
| | | if (centerParts.length < 2) { |
| | | return "圆形障碍物坐标格式错误"; |
| | | } |
| | | double centerX = Double.parseDouble(centerParts[0].trim()); |
| | | double centerY = Double.parseDouble(centerParts[1].trim()); |
| | | |
| | | // 解析圆上一点坐标 |
| | | String[] radiusParts = pairs[1].trim().split(","); |
| | | if (radiusParts.length < 2) { |
| | | return "圆形障碍物坐标格式错误"; |
| | | } |
| | | double radiusX = Double.parseDouble(radiusParts[0].trim()); |
| | | double radiusY = Double.parseDouble(radiusParts[1].trim()); |
| | | |
| | | // 计算半径(米) |
| | | double radius = Math.sqrt(Math.pow(radiusX - centerX, 2) + Math.pow(radiusY - centerY, 2)); |
| | | |
| | | // 格式化显示:当前采集圆形圆心坐标x,y,半径n米 |
| | | return String.format(Locale.US, "当前采集圆形圆心坐标%.2f,%.2f,半径%.2f米", |
| | | centerX, centerY, radius); |
| | | } catch (Exception e) { |
| | | return "圆形障碍物坐标解析失败"; |
| | | } |
| | | } |
| | | |
| | | private void updateSaveButtonState() { |
| | | if (saveButton != null) { |
| | | boolean hasCoords = isMeaningfulValue(formData.get("obstacleCoordinates")); |
| | |
| | | |
| | | String coords = coordsValue.trim(); |
| | | String originalCoords = isMeaningfulValue(originalValue) ? originalValue.trim() : null; |
| | | // 如果有生成的边界坐标,使用生成的边界坐标;否则使用原始坐标 |
| | | String finalCoords = isMeaningfulValue(generatedBoundaryValue) ? generatedBoundaryValue.trim() : coords; |
| | | // 对于圆形障碍物,直接使用obstacleCoordinates(在第3个点确认时已生成) |
| | | // 对于多边形障碍物,如果有生成的边界坐标,使用生成的边界坐标;否则使用原始坐标 |
| | | String finalCoords; |
| | | if ("circle".equals(shapeKey)) { |
| | | // 圆形障碍物直接使用已生成的坐标 |
| | | finalCoords = coords; |
| | | } else { |
| | | // 多边形障碍物优先使用生成的边界坐标 |
| | | finalCoords = isMeaningfulValue(generatedBoundaryValue) ? generatedBoundaryValue.trim() : coords; |
| | | } |
| | | String trimmedName = isMeaningfulValue(obstacleName) ? obstacleName.trim() : null; |
| | | if (!isMeaningfulValue(trimmedName)) { |
| | | JOptionPane.showMessageDialog(this, "障碍物名称无效", "错误", JOptionPane.ERROR_MESSAGE); |
| | |
| | | } |
| | | |
| | | String shapeKey = formData.get("obstacleShape"); |
| | | if (!"polygon".equals(shapeKey)) { |
| | | JOptionPane.showMessageDialog(this, "只有多边形障碍物才需要生成边界坐标", "提示", JOptionPane.INFORMATION_MESSAGE); |
| | | |
| | | try { |
| | | String method = formData.get("drawingMethod"); |
| | | |
| | | // 处理圆形障碍物 |
| | | if ("circle".equals(shapeKey)) { |
| | | if (!"mower".equals(method)) { |
| | | JOptionPane.showMessageDialog(this, "只有割草机绘制的圆形障碍物才支持生成边界坐标", "提示", JOptionPane.INFORMATION_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | // 将原始坐标转换为Coordinate对象列表 |
| | | List<Coordinate> coordinateList = parseOriginalCoordinatesToCoordinateList(originalCoords); |
| | | if (coordinateList.size() < 3) { |
| | | JOptionPane.showMessageDialog(this, "圆形障碍物至少需要三个采集点", "错误", JOptionPane.ERROR_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()); |
| | | |
| | | // 将原始坐标转换为XY坐标(本地坐标系) |
| | | List<double[]> xyPoints = new ArrayList<>(); |
| | | for (Coordinate coord : coordinateList) { |
| | | double lat = parseDMToDecimal(coord.getLatitude(), coord.getLatDirection()); |
| | | double lon = parseDMToDecimal(coord.getLongitude(), coord.getLonDirection()); |
| | | xyPoints.add(convertLatLonToLocal(lat, lon, baseLat, baseLon)); |
| | | } |
| | | |
| | | // 使用ThreePointCircle算法计算圆心和半径 |
| | | if (xyPoints.size() < 3) { |
| | | JOptionPane.showMessageDialog(this, "至少需要三个点才能生成圆形边界", "错误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | // 取前三个点计算圆 |
| | | double[] p1 = xyPoints.get(0); |
| | | double[] p2 = xyPoints.get(1); |
| | | double[] p3 = xyPoints.get(2); |
| | | |
| | | String point1 = String.format(Locale.US, "%.2f,%.2f", p1[0], p1[1]); |
| | | String point2 = String.format(Locale.US, "%.2f,%.2f", p2[0], p2[1]); |
| | | String point3 = String.format(Locale.US, "%.2f,%.2f", p3[0], p3[1]); |
| | | |
| | | String circleResult = bianjie.ThreePointCircle.getCircleFromPoints(point1, point2, point3); |
| | | |
| | | // 解析结果:格式为 "圆心: x,y; 半径: r" |
| | | if (circleResult == null || circleResult.startsWith("错误")) { |
| | | JOptionPane.showMessageDialog(this, "生成圆形边界失败: " + circleResult, "错误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | // 解析圆心和半径 |
| | | String[] parts = circleResult.split(";"); |
| | | if (parts.length < 2) { |
| | | JOptionPane.showMessageDialog(this, "解析圆形边界结果失败", "错误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | // 提取圆心坐标 |
| | | String centerPart = parts[0].trim(); // "圆心: x,y" |
| | | String radiusPart = parts[1].trim(); // "半径: r" |
| | | |
| | | String centerCoords = centerPart.substring(centerPart.indexOf(":") + 1).trim(); |
| | | String radiusStr = radiusPart.substring(radiusPart.indexOf(":") + 1).trim(); |
| | | |
| | | String[] centerXY = centerCoords.split(","); |
| | | if (centerXY.length < 2) { |
| | | JOptionPane.showMessageDialog(this, "解析圆心坐标失败", "错误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | double centerX = Double.parseDouble(centerXY[0].trim()); |
| | | double centerY = Double.parseDouble(centerXY[1].trim()); |
| | | double radius = Double.parseDouble(radiusStr.trim()); |
| | | |
| | | // 计算圆上一点(圆心右侧的点) |
| | | double radiusX = centerX + radius; |
| | | double radiusY = centerY; |
| | | |
| | | // 生成边界坐标格式:圆心X,圆心Y;圆上点X,圆上点Y |
| | | String boundaryCoords = String.format(Locale.US, "%.2f,%.2f;%.2f,%.2f", |
| | | centerX, centerY, radiusX, radiusY); |
| | | |
| | | // 保存生成的边界坐标 |
| | | formData.put("generatedBoundaryCoordinates", boundaryCoords); |
| | | |
| | | // 更新边界状态标签显示(圆形只有2个点:圆心和圆上一点) |
| | | updateBoundaryStatusLabel(2); |
| | | |
| | | // 更新预览按钮显示(变成绿色可点击) |
| | | updatePreviewButtonState(); |
| | | |
| | | // 更新保存按钮状态(变成可点击) |
| | | updateSaveButtonState(); |
| | | |
| | | // 强制刷新UI |
| | | SwingUtilities.invokeLater(() -> { |
| | | if (previewButton != null) { |
| | | previewButton.revalidate(); |
| | | previewButton.repaint(); |
| | | } |
| | | }); |
| | | |
| | | JOptionPane.showMessageDialog(this, "圆形障碍物边界坐标已生成", "成功", JOptionPane.INFORMATION_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | // 处理多边形障碍物 |
| | | if (!"polygon".equals(shapeKey)) { |
| | | JOptionPane.showMessageDialog(this, "只有多边形或圆形障碍物才需要生成边界坐标", "提示", JOptionPane.INFORMATION_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | // 检查绘制方式,只有割草机绘制的多边形才调用bianjieguihua2 |
| | | String method = formData.get("drawingMethod"); |
| | | if (!"mower".equals(method)) { |
| | | JOptionPane.showMessageDialog(this, "只有割草机绘制的多边形才支持生成边界坐标", "提示", JOptionPane.INFORMATION_MESSAGE); |
| | | return; |
| | |
| | | String landName = targetDikuai.getLandName(); |
| | | String boundary = targetDikuai.getBoundaryCoordinates(); |
| | | |
| | | // 获取障碍物坐标(优先使用生成的边界坐标,如果没有则使用原始坐标) |
| | | String obstacleCoords = generatedBoundary; |
| | | String shapeKey = formData.get("obstacleShape"); |
| | | String obstacleName = formData.get("obstacleName"); |
| | | |
| | | // 对于圆形障碍物,生成的边界坐标格式就是障碍物坐标格式,可以直接使用 |
| | | // 对于多边形障碍物,也需要使用生成的边界坐标 |
| | | // 如果生成的边界坐标不可用,尝试使用原始障碍物坐标 |
| | | if (!isMeaningfulValue(obstacleCoords)) { |
| | | obstacleCoords = formData.get("obstacleCoordinates"); |
| | | if (!isMeaningfulValue(obstacleCoords)) { |
| | | JOptionPane.showMessageDialog(this, "无法获取障碍物坐标进行预览", "提示", JOptionPane.INFORMATION_MESSAGE); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // 构建障碍物数据字符串,包含名称、形状和坐标 |
| | | // 格式:障碍物名称::形状::坐标 或 障碍物名称:形状:坐标 |
| | | final String obstacleData; |
| | | if (isMeaningfulValue(obstacleName) && isMeaningfulValue(shapeKey)) { |
| | | // 使用 :: 分隔符格式:名称::形状::坐标 |
| | | obstacleData = obstacleName.trim() + "::" + shapeKey.trim() + "::" + obstacleCoords; |
| | | } else if (isMeaningfulValue(shapeKey)) { |
| | | // 只有形状:形状::坐标 |
| | | obstacleData = shapeKey.trim() + "::" + obstacleCoords; |
| | | } else { |
| | | // 只有坐标 |
| | | obstacleData = obstacleCoords; |
| | | } |
| | | |
| | | // 关闭当前对话框 |
| | | setVisible(false); |
| | | |
| | |
| | | landNumber, |
| | | landName, |
| | | boundary, |
| | | generatedBoundary, // 使用生成的边界坐标作为障碍物坐标 |
| | | obstacleData, // 使用包含名称和形状的障碍物数据 |
| | | null, |
| | | () -> SwingUtilities.invokeLater(() -> { |
| | | // 重新打开新增障碍物步骤2页面 |
| | |
| | | saveButton.setVisible(true); |
| | | updateDrawingStatus(); |
| | | updatePreviewButtonState(); |
| | | // 对于圆形障碍物,确保预览按钮和生成边界按钮隐藏 |
| | | String shapeKey = formData.get("obstacleShape"); |
| | | boolean isCircle = "circle".equals(shapeKey); |
| | | if (isCircle) { |
| | | if (previewButton != null) { |
| | | previewButton.setVisible(false); |
| | | } |
| | | if (generateBoundaryButton != null) { |
| | | generateBoundaryButton.setVisible(false); |
| | | } |
| | | } |
| | | } |
| | | updateSaveButtonState(); |
| | | revalidate(); |