|
|
|
|
@ -240,29 +240,63 @@ class ControlStationUI(QMainWindow):
|
|
|
|
|
<script>
|
|
|
|
|
var map = L.map('map').setView([0, 0], 16);
|
|
|
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
|
|
|
maxZoom: 19
|
|
|
|
|
maxZoom: 19
|
|
|
|
|
}).addTo(map);
|
|
|
|
|
|
|
|
|
|
var arrowIcon = L.icon({
|
|
|
|
|
iconUrl: 'https://cdn-icons-png.flaticon.com/512/399/399308.png', // 朝上的箭頭
|
|
|
|
|
iconSize: [40, 40],
|
|
|
|
|
iconAnchor: [20, 20]
|
|
|
|
|
iconUrl: 'https://cdn-icons-png.flaticon.com/512/399/399308.png',
|
|
|
|
|
iconSize: [40, 40],
|
|
|
|
|
iconAnchor: [20, 20]
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var markers = {};
|
|
|
|
|
var markers = {}; // 儲存所有無人機
|
|
|
|
|
var focusedId = null; // 目前被鎖定的 sysid
|
|
|
|
|
var initialized = false; // 是否完成首次初始化
|
|
|
|
|
|
|
|
|
|
function updateDrone(lat, lon, id, heading) {
|
|
|
|
|
if (markers[id]) {
|
|
|
|
|
markers[id].setLatLng([lat, lon]);
|
|
|
|
|
markers[id].setRotationAngle(heading);
|
|
|
|
|
} else {
|
|
|
|
|
markers[id] = L.marker([lat, lon], {
|
|
|
|
|
icon: arrowIcon,
|
|
|
|
|
rotationAngle: heading,
|
|
|
|
|
rotationOrigin: 'center'
|
|
|
|
|
}).addTo(map).bindPopup(id);
|
|
|
|
|
// ⬆️ 鎖定指定無人機(置中地圖並彈出名稱)
|
|
|
|
|
function focusOn(id) {
|
|
|
|
|
if (!markers[id]) return;
|
|
|
|
|
focusedId = id;
|
|
|
|
|
var latlng = markers[id].getLatLng();
|
|
|
|
|
map.flyTo(latlng, map.getZoom());
|
|
|
|
|
markers[id].openPopup();
|
|
|
|
|
}
|
|
|
|
|
map.setView([lat, lon]);
|
|
|
|
|
|
|
|
|
|
// 🔁 定期重新鎖定 focusedId
|
|
|
|
|
setInterval(() => {
|
|
|
|
|
if (focusedId && markers[focusedId]) {
|
|
|
|
|
var latlng = markers[focusedId].getLatLng();
|
|
|
|
|
map.panTo(latlng); // 或 flyTo / setView
|
|
|
|
|
}
|
|
|
|
|
}, 1000); // 每秒更新一次視角
|
|
|
|
|
|
|
|
|
|
// 📡 更新/新增無人機 marker
|
|
|
|
|
function updateDrone(lat, lon, id, heading) {
|
|
|
|
|
if (markers[id]) {
|
|
|
|
|
markers[id]
|
|
|
|
|
.setLatLng([lat, lon])
|
|
|
|
|
.setRotationAngle(heading);
|
|
|
|
|
} else {
|
|
|
|
|
markers[id] = L.marker([lat, lon], {
|
|
|
|
|
icon: arrowIcon,
|
|
|
|
|
rotationAngle: heading,
|
|
|
|
|
rotationOrigin: 'center'
|
|
|
|
|
})
|
|
|
|
|
.addTo(map)
|
|
|
|
|
.bindPopup("UAV #" + id)
|
|
|
|
|
.on('click', function () {
|
|
|
|
|
focusOn(id); // 使用者點選無人機 → 鎖定
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 🧭 第一次加入 → 若未初始化,則以 sysid 最小的初始化
|
|
|
|
|
if (!initialized || id < focusedId) {
|
|
|
|
|
focusOn(id);
|
|
|
|
|
markers[id]
|
|
|
|
|
.setLatLng([lat, lon])
|
|
|
|
|
.setRotationAngle(heading);
|
|
|
|
|
initialized = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
</body>
|
|
|
|
|
@ -421,12 +455,29 @@ class ControlStationUI(QMainWindow):
|
|
|
|
|
label.setText(text)
|
|
|
|
|
if color:
|
|
|
|
|
label.setStyleSheet(f"color: {color};")
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
def add_drone(self, drone_id):
|
|
|
|
|
if drone_id not in self.drones:
|
|
|
|
|
panel = self.create_drone_panel(drone_id)
|
|
|
|
|
self.info_layout.addWidget(panel)
|
|
|
|
|
self.drones[drone_id] = panel
|
|
|
|
|
'''
|
|
|
|
|
def add_drone(self, drone_id):
|
|
|
|
|
if drone_id not in self.drones:
|
|
|
|
|
panel = self.create_drone_panel(drone_id)
|
|
|
|
|
self.info_layout.addWidget(panel)
|
|
|
|
|
self.drones[drone_id] = panel
|
|
|
|
|
|
|
|
|
|
# 清空原有 layout
|
|
|
|
|
for i in reversed(range(self.info_layout.count())):
|
|
|
|
|
widget = self.info_layout.itemAt(i).widget()
|
|
|
|
|
if widget:
|
|
|
|
|
self.info_layout.removeWidget(widget)
|
|
|
|
|
widget.setParent(None)
|
|
|
|
|
|
|
|
|
|
# 根據 key 排序後重新加入
|
|
|
|
|
for sorted_id in sorted(self.drones, key=lambda x: int(''.join(filter(str.isdigit, x)) or 0)):
|
|
|
|
|
self.info_layout.addWidget(self.drones[sorted_id])
|
|
|
|
|
|
|
|
|
|
def spin_ros(self):
|
|
|
|
|
try:
|
|
|
|
|
|