#!/usr/bin/env python3 from PyQt6.QtWebEngineWidgets import QWebEngineView from PyQt6.QtCore import QTimer, pyqtSignal, QObject, pyqtSlot from PyQt6.QtWebChannel import QWebChannel class DroneMap: """無人機地圖類別 - 負責管理 Leaflet 地圖顯示""" def __init__(self): """初始化地圖""" self.map_view = QWebEngineView() self.map_loaded = False self.pending_map_updates = {} # 創建橋接對象 self.bridge = MapBridge() # 設置 QWebChannel self.channel = QWebChannel() self.channel.registerObject('bridge', self.bridge) self.map_view.page().setWebChannel(self.channel) # 設置地圖 HTML inline_html = '''
''' self.map_view.setHtml(inline_html) self.map_view.loadFinished.connect(self._on_map_loaded) # 設置地圖更新計時器 self.map_update_timer = QTimer() self.map_update_timer.timeout.connect(self.update_map_positions) self.map_update_timer.start(200) # 每 200ms 更新一次 def _on_map_loaded(self, ok: bool): """地圖加載完成回調""" if ok: self.map_loaded = True else: print("⚠️ 地圖加載失敗") def update_drone_position(self, drone_id, lat, lon, heading): """更新無人機位置(加入待處理隊列)""" self.pending_map_updates[drone_id] = (lat, lon, heading) def update_map_positions(self): """批量更新地圖上的無人機位置""" if not self.map_loaded or not self.pending_map_updates: return # 批量執行所有待更新的位置 js_commands = [] for drone_id, (lat, lon, heading) in self.pending_map_updates.items(): js_commands.append(f"updateDrone({lat:.6f}, {lon:.6f}, '{drone_id}', {heading:.1f});") if js_commands: combined_js = "\n".join(js_commands) self.map_view.page().runJavaScript(combined_js) # 清空待更新緩存 self.pending_map_updates.clear() def clear_trajectories(self): """清除所有軌跡""" if self.map_loaded: self.map_view.page().runJavaScript("clearAllTrajectories();") def focus_on_drone(self, drone_id): """聚焦到指定無人機""" if self.map_loaded: self.map_view.page().runJavaScript(f"focusOn('{drone_id}');") # ================================================================================ # 【新增】任務規劃視覺化方法 # ================================================================================ def draw_mission_plan(self, center_lat, center_lon, target_lat, target_lon): """ 在地圖上繪製任務規劃 Args: center_lat: 中心點緯度 center_lon: 中心點經度 target_lat: 目標點緯度 target_lon: 目標點經度 """ if self.map_loaded: js_code = f"drawMissionPlan({center_lat:.6f}, {center_lon:.6f}, {target_lat:.6f}, {target_lon:.6f});" self.map_view.page().runJavaScript(js_code) print(f"📍 地圖已繪製任務規劃: C({center_lat:.6f}, {center_lon:.6f}) -> T({target_lat:.6f}, {target_lon:.6f})") def clear_mission_plan(self): """清除地圖上的任務規劃標記""" if self.map_loaded: self.map_view.page().runJavaScript("clearMissionPlan();") print("🗑️ 地圖已清除任務規劃") # ================================================================================ def get_widget(self): """獲取地圖 widget""" return self.map_view def get_gps_signal(self): """獲取 GPS 信號""" return self.bridge.gps_signal def get_drone_clicked_signal(self): """獲取無人機點擊信號""" return self.bridge.drone_clicked class MapBridge(QObject): """JavaScript 和 Python 之間的橋接類""" gps_signal = pyqtSignal(float, float) # lat, lon drone_clicked = pyqtSignal(str) # drone_id def __init__(self): super().__init__() @pyqtSlot(float, float) def emitGpsSignal(self, lat, lon): """供 JavaScript 調用的方法""" self.gps_signal.emit(lat, lon) @pyqtSlot(str) def emitDroneClicked(self, drone_id): """供 JavaScript 調用的方法 - 當無人機被點擊時""" self.drone_clicked.emit(drone_id)