diff --git a/src/GUI/gui.py b/src/GUI/gui.py index 5a92c16..d1938ec 100644 --- a/src/GUI/gui.py +++ b/src/GUI/gui.py @@ -147,11 +147,11 @@ class ToggleSwitch(QWidget): class ControlStationUI(QMainWindow): planning_finished = pyqtSignal(object) - VERSION = '2.8.0' + VERSION = '2.8.2' FONT_SCALE_MIN = 70 FONT_SCALE_MAX = 180 FONT_SCALE_DEFAULT = 100 - + def __init__(self): super().__init__() self.setWindowTitle(f'GCS v{self.VERSION}') @@ -1596,6 +1596,8 @@ class ControlStationUI(QMainWindow): group.planned_waypoints = None group.mission_target = None self.drone_map.clear_mission_plan_for_group(group_id) + self.drone_map.clear_grid_sweep_area_for_group( + group_id, restart_selection=(group.mission_type == 'GRID_SWEEP')) panel = self.group_panels.get(group_id) if panel: panel.update_status() @@ -1876,6 +1878,7 @@ class ControlStationUI(QMainWindow): if group.executor: group.executor.stop() self.drone_map.clear_mission_plan_for_group(group_id) + self.drone_map.clear_grid_sweep_area_for_group(group_id) # 只有當操作目標組是 active 組時,才更新 UI if group_id == self.active_group_id: @@ -1910,6 +1913,7 @@ class ControlStationUI(QMainWindow): # 移除地圖上的任務計畫 self.drone_map.clear_mission_plan_for_group(group_id) + self.drone_map.clear_grid_sweep_area_for_group(group_id) # 移除 tab for i in range(self.group_tab_widget.count()): @@ -2256,10 +2260,14 @@ class ControlStationUI(QMainWindow): center_lat, center_lon, _ = center_origin target_lat, target_lon = context['draw_target'] group.mission_target = (float(target_lat), float(target_lon)) - self.drone_map.draw_mission_plan_for_group( - group_id, group.color, - center_lat, center_lon, target_lat, target_lon - ) + if expected_mission_type == 'GRID_SWEEP': + # Grid Sweep 的範圍已由矩形圖層表示,不另外顯示中心/目標點。 + self.drone_map.clear_mission_plan_for_group(group_id) + else: + self.drone_map.draw_mission_plan_for_group( + group_id, group.color, + center_lat, center_lon, target_lat, target_lon + ) self._launch_verification( context['verification_type'], drone_gps_positions, selected_drones, @@ -2351,6 +2359,10 @@ class ControlStationUI(QMainWindow): self.statusBar().showMessage("矩形座標解析失敗", 3000) return + # 將確認後的框選範圍存入群組專屬圖層,供停止任務時精準清除。 + self.drone_map.draw_grid_sweep_area_for_group( + group.group_id, group.color, rect_corners) + panel = self.group_panels.get(group.group_id) params = panel.get_mission_params() if panel else {} base_alt = params.get('altitude', 10.0) diff --git a/src/GUI/map_layout.py b/src/GUI/map_layout.py index 56fdbc0..df40034 100644 --- a/src/GUI/map_layout.py +++ b/src/GUI/map_layout.py @@ -368,6 +368,8 @@ class DroneMap: // 多群組任務規劃 — 每個 group 各自的 layer group var groupMissionLayers = {}; // group_id → L.layerGroup + // Grid Sweep 框選範圍也依群組保存,停止時可單獨清除。 + var gridSweepAreaLayers = {}; // group_id → L.layerGroup // 選擇工具變量 var selectionMode = null; // 'drones', 'rect', 'polygon', 'route', null @@ -581,23 +583,6 @@ class DroneMap: [bounds.getSouth(), bounds.getEast()], [bounds.getSouth(), bounds.getWest()] ]; - - L.rectangle(bounds, { - color: '#FF6B6B', - fillColor: '#FF6B6B', - fillOpacity: 0.2, - weight: 2 - }).addTo(selectionLayer); - - rectPoints.forEach(point => { - L.circleMarker(point, { - radius: 5, - color: '#FF6B6B', - fillColor: '#FF6B6B', - fillOpacity: 0.8 - }).addTo(selectionLayer); - }); - if (bridge) { var pointsStr = JSON.stringify(rectPoints); bridge.rectangleSelected(pointsStr); @@ -974,6 +959,41 @@ class DroneMap: } } + function drawGridSweepAreaForGroup(groupId, color, rectPoints) { + clearGridSweepAreaForGroup(groupId); + var layerGroup = L.layerGroup().addTo(map); + gridSweepAreaLayers[groupId] = layerGroup; + var bounds = L.latLngBounds(rectPoints); + + L.rectangle(bounds, { + color: color, + fillColor: color, + fillOpacity: 0.2, + weight: 2 + }).addTo(layerGroup); + rectPoints.forEach(point => { + L.circleMarker(point, { + radius: 5, + color: color, + fillColor: color, + fillOpacity: 0.8 + }).addTo(layerGroup); + }); + } + + function clearGridSweepAreaForGroup(groupId, restartSelection) { + if (gridSweepAreaLayers[groupId]) { + map.removeLayer(gridSweepAreaLayers[groupId]); + delete gridSweepAreaLayers[groupId]; + } + if (restartSelection) { + clearSelectionMode(); + selectionMode = 'rect'; + map.dragging.disable(); + console.log('Grid Sweep: 已清除範圍並重新進入框選模式'); + } + } + function clearAllMissionPlans() { for (var gid in groupMissionLayers) { map.removeLayer(groupMissionLayers[gid]); @@ -1110,6 +1130,23 @@ class DroneMap: self.map_view.page().runJavaScript(f"clearMissionPlanForGroup('{group_id}');") _log("INFO", f"地圖已清除 Group {group_id} 任務規劃") + def draw_grid_sweep_area_for_group(self, group_id, color, rect_corners): + """繪製指定群組的 Grid Sweep 框選範圍。""" + if self.map_loaded: + group_json = json.dumps(group_id) + color_json = json.dumps(color) + corners_json = json.dumps(rect_corners) + self.map_view.page().runJavaScript( + f"drawGridSweepAreaForGroup({group_json}, {color_json}, {corners_json});") + + def clear_grid_sweep_area_for_group(self, group_id, restart_selection=False): + """清除指定群組的 Grid Sweep 範圍,可選擇重新進入框選模式。""" + if self.map_loaded: + group_json = json.dumps(group_id) + restart_js = str(bool(restart_selection)).lower() + self.map_view.page().runJavaScript( + f"clearGridSweepAreaForGroup({group_json}, {restart_js});") + def set_mission_mode(self, mode): """從 Python 端切換地圖的任務模式(觸發框選/路徑標記等)""" if self.map_loaded: