From 6799351be12deb2f713f2c0a2b4c467a6d1098c3 Mon Sep 17 00:00:00 2001
From: 张世豪 <979909237@qq.com>
Date: 星期二, 02 十二月 2025 19:51:00 +0800
Subject: [PATCH] 2025122
---
src/dikuai/addzhangaiwu.java | 854 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 800 insertions(+), 54 deletions(-)
diff --git a/src/dikuai/addzhangaiwu.java b/src/dikuai/addzhangaiwu.java
index c936a6b..bd7d8e1 100644
--- a/src/dikuai/addzhangaiwu.java
+++ b/src/dikuai/addzhangaiwu.java
@@ -36,15 +36,20 @@
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
+import javax.swing.JTextField;
import javax.swing.border.Border;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
import baseStation.BaseStation;
import gecaoji.Device;
+import set.Setsys;
import ui.UIConfig;
import zhuye.Coordinate;
import zhuye.Shouye;
import zhangaiwu.AddDikuai;
import zhangaiwu.Obstacledge;
+import zhangaiwu.yulanzhangaiwu;
/**
* 闅滅鐗╂柊澧�/缂栬緫瀵硅瘽妗嗐�傝璁¤瑷�鍙傝�� {@link AddDikuai}锛屾敮鎸侀�氳繃瀹炲湴缁樺埗閲囬泦闅滅鐗╁潗鏍囥��
@@ -61,6 +66,8 @@
private final Color MEDIUM_GRAY = new Color(233, 236, 239);
private final Color TEXT_COLOR = new Color(33, 37, 41);
private final Color LIGHT_TEXT = new Color(108, 117, 125);
+ private final Color DANGER_COLOR = new Color(220, 53, 69);
+ private final Color DANGER_LIGHT = new Color(255, 235, 238);
private final Dikuai targetDikuai;
private final List<ExistingObstacle> existingObstacles;
@@ -80,6 +87,9 @@
private JPanel selectedShapePanel;
private JButton drawButton;
private JLabel drawingStatusLabel;
+ private JTextField obstacleNameField;
+ private JPanel existingObstacleListPanel;
+ private JPanel step1NextButtonRow;
private int currentStep = 1;
private boolean drawingInProgress;
@@ -102,8 +112,11 @@
if (target == null) {
throw new IllegalArgumentException("targetDikuai 涓嶈兘涓虹┖");
}
- this.targetDikuai = target;
- this.existingObstacles = Collections.unmodifiableList(resolveExistingObstacles(target, obstacleNames));
+ Coordinate.coordinates.clear();
+ Coordinate.setStartSaveGngga(false);
+ Coordinate.clearActiveDeviceIdFilter();
+ this.targetDikuai = target;
+ this.existingObstacles = new ArrayList<>(resolveExistingObstacles(target, obstacleNames));
initializeUI();
setupEventHandlers();
preloadData();
@@ -161,7 +174,11 @@
infoContainer.add(createInfoRow("鍦板潡缂栧彿锛�", safeValue(targetDikuai.getLandNumber(), "鏈缃�")));
infoContainer.add(Box.createRigidArea(new Dimension(0, 16)));
infoContainer.add(createInfoRow("鍦板潡鍚嶇О锛�", safeValue(targetDikuai.getLandName(), "鏈懡鍚�")));
- infoContainer.add(Box.createRigidArea(new Dimension(0, 20)));
+ infoContainer.add(Box.createRigidArea(new Dimension(0, 16)));
+ infoContainer.add(createObstacleNameRow());
+ infoContainer.add(Box.createRigidArea(new Dimension(0, 12)));
+ infoContainer.add(createStep1NextButtonRow());
+ infoContainer.add(Box.createRigidArea(new Dimension(0, 12)));
infoContainer.add(buildExistingObstacleSection());
stepPanel.add(infoContainer);
@@ -182,32 +199,64 @@
section.add(titleLabel);
section.add(Box.createRigidArea(new Dimension(0, 8)));
+ existingObstacleListPanel = new JPanel();
+ existingObstacleListPanel.setLayout(new BoxLayout(existingObstacleListPanel, BoxLayout.Y_AXIS));
+ existingObstacleListPanel.setOpaque(false);
+ existingObstacleListPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ section.add(existingObstacleListPanel);
+ refreshExistingObstacleList();
+ return section;
+ }
+
+ private JPanel createStep1NextButtonRow() {
+ if (step1NextButtonRow == null) {
+ step1NextButtonRow = new JPanel();
+ step1NextButtonRow.setLayout(new BoxLayout(step1NextButtonRow, BoxLayout.X_AXIS));
+ step1NextButtonRow.setOpaque(false);
+ step1NextButtonRow.setAlignmentX(Component.LEFT_ALIGNMENT);
+ step1NextButtonRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, 36));
+ step1NextButtonRow.add(Box.createHorizontalGlue());
+ }
+ return step1NextButtonRow;
+ }
+
+ private void attachNextButtonToStep1Row() {
+ if (step1NextButtonRow == null || nextButton == null) {
+ return;
+ }
+ step1NextButtonRow.removeAll();
+ step1NextButtonRow.add(Box.createHorizontalGlue());
+ nextButton.setAlignmentX(Component.RIGHT_ALIGNMENT);
+ step1NextButtonRow.add(nextButton);
+ step1NextButtonRow.revalidate();
+ step1NextButtonRow.repaint();
+ }
+
+ private void refreshExistingObstacleList() {
+ if (existingObstacleListPanel == null) {
+ return;
+ }
+ existingObstacleListPanel.removeAll();
if (existingObstacles.isEmpty()) {
JLabel emptyLabel = new JLabel("鏆傛棤");
emptyLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 13));
emptyLabel.setForeground(LIGHT_TEXT);
emptyLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
- section.add(emptyLabel);
- return section;
- }
-
- JPanel listPanel = new JPanel();
- listPanel.setLayout(new BoxLayout(listPanel, BoxLayout.Y_AXIS));
- listPanel.setOpaque(false);
- listPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
-
- for (int i = 0; i < existingObstacles.size(); i++) {
- ExistingObstacle obstacle = existingObstacles.get(i);
- JPanel row = createObstacleSummaryRow(obstacle);
- row.setAlignmentX(Component.LEFT_ALIGNMENT);
- listPanel.add(row);
- if (i < existingObstacles.size() - 1) {
- listPanel.add(Box.createRigidArea(new Dimension(0, 6)));
+ existingObstacleListPanel.add(emptyLabel);
+ } else {
+ for (int i = 0; i < existingObstacles.size(); i++) {
+ ExistingObstacle obstacle = existingObstacles.get(i);
+ JPanel row = createObstacleSummaryRow(obstacle);
+ row.setAlignmentX(Component.LEFT_ALIGNMENT);
+ existingObstacleListPanel.add(row);
+ if (i < existingObstacles.size() - 1) {
+ existingObstacleListPanel.add(Box.createRigidArea(new Dimension(0, 6)));
+ }
}
}
-
- section.add(listPanel);
- return section;
+ existingObstacleListPanel.revalidate();
+ existingObstacleListPanel.repaint();
}
private JPanel createObstacleSummaryRow(ExistingObstacle obstacle) {
@@ -225,9 +274,14 @@
JButton editButton = createInlineButton("淇敼");
editButton.addActionListener(e -> populateObstacleForEditing(obstacle));
+ JButton deleteButton = createInlineIconButton("image/delete.png", "鍒犻櫎闅滅鐗�");
+ deleteButton.addActionListener(e -> handleDeleteExistingObstacle(obstacle));
+
row.add(infoLabel);
row.add(Box.createHorizontalGlue());
row.add(editButton);
+ row.add(Box.createRigidArea(new Dimension(6, 0)));
+ row.add(deleteButton);
return row;
}
@@ -257,10 +311,56 @@
return button;
}
+ private JButton createInlineIconButton(String iconPath, String tooltip) {
+ JButton button = new JButton();
+ button.setPreferredSize(new Dimension(32, 28));
+ button.setMinimumSize(new Dimension(32, 28));
+ button.setMaximumSize(new Dimension(32, 28));
+ button.setBackground(WHITE);
+ button.setBorder(BorderFactory.createLineBorder(DANGER_COLOR));
+ button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+ button.setFocusPainted(false);
+ button.setFocusable(false);
+ button.setOpaque(true);
+ button.setContentAreaFilled(true);
+ if (tooltip != null && !tooltip.trim().isEmpty()) {
+ button.setToolTipText(tooltip);
+ }
+
+ ImageIcon icon = null;
+ if (iconPath != null && !iconPath.trim().isEmpty()) {
+ ImageIcon rawIcon = new ImageIcon(iconPath);
+ if (rawIcon.getIconWidth() > 0 && rawIcon.getIconHeight() > 0) {
+ Image scaled = rawIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH);
+ icon = new ImageIcon(scaled);
+ }
+ }
+ if (icon != null) {
+ button.setIcon(icon);
+ } else {
+ button.setText("鍒�");
+ button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 12));
+ button.setForeground(DANGER_COLOR);
+ }
+
+ button.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseEntered(MouseEvent e) {
+ button.setBackground(DANGER_LIGHT);
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {
+ button.setBackground(WHITE);
+ }
+ });
+ return button;
+ }
+
private String buildObstacleSummaryText(ExistingObstacle obstacle) {
String name = obstacle.getName();
String shape = obstacle.getShapeDisplay();
- String coordPreview = buildCoordinatePreview(obstacle.getCoordinates(), 5);
+ String coordPreview = buildCoordinatePreview(obstacle.getDisplayCoordinates(), 5);
return String.format(Locale.CHINA, "%s锛�%s锛屽潗鏍�:%s", name, shape, coordPreview);
}
@@ -278,16 +378,70 @@
return sanitized.substring(0, keepLength) + "...";
}
+ private boolean deleteObstacleFromConfig(String obstacleName) {
+ String landNumber = targetDikuai != null ? targetDikuai.getLandNumber() : null;
+ if (!isMeaningfulValue(landNumber) || !isMeaningfulValue(obstacleName)) {
+ return false;
+ }
+ try {
+ File configFile = new File("Obstacledge.properties");
+ if (!configFile.exists()) {
+ return false;
+ }
+ Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager();
+ if (!manager.loadFromFile(configFile.getAbsolutePath())) {
+ return false;
+ }
+ Obstacledge.Plot plot = manager.getPlotById(landNumber.trim());
+ if (plot == null) {
+ return false;
+ }
+ if (!plot.removeObstacleByName(obstacleName.trim())) {
+ return false;
+ }
+ if (!manager.saveToFile(configFile.getAbsolutePath())) {
+ return false;
+ }
+ Dikuai.updateField(landNumber.trim(), "updateTime", currentTime());
+ Dikuai.saveToProperties();
+ Dikuaiguanli.notifyExternalCreation(landNumber.trim());
+ return true;
+ } catch (Exception ex) {
+ System.err.println("鍒犻櫎闅滅鐗╁け璐�: " + ex.getMessage());
+ return false;
+ }
+ }
+
private void populateObstacleForEditing(ExistingObstacle obstacle) {
if (obstacle == null) {
return;
}
- String coords = obstacle.getCoordinates();
- if (isMeaningfulValue(coords)) {
- formData.put("obstacleCoordinates", coords.trim());
+ String name = obstacle.getName();
+ if (isMeaningfulValue(name)) {
+ formData.put("obstacleName", name.trim());
+ formData.put("editingObstacleName", name.trim());
+ } else {
+ formData.remove("obstacleName");
+ formData.remove("editingObstacleName");
+ }
+ if (obstacleNameField != null) {
+ obstacleNameField.setText(isMeaningfulValue(name) ? name.trim() : "");
+ }
+ String xyCoords = obstacle.getXyCoordinates();
+ String displayCoords = obstacle.getDisplayCoordinates();
+ if (isMeaningfulValue(xyCoords)) {
+ formData.put("obstacleCoordinates", xyCoords.trim());
+ } else if (isMeaningfulValue(displayCoords)) {
+ formData.put("obstacleCoordinates", displayCoords.trim());
} else {
formData.remove("obstacleCoordinates");
}
+ String originalCoords = obstacle.getOriginalCoordinates();
+ if (isMeaningfulValue(originalCoords)) {
+ formData.put("obstacleOriginalCoordinates", originalCoords.trim());
+ } else {
+ formData.remove("obstacleOriginalCoordinates");
+ }
String shapeKey = obstacle.getShapeKey();
if (shapeKey != null) {
JPanel shapePanel = shapeOptionPanels.get(shapeKey);
@@ -297,6 +451,56 @@
showStep(2);
}
+ private void handleDeleteExistingObstacle(ExistingObstacle obstacle) {
+ if (obstacle == null) {
+ return;
+ }
+ String obstacleName = obstacle.getName();
+ if (!isMeaningfulValue(obstacleName)) {
+ JOptionPane.showMessageDialog(this, "鏃犳硶鍒犻櫎锛氶殰纰嶇墿鍚嶇О鏃犳晥", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+ int choice = JOptionPane.showConfirmDialog(this,
+ "纭畾瑕佸垹闄ら殰纰嶇墿 \"" + obstacleName + "\" 鍚楋紵姝ゆ搷浣滄棤娉曟挙閿�銆�",
+ "鍒犻櫎纭",
+ JOptionPane.YES_NO_OPTION,
+ JOptionPane.WARNING_MESSAGE);
+ if (choice != JOptionPane.YES_OPTION) {
+ return;
+ }
+ boolean removedFromConfig = deleteObstacleFromConfig(obstacleName);
+ boolean hasPersistedData = obstacle.hasPersistedData() || obstacle.getShapeKey() != null;
+ if (!removedFromConfig && hasPersistedData) {
+ JOptionPane.showMessageDialog(this, "鍒犻櫎澶辫触锛屾湭鑳藉湪閰嶇疆涓Щ闄よ闅滅鐗┿��", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+ if (!existingObstacles.remove(obstacle)) {
+ existingObstacles.removeIf(item -> obstacleName.equals(item != null ? item.getName() : null));
+ }
+ refreshExistingObstacleList();
+ clearEditingStateIfDeleted(obstacleName);
+ JOptionPane.showMessageDialog(this, "闅滅鐗╁凡鍒犻櫎", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+ }
+
+ private void clearEditingStateIfDeleted(String obstacleName) {
+ if (!isMeaningfulValue(obstacleName)) {
+ return;
+ }
+ String editingName = formData.get("editingObstacleName");
+ if (isMeaningfulValue(editingName) && obstacleName.equals(editingName)) {
+ formData.remove("editingObstacleName");
+ formData.remove("obstacleCoordinates");
+ formData.remove("obstacleOriginalCoordinates");
+ if (obstacleNameField != null) {
+ obstacleNameField.setText("");
+ } else {
+ formData.remove("obstacleName");
+ }
+ updateDrawingStatus();
+ updateSaveButtonState();
+ }
+ }
+
private JPanel createStep2Panel() {
JPanel stepPanel = new JPanel();
stepPanel.setLayout(new BoxLayout(stepPanel, BoxLayout.Y_AXIS));
@@ -322,7 +526,7 @@
stepPanel.add(methodOptionsPanel);
stepPanel.add(Box.createRigidArea(new Dimension(0, 20)));
- stepPanel.add(createSectionHeader("闅滅鐗╁舰鐘�", "澶氳竟褰㈤渶閲囬泦澶氫釜鐐癸紝鍦嗗舰鍙渶鍦嗗績涓庡渾鍛ㄤ笂涓�鐐�"));
+ stepPanel.add(createSectionHeader("闅滅鐗╁舰鐘�", "澶氳竟褰㈤渶閲囬泦澶氫釜鐐癸紝鍦嗗舰闇�鍦ㄥ渾鍛ㄤ笂閲囬泦鑷冲皯涓変釜鐐�"));
stepPanel.add(Box.createRigidArea(new Dimension(0, 10)));
shapeOptionsPanel = new JPanel();
@@ -331,7 +535,7 @@
shapeOptionsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
shapeOptionsPanel.add(createShapeOption("polygon", "澶氳竟褰�", "渚濇閲囬泦杞粨涓婄殑澶氫釜鐐规垨鑰呮部闅滅鐗╄竟缂樿蛋涓�鍦�"));
shapeOptionsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
- shapeOptionsPanel.add(createShapeOption("circle", "鍦嗗舰", "鍏堥噰闆嗗渾蹇冨潗鏍囷紝鍐嶉噰闆嗗渾鍛ㄤ笂涓�鐐�"));
+ shapeOptionsPanel.add(createShapeOption("circle", "鍦嗗舰", "娌垮渾鍛ㄤ换鎰忛噰闆嗕笁涓偣鑷姩鐢熸垚鍦�"));
stepPanel.add(shapeOptionsPanel);
stepPanel.add(Box.createRigidArea(new Dimension(0, 24)));
@@ -366,10 +570,11 @@
buttonPanel.add(prevButton);
buttonPanel.add(Box.createHorizontalGlue());
- buttonPanel.add(nextButton);
buttonPanel.add(Box.createRigidArea(new Dimension(12, 0)));
buttonPanel.add(saveButton);
+ attachNextButtonToStep1Row();
+
return buttonPanel;
}
@@ -616,6 +821,22 @@
nextButton.addActionListener(e -> {
if (currentStep == 1) {
+ String name = obstacleNameField != null ? obstacleNameField.getText() : null;
+ if (!isMeaningfulValue(name)) {
+ JOptionPane.showMessageDialog(this, "璇峰厛濉啓闅滅鐗╁悕绉�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ if (obstacleNameField != null) {
+ obstacleNameField.requestFocusInWindow();
+ }
+ return;
+ }
+ if (!isObstacleNameUnique(name)) {
+ JOptionPane.showMessageDialog(this, "闅滅鐗╁悕绉板凡瀛樺湪锛岃杈撳叆鍞竴鍚嶇О", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ if (obstacleNameField != null) {
+ obstacleNameField.requestFocusInWindow();
+ }
+ return;
+ }
+ formData.put("obstacleName", name.trim());
Coordinate.coordinates.clear();
showStep(2);
}
@@ -656,14 +877,28 @@
return;
}
+ String deviceId = resolveDrawingDeviceId(method);
+ if (!isMeaningfulValue(deviceId)) {
+ String idLabel = "handheld".equalsIgnoreCase(method) ? "鎵嬫寔璁惧缂栧彿" : "鍓茶崏鏈虹紪鍙�";
+ JOptionPane.showMessageDialog(this,
+ "鏈幏鍙栧埌鏈夋晥鐨�" + idLabel + "锛岃鍏堝湪绯荤粺璁剧疆涓畬鎴愰厤缃�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
activeDrawingShape = shape.toLowerCase(Locale.ROOT);
Coordinate.coordinates.clear();
+ Coordinate.setActiveDeviceIdFilter(deviceId);
Coordinate.setStartSaveGngga(true);
+ yulanzhangaiwu.startPreview(activeDrawingShape, baseStation);
drawingInProgress = true;
drawButton.setText("姝e湪缁樺埗...");
drawButton.setEnabled(false);
- drawingStatusLabel.setText("姝e湪閲囬泦闅滅鐗╁潗鏍囷紝璇峰湪涓荤晫闈㈠畬鎴愮粯鍒躲��");
+ if ("circle".equals(activeDrawingShape)) {
+ drawingStatusLabel.setText("姝e湪閲囬泦鍦嗗舰闅滅鐗╋紝璇锋部鍦嗗懆閲囬泦鑷冲皯涓変釜鐐瑰悗鍦ㄤ富鐣岄潰缁撴潫缁樺埗銆�");
+ } else {
+ drawingStatusLabel.setText("姝e湪閲囬泦闅滅鐗╁潗鏍囷紝璇峰湪涓荤晫闈㈠畬鎴愮粯鍒躲��");
+ }
if (activeSession == null) {
activeSession = new ObstacleDrawingSession();
@@ -692,6 +927,8 @@
public static void finishDrawingSession() {
Coordinate.setStartSaveGngga(false);
+ Coordinate.clearActiveDeviceIdFilter();
+ yulanzhangaiwu.stopPreview();
Shouye shouye = Shouye.getInstance();
if (shouye != null) {
@@ -710,11 +947,20 @@
showDialog(parent, activeSession.target);
}
+ public static String getActiveSessionBaseStation() {
+ if (activeSession != null && isMeaningfulValue(activeSession.baseStation)) {
+ return activeSession.baseStation.trim();
+ }
+ return null;
+ }
+
/**
* Stops an in-progress circle capture and reopens the wizard on step 2.
*/
public static void abortCircleDrawingAndReturn(String message) {
Coordinate.setStartSaveGngga(false);
+ Coordinate.clearActiveDeviceIdFilter();
+ yulanzhangaiwu.stopPreview();
Coordinate.coordinates.clear();
if (activeSession == null || activeSession.target == null) {
@@ -750,6 +996,13 @@
return;
}
+ String originalCoordStr = buildOriginalCoordinateString(captured);
+ if (isMeaningfulValue(originalCoordStr)) {
+ session.data.put("obstacleOriginalCoordinates", originalCoordStr);
+ } else {
+ session.data.remove("obstacleOriginalCoordinates");
+ }
+
List<double[]> xyPoints = convertToLocalXY(captured, session.baseStation);
if (xyPoints.isEmpty()) {
session.captureSuccessful = false;
@@ -759,18 +1012,28 @@
String shape = session.data.get("obstacleShape");
if ("circle".equals(shape)) {
- if (xyPoints.size() < 2) {
+ if (xyPoints.size() < 3) {
session.captureSuccessful = false;
- session.captureMessage = "鍦嗗舰闅滅鐗╄嚦灏戦渶瑕佷袱涓噰闆嗙偣锛堝渾蹇冨拰鍦嗗懆鐐癸級";
+ session.captureMessage = "鍦嗗舰闅滅鐗╄嚦灏戦渶瑕佷笁涓噰闆嗙偣锛堝渾鍛ㄤ笂鐨勭偣锛�";
return;
}
- double[] center = xyPoints.get(0);
- double[] radiusPoint = xyPoints.get(xyPoints.size() - 1);
+ CircleFitResult circle = fitCircleFromPoints(xyPoints);
+ if (circle == null) {
+ session.captureSuccessful = false;
+ session.captureMessage = "鏃犳硶鏍规嵁閲囬泦鐨勭偣鐢熸垚鍦嗭紝璇风‘淇濋�夋嫨浜嗕笁涓潪鍏辩嚎鐨勫渾鍛ㄧ偣";
+ return;
+ }
+ double[] radiusPoint = pickRadiusPoint(xyPoints, circle);
+ if (radiusPoint == null) {
+ session.captureSuccessful = false;
+ session.captureMessage = "閲囬泦鐨勫渾鍛ㄧ偣寮傚父锛屾棤娉曠敓鎴愬渾";
+ return;
+ }
String result = String.format(Locale.US, "%.2f,%.2f;%.2f,%.2f",
- center[0], center[1], radiusPoint[0], radiusPoint[1]);
+ circle.centerX, circle.centerY, radiusPoint[0], radiusPoint[1]);
session.data.put("obstacleCoordinates", result);
session.captureSuccessful = true;
- session.captureMessage = "宸查噰闆嗗渾褰㈤殰纰嶇墿鍧愭爣";
+ session.captureMessage = "宸查噰闆嗗渾褰㈤殰纰嶇墿锛屽叡 " + xyPoints.size() + " 涓偣";
} else {
if (xyPoints.size() < 3) {
session.captureSuccessful = false;
@@ -816,6 +1079,67 @@
return result;
}
+ private static String buildOriginalCoordinateString(List<Coordinate> coords) {
+ if (coords == null || coords.isEmpty()) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (Coordinate coord : coords) {
+ if (coord == null) {
+ continue;
+ }
+ String latToken = sanitizeCoordinateToken(coord.getLatitude());
+ String lonToken = sanitizeCoordinateToken(coord.getLongitude());
+ if (latToken == null || lonToken == null) {
+ continue;
+ }
+ char latDir = sanitizeDirection(coord.getLatDirection(), 'N');
+ char lonDir = sanitizeDirection(coord.getLonDirection(), 'E');
+ if (sb.length() > 0) {
+ sb.append(";");
+ }
+ sb.append(latToken)
+ .append(",")
+ .append(latDir)
+ .append(",")
+ .append(lonToken)
+ .append(",")
+ .append(lonDir);
+ }
+ return sb.length() > 0 ? sb.toString() : null;
+ }
+
+ private static String sanitizeCoordinateToken(String token) {
+ if (token == null) {
+ return null;
+ }
+ String trimmed = token.trim();
+ if (trimmed.isEmpty()) {
+ return null;
+ }
+ try {
+ Double.parseDouble(trimmed);
+ return trimmed;
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ }
+
+ private static char sanitizeDirection(String direction, char fallback) {
+ if (direction == null) {
+ return fallback;
+ }
+ String trimmed = direction.trim();
+ if (trimmed.isEmpty()) {
+ return fallback;
+ }
+ char ch = Character.toUpperCase(trimmed.charAt(0));
+ if (ch != 'N' && ch != 'S' && ch != 'E' && ch != 'W') {
+ return fallback;
+ }
+ return ch;
+ }
+
private static double parseDMToDecimal(String dmm, String direction) {
if (dmm == null || dmm.trim().isEmpty()) {
return Double.NaN;
@@ -847,6 +1171,101 @@
return new double[]{eastMeters, northMeters};
}
+ private static CircleFitResult fitCircleFromPoints(List<double[]> points) {
+ if (points == null || points.size() < 3) {
+ return null;
+ }
+ CircleFitResult best = null;
+ double bestScore = 0.0;
+ int n = points.size();
+ for (int i = 0; i < n - 2; i++) {
+ double[] p1 = points.get(i);
+ for (int j = i + 1; j < n - 1; j++) {
+ double[] p2 = points.get(j);
+ for (int k = j + 1; k < n; k++) {
+ double[] p3 = points.get(k);
+ CircleFitResult candidate = circleFromThreePoints(p1, p2, p3);
+ if (candidate == null || candidate.radius <= 0) {
+ continue;
+ }
+ double minEdge = Math.min(distance(p1, p2), Math.min(distance(p2, p3), distance(p1, p3)));
+ if (minEdge > bestScore) {
+ bestScore = minEdge;
+ best = candidate;
+ }
+ }
+ }
+ }
+ return best;
+ }
+
+ private static CircleFitResult circleFromThreePoints(double[] p1, double[] p2, double[] p3) {
+ double x1 = p1[0];
+ double y1 = p1[1];
+ double x2 = p2[0];
+ double y2 = p2[1];
+ double x3 = p3[0];
+ double y3 = p3[1];
+
+ double determinant = 2.0 * (x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2));
+ if (Math.abs(determinant) < 1e-6) {
+ return null;
+ }
+
+ double x1Sq = x1 * x1 + y1 * y1;
+ double x2Sq = x2 * x2 + y2 * y2;
+ double x3Sq = x3 * x3 + y3 * y3;
+
+ double centerX = (x1Sq * (y2 - y3) + x2Sq * (y3 - y1) + x3Sq * (y1 - y2)) / determinant;
+ double centerY = (x1Sq * (x3 - x2) + x2Sq * (x1 - x3) + x3Sq * (x2 - x1)) / determinant;
+ double radius = Math.hypot(centerX - x1, centerY - y1);
+
+ if (!Double.isFinite(centerX) || !Double.isFinite(centerY) || !Double.isFinite(radius)) {
+ return null;
+ }
+ if (radius < 0.05) {
+ return null;
+ }
+
+ return new CircleFitResult(centerX, centerY, radius, new double[]{x1, y1});
+ }
+
+ private static double[] pickRadiusPoint(List<double[]> points, CircleFitResult circle) {
+ if (circle == null || points == null || points.isEmpty()) {
+ return null;
+ }
+ double[] best = null;
+ double bestDistance = 0.0;
+ for (double[] pt : points) {
+ double dist = Math.hypot(pt[0] - circle.centerX, pt[1] - circle.centerY);
+ if (dist > bestDistance) {
+ bestDistance = dist;
+ best = pt;
+ }
+ }
+ return best;
+ }
+
+ private static double distance(double[] a, double[] b) {
+ double dx = a[0] - b[0];
+ double dy = a[1] - b[1];
+ return Math.hypot(dx, dy);
+ }
+
+ private static final class CircleFitResult {
+ final double centerX;
+ final double centerY;
+ final double radius;
+ final double[] referencePoint;
+
+ CircleFitResult(double centerX, double centerY, double radius, double[] referencePoint) {
+ this.centerX = centerX;
+ this.centerY = centerY;
+ this.radius = radius;
+ this.referencePoint = referencePoint;
+ }
+ }
+
private List<ExistingObstacle> resolveExistingObstacles(Dikuai target, List<String> providedNames) {
String landNumber = target != null ? target.getLandNumber() : null;
Map<String, ExistingObstacle> configMap = loadObstacleDetailsFromConfig(landNumber);
@@ -914,11 +1333,11 @@
}
String trimmedName = name.trim();
Obstacledge.ObstacleShape shape = obstacle.getShape();
- String xyCoords = obstacle.getXyCoordsString();
- String original = obstacle.getOriginalCoordsString();
- String coords = isMeaningfulValue(xyCoords) ? xyCoords.trim()
- : (isMeaningfulValue(original) ? original.trim() : "");
- details.put(trimmedName, new ExistingObstacle(trimmedName, shape, coords));
+ String xyCoords = obstacle.getXyCoordsString();
+ String original = obstacle.getOriginalCoordsString();
+ String xy = isMeaningfulValue(xyCoords) ? xyCoords.trim() : "";
+ String originalTrimmed = isMeaningfulValue(original) ? original.trim() : "";
+ details.put(trimmedName, new ExistingObstacle(trimmedName, shape, xy, originalTrimmed));
}
} catch (Exception ex) {
System.err.println("鍔犺浇闅滅鐗╄鎯呭け璐�: " + ex.getMessage());
@@ -927,10 +1346,9 @@
}
private void preloadData() {
- String existing = targetDikuai.getObstacleCoordinates();
- if (isMeaningfulValue(existing)) {
- formData.put("obstacleCoordinates", existing.trim());
- }
+ formData.remove("obstacleCoordinates");
+ formData.remove("obstacleOriginalCoordinates");
+ formData.remove("editingObstacleName");
updateDrawingStatus();
}
@@ -979,11 +1397,40 @@
private void updateSaveButtonState() {
if (saveButton != null) {
- saveButton.setEnabled(isMeaningfulValue(formData.get("obstacleCoordinates")));
+ boolean hasCoords = isMeaningfulValue(formData.get("obstacleCoordinates"));
+ boolean hasName = isMeaningfulValue(formData.get("obstacleName"));
+ boolean enabled = hasCoords && hasName;
+ saveButton.setEnabled(enabled);
+ if (enabled) {
+ saveButton.setBackground(PRIMARY_COLOR);
+ saveButton.setForeground(WHITE);
+ saveButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+ } else {
+ saveButton.setBackground(MEDIUM_GRAY);
+ saveButton.setForeground(TEXT_COLOR);
+ saveButton.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ }
}
}
private boolean validateStep2() {
+ String name = formData.get("obstacleName");
+ if (!isMeaningfulValue(name)) {
+ JOptionPane.showMessageDialog(this, "璇峰~鍐欓殰纰嶇墿鍚嶇О", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ showStep(1);
+ if (obstacleNameField != null) {
+ obstacleNameField.requestFocusInWindow();
+ }
+ return false;
+ }
+ if (!isObstacleNameUnique(name)) {
+ JOptionPane.showMessageDialog(this, "闅滅鐗╁悕绉板凡瀛樺湪锛岃杈撳叆鍞竴鍚嶇О", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+ showStep(1);
+ if (obstacleNameField != null) {
+ obstacleNameField.requestFocusInWindow();
+ }
+ return false;
+ }
if (!isMeaningfulValue(formData.get("obstacleCoordinates"))) {
JOptionPane.showMessageDialog(this, "璇峰厛瀹屾垚闅滅鐗╃粯鍒�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
return false;
@@ -995,34 +1442,198 @@
if (!validateStep2()) {
return;
}
- String coords = formData.get("obstacleCoordinates").trim();
String landNumber = targetDikuai.getLandNumber();
+ String obstacleName = formData.get("obstacleName");
+ String coordsValue = formData.get("obstacleCoordinates");
+ String originalValue = formData.get("obstacleOriginalCoordinates");
+ String shapeKey = formData.get("obstacleShape");
+ String previousName = formData.get("editingObstacleName");
+
+ if (!isMeaningfulValue(coordsValue)) {
+ JOptionPane.showMessageDialog(this, "闅滅鐗╁潗鏍囨棤鏁�", "閿欒", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ String coords = coordsValue.trim();
+ String originalCoords = isMeaningfulValue(originalValue) ? originalValue.trim() : null;
+ String trimmedName = isMeaningfulValue(obstacleName) ? obstacleName.trim() : null;
+ if (!isMeaningfulValue(trimmedName)) {
+ JOptionPane.showMessageDialog(this, "闅滅鐗╁悕绉版棤鏁�", "閿欒", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
+ formData.put("obstacleName", trimmedName);
+ if (isMeaningfulValue(previousName)) {
+ formData.put("editingObstacleName", trimmedName);
+ }
+
+ if (!persistObstacleToConfig(landNumber, previousName, trimmedName, shapeKey, coords, originalCoords)) {
+ JOptionPane.showMessageDialog(this, "鍐欏叆闅滅鐗╅厤缃け璐ワ紝璇烽噸璇�", "閿欒", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+
if (!Dikuai.updateField(landNumber, "obstacleCoordinates", coords)) {
JOptionPane.showMessageDialog(this, "鏃犳硶鏇存柊闅滅鐗╁潗鏍�", "閿欒", JOptionPane.ERROR_MESSAGE);
return;
}
+ if (originalCoords != null) {
+ if (!Dikuai.updateField(landNumber, "obstacleOriginalCoordinates", originalCoords)) {
+ JOptionPane.showMessageDialog(this, "鏃犳硶鏇存柊闅滅鐗╁師濮嬪潗鏍�", "閿欒", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ } else if (!Dikuai.updateField(landNumber, "obstacleOriginalCoordinates", "-1")) {
+ JOptionPane.showMessageDialog(this, "鏃犳硶閲嶇疆闅滅鐗╁師濮嬪潗鏍�", "閿欒", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
Dikuai.updateField(landNumber, "updateTime", currentTime());
Dikuai.saveToProperties();
Dikuaiguanli.notifyExternalCreation(landNumber);
- JOptionPane.showMessageDialog(this, "闅滅鐗╁潗鏍囧凡淇濆瓨", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
+ JOptionPane.showMessageDialog(this, "闅滅鐗╂暟鎹凡淇濆瓨", "鎴愬姛", JOptionPane.INFORMATION_MESSAGE);
activeSession = null;
dispose();
}
+ private boolean persistObstacleToConfig(String landNumber, String previousName, String obstacleName,
+ String shapeKey, String xyCoords, String originalCoords) {
+ if (!isMeaningfulValue(landNumber) || !isMeaningfulValue(obstacleName) || !isMeaningfulValue(xyCoords)) {
+ return false;
+ }
+
+ String normalizedLandNumber = landNumber.trim();
+ String normalizedNewName = obstacleName.trim();
+ String normalizedPrevious = isMeaningfulValue(previousName) ? previousName.trim() : null;
+
+ String normalizedXy = xyCoords.trim();
+
+ try {
+ File configFile = new File("Obstacledge.properties");
+ Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager();
+ if (configFile.exists()) {
+ if (!manager.loadFromFile(configFile.getAbsolutePath())) {
+ return false;
+ }
+ }
+
+ Obstacledge.Plot plot = manager.getPlotById(normalizedLandNumber);
+ if (plot == null) {
+ plot = new Obstacledge.Plot(normalizedLandNumber);
+ manager.addPlot(plot);
+ }
+
+ ensurePlotBaseStation(plot);
+
+ if (normalizedPrevious != null && !normalizedPrevious.equals(normalizedNewName)) {
+ plot.removeObstacleByName(normalizedPrevious);
+ }
+
+ Obstacledge.Obstacle obstacle = plot.getObstacleByName(normalizedNewName);
+ if (obstacle == null) {
+ obstacle = new Obstacledge.Obstacle(normalizedLandNumber, normalizedNewName,
+ determineObstacleShape(shapeKey, normalizedXy));
+ plot.addObstacle(obstacle);
+ }
+
+ obstacle.setPlotId(normalizedLandNumber);
+ obstacle.setObstacleName(normalizedNewName);
+ obstacle.setShape(determineObstacleShape(shapeKey, normalizedXy));
+
+ obstacle.setXyCoordinates(new ArrayList<Obstacledge.XYCoordinate>());
+ obstacle.setOriginalCoordinates(new ArrayList<Obstacledge.DMCoordinate>());
+
+ obstacle.setXyCoordsString(normalizedXy);
+
+ if (isMeaningfulValue(originalCoords)) {
+ try {
+ obstacle.setOriginalCoordsString(originalCoords);
+ } catch (Exception parseEx) {
+ System.err.println("瑙f瀽闅滅鐗╁師濮嬪潗鏍囧け璐ワ紝灏嗕娇鐢ㄥ崰浣嶅��: " + parseEx.getMessage());
+ obstacle.setOriginalCoordinates(new ArrayList<Obstacledge.DMCoordinate>());
+ }
+ }
+
+ ensurePlaceholderOriginalCoords(obstacle);
+
+ return manager.saveToFile(configFile.getAbsolutePath());
+ } catch (Exception ex) {
+ System.err.println("淇濆瓨闅滅鐗╅厤缃け璐�: " + ex.getMessage());
+ return false;
+ }
+ }
+
+ private Obstacledge.ObstacleShape determineObstacleShape(String shapeKey, String xyCoords) {
+ if (isMeaningfulValue(shapeKey)) {
+ String normalized = shapeKey.trim().toLowerCase(Locale.ROOT);
+ if ("circle".equals(normalized) || "鍦嗗舰".equals(normalized) || "0".equals(normalized)) {
+ return Obstacledge.ObstacleShape.CIRCLE;
+ }
+ if ("polygon".equals(normalized) || "澶氳竟褰�".equals(normalized) || "1".equals(normalized)) {
+ return Obstacledge.ObstacleShape.POLYGON;
+ }
+ }
+
+ int pairCount = countCoordinatePairs(xyCoords);
+ if (pairCount <= 0) {
+ return Obstacledge.ObstacleShape.POLYGON;
+ }
+ if (pairCount <= 2) {
+ return Obstacledge.ObstacleShape.CIRCLE;
+ }
+ return Obstacledge.ObstacleShape.POLYGON;
+ }
+
+ private void ensurePlaceholderOriginalCoords(Obstacledge.Obstacle obstacle) {
+ if (obstacle == null) {
+ return;
+ }
+ List<Obstacledge.DMCoordinate> originals = obstacle.getOriginalCoordinates();
+ if (originals != null && !originals.isEmpty()) {
+ return;
+ }
+ List<Obstacledge.XYCoordinate> xyCoords = obstacle.getXyCoordinates();
+ int pointCount = (xyCoords != null && !xyCoords.isEmpty()) ? xyCoords.size() : 1;
+ List<Obstacledge.DMCoordinate> placeholders = new ArrayList<>(pointCount * 2);
+ for (int i = 0; i < pointCount; i++) {
+ placeholders.add(new Obstacledge.DMCoordinate(0.0, 'N'));
+ placeholders.add(new Obstacledge.DMCoordinate(0.0, 'E'));
+ }
+ obstacle.setOriginalCoordinates(placeholders);
+ }
+
+ private void ensurePlotBaseStation(Obstacledge.Plot plot) {
+ if (plot == null) {
+ return;
+ }
+ String existing = plot.getBaseStationString();
+ if (isMeaningfulValue(existing)) {
+ return;
+ }
+ String baseStation = resolveBaseStationCoordinates();
+ if (!isMeaningfulValue(baseStation)) {
+ return;
+ }
+ try {
+ plot.setBaseStationString(baseStation.trim());
+ } catch (Exception ex) {
+ System.err.println("璁剧疆鍩哄噯绔欏潗鏍囧け璐�: " + ex.getMessage());
+ }
+ }
+
private static final class ExistingObstacle {
private final String name;
private final Obstacledge.ObstacleShape shape;
- private final String coordinates;
+ private final String xyCoordinates;
+ private final String originalCoordinates;
- ExistingObstacle(String name, Obstacledge.ObstacleShape shape, String coordinates) {
+ ExistingObstacle(String name, Obstacledge.ObstacleShape shape, String xyCoordinates, String originalCoordinates) {
this.name = name != null ? name : "";
this.shape = shape;
- this.coordinates = coordinates != null ? coordinates : "";
+ this.xyCoordinates = safeCoordString(xyCoordinates);
+ this.originalCoordinates = safeCoordString(originalCoordinates);
}
static ExistingObstacle placeholder(String name) {
- return new ExistingObstacle(name, null, "");
+ return new ExistingObstacle(name, null, "", "");
}
String getName() {
@@ -1047,7 +1658,38 @@
}
String getCoordinates() {
- return coordinates;
+ return getDisplayCoordinates();
+ }
+
+ String getDisplayCoordinates() {
+ return hasText(xyCoordinates) ? xyCoordinates : originalCoordinates;
+ }
+
+ String getXyCoordinates() {
+ return xyCoordinates;
+ }
+
+ String getOriginalCoordinates() {
+ return originalCoordinates;
+ }
+
+ boolean hasPersistedData() {
+ return hasText(xyCoordinates) || hasText(originalCoordinates);
+ }
+
+ private static String safeCoordString(String value) {
+ if (value == null) {
+ return "";
+ }
+ return value.trim();
+ }
+
+ private static boolean hasText(String value) {
+ if (value == null) {
+ return false;
+ }
+ String trimmed = value.trim();
+ return !trimmed.isEmpty() && !"-1".equals(trimmed);
}
}
@@ -1076,6 +1718,11 @@
formData.clear();
formData.putAll(session.data);
+ String sessionName = session.data.get("obstacleName");
+ if (obstacleNameField != null) {
+ obstacleNameField.setText(sessionName != null ? sessionName : "");
+ }
+
String method = session.data.get("drawingMethod");
if (method != null) {
JPanel panel = methodOptionPanels.get(method);
@@ -1092,7 +1739,7 @@
}
}
- if (session.captureMessage != null) {
+ if (session.captureMessage != null && !session.captureSuccessful) {
JOptionPane.showMessageDialog(this,
session.captureMessage,
session.captureSuccessful ? "鎴愬姛" : "鎻愮ず",
@@ -1108,6 +1755,93 @@
return createInfoRow(label, value, null);
}
+ private JPanel createObstacleNameRow() {
+ JPanel row = new JPanel();
+ row.setLayout(new BoxLayout(row, BoxLayout.X_AXIS));
+ row.setOpaque(false);
+ row.setAlignmentX(Component.LEFT_ALIGNMENT);
+ row.setMaximumSize(new Dimension(Integer.MAX_VALUE, 36));
+
+ JLabel label = new JLabel("闅滅鐗╁悕绉帮細");
+ label.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 14));
+ label.setForeground(TEXT_COLOR);
+ label.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ if (obstacleNameField == null) {
+ obstacleNameField = new JTextField();
+ obstacleNameField.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+ obstacleNameField.setColumns(16);
+ Dimension fieldSize = new Dimension(240, 30);
+ obstacleNameField.setPreferredSize(fieldSize);
+ obstacleNameField.setMinimumSize(fieldSize);
+ obstacleNameField.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
+ obstacleNameField.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createLineBorder(BORDER_COLOR, 1),
+ BorderFactory.createEmptyBorder(4, 8, 4, 8)));
+ obstacleNameField.getDocument().addDocumentListener(new DocumentListener() {
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ handleObstacleNameChanged();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ handleObstacleNameChanged();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ handleObstacleNameChanged();
+ }
+ });
+ }
+
+ String existing = formData.get("obstacleName");
+ if (existing != null && !existing.equals(obstacleNameField.getText())) {
+ obstacleNameField.setText(existing);
+ }
+
+ row.add(label);
+ row.add(Box.createRigidArea(new Dimension(10, 0)));
+ row.add(obstacleNameField);
+ row.add(Box.createHorizontalGlue());
+ return row;
+ }
+
+ private void handleObstacleNameChanged() {
+ if (obstacleNameField == null) {
+ return;
+ }
+ String text = obstacleNameField.getText();
+ if (isMeaningfulValue(text)) {
+ formData.put("obstacleName", text.trim());
+ } else {
+ formData.remove("obstacleName");
+ }
+ updateSaveButtonState();
+ }
+
+ private boolean isObstacleNameUnique(String candidate) {
+ if (!isMeaningfulValue(candidate)) {
+ return false;
+ }
+ String trimmed = candidate.trim().toLowerCase(Locale.ROOT);
+ String original = formData.get("editingObstacleName");
+ if (isMeaningfulValue(original) && trimmed.equals(original.trim().toLowerCase(Locale.ROOT))) {
+ return true;
+ }
+ for (ExistingObstacle obstacle : existingObstacles) {
+ if (obstacle == null) {
+ continue;
+ }
+ String existingName = obstacle.getName();
+ if (isMeaningfulValue(existingName) && trimmed.equals(existingName.trim().toLowerCase(Locale.ROOT))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private JPanel createInfoRow(String label, String value, String tooltip) {
JPanel row = new JPanel();
row.setLayout(new BoxLayout(row, BoxLayout.X_AXIS));
@@ -1206,6 +1940,18 @@
return null;
}
+ private String resolveDrawingDeviceId(String method) {
+ if (!isMeaningfulValue(method)) {
+ return null;
+ }
+ String key = "handheld".equalsIgnoreCase(method) ? "handheldMarkerId" : "mowerId";
+ String value = Setsys.getPropertyValue(key);
+ if (!isMeaningfulValue(value)) {
+ return null;
+ }
+ return value.trim();
+ }
+
private String safeValue(String value, String fallback) {
if (!isMeaningfulValue(value)) {
return fallback;
--
Gitblit v1.10.0