angolo di rotazione opposto
This commit is contained in:
parent
99befa3214
commit
2dd7c4715c
42
app.py
42
app.py
@ -19,6 +19,7 @@ import math
|
||||
import sys
|
||||
import socket # Required for network setup
|
||||
from typing import Optional, Tuple, Any, Dict, TYPE_CHECKING
|
||||
import datetime
|
||||
|
||||
# --- Third-party imports ---
|
||||
import tkinter as tk
|
||||
@ -50,7 +51,7 @@ from ui import ControlPanel, StatusBar, create_main_window
|
||||
# image_processing functions are used by other modules, App uses ImagePipeline now
|
||||
# from image_processing import ...
|
||||
from display import DisplayManager
|
||||
from utils import put_queue, clear_queue, decimal_to_dms
|
||||
from utils import put_queue, clear_queue, decimal_to_dms, generate_sar_kml, launch_google_earth
|
||||
from network import create_udp_socket, close_udp_socket
|
||||
from receiver import UdpReceiver
|
||||
from app_state import AppState # Centralized state
|
||||
@ -1128,6 +1129,7 @@ class App:
|
||||
|
||||
# 3. Trigger Map Update (if map manager exists and geo is valid)
|
||||
geo_info = self.state.current_sar_geo_info # Get current info
|
||||
is_geo_valid = geo_info and geo_info.get("valid", False)
|
||||
# Check if map manager was initialized successfully
|
||||
map_manager_active = hasattr(self, 'map_integration_manager') and self.map_integration_manager is not None
|
||||
if map_manager_active and geo_info and geo_info.get("valid", False):
|
||||
@ -1145,6 +1147,42 @@ class App:
|
||||
logging.debug(f"{log_prefix} Skipping map update: MapIntegrationManager not available.")
|
||||
elif not geo_info or not geo_info.get("valid", False):
|
||||
logging.debug(f"{log_prefix} Skipping map update: GeoInfo not valid.")
|
||||
|
||||
if is_geo_valid and config.ENABLE_KML_GENERATION:
|
||||
kml_log_prefix = "[App KML]"
|
||||
logging.debug(f"{kml_log_prefix} KML generation enabled. Proceeding...")
|
||||
try:
|
||||
# Assicurati che la cartella di output esista
|
||||
kml_dir = config.KML_OUTPUT_DIRECTORY
|
||||
os.makedirs(kml_dir, exist_ok=True)
|
||||
|
||||
# Crea un nome file univoco (es. basato su timestamp)
|
||||
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S_%f")
|
||||
kml_filename = f"sar_footprint_{timestamp}.kml"
|
||||
kml_output_path = os.path.join(kml_dir, kml_filename)
|
||||
|
||||
# Genera il KML
|
||||
logging.debug(f"{kml_log_prefix} Calling generate_sar_kml for path: {kml_output_path}")
|
||||
kml_success = generate_sar_kml(geo_info, kml_output_path) # Passa geo_info
|
||||
|
||||
if kml_success:
|
||||
logging.info(f"{kml_log_prefix} KML file generated successfully: {kml_output_path}")
|
||||
# Lancia Google Earth se richiesto
|
||||
if config.AUTO_LAUNCH_GOOGLE_EARTH:
|
||||
logging.debug(f"{kml_log_prefix} Auto-launch Google Earth enabled. Calling launch function...")
|
||||
launch_google_earth(kml_output_path)
|
||||
else:
|
||||
logging.debug(f"{kml_log_prefix} Auto-launch Google Earth disabled.")
|
||||
else:
|
||||
logging.error(f"{kml_log_prefix} KML file generation failed.")
|
||||
|
||||
except ImportError as ie:
|
||||
# Logga se manca una libreria necessaria per KML
|
||||
logging.error(f"{kml_log_prefix} Cannot generate KML due to missing library: {ie}")
|
||||
except Exception as e:
|
||||
logging.exception(f"{kml_log_prefix} Error during KML generation/launch process:")
|
||||
elif is_geo_valid and not config.ENABLE_KML_GENERATION:
|
||||
logging.debug(f"{log_prefix} KML generation disabled in config.")
|
||||
|
||||
# 4. Update FPS Statistics for SAR
|
||||
self._update_fps_stats("sar") # Updates self.state counters
|
||||
@ -1794,6 +1832,8 @@ class App:
|
||||
ref_lat_rad = geo["lat"] # radians
|
||||
ref_lon_rad = geo["lon"] # radians
|
||||
orient_rad = geo.get("orientation", 0.0) # radians (default to 0 if missing)
|
||||
|
||||
orient_rad = -orient_rad #inverse angle
|
||||
|
||||
# --- Coordinate Transformation ---
|
||||
# 1. Normalize display coordinates to [0, 1] range
|
||||
|
||||
@ -171,5 +171,12 @@ SAR_IMAGE_SIZE_KM = (
|
||||
50.0 # Example: Width/Height of the area to show on the map in Kilometers
|
||||
)
|
||||
|
||||
# --- KML / Google Earth Integration Configuration ---
|
||||
ENABLE_KML_GENERATION = True # Imposta a True per generare file KML quando arrivano dati SAR validi
|
||||
KML_OUTPUT_DIRECTORY = "kml_output" # Cartella dove salvare i file KML generati
|
||||
AUTO_LAUNCH_GOOGLE_EARTH = False # Imposta a True per tentare di aprire automaticamente il KML generato con Google Earth Pro (se installato)
|
||||
# Opzionale: potresti aggiungere un percorso esplicito all'eseguibile di Google Earth se non è nel PATH
|
||||
# GOOGLE_EARTH_EXECUTABLE_PATH = "C:/Program Files/Google/Google Earth Pro/client/googleearth.exe" # Esempio Windows
|
||||
|
||||
|
||||
# --- END OF FILE config.py ---
|
||||
|
||||
@ -124,6 +124,8 @@ class ImagePipeline:
|
||||
# --- Processing Steps ---
|
||||
is_geo_valid = geo_info.get("valid", False) if geo_info else False
|
||||
orient_rad = geo_info.get("orientation", 0.0) if is_geo_valid else 0.0
|
||||
|
||||
orient_rad = -orient_rad # invert angle
|
||||
|
||||
# 1. Apply B/C LUT (from state)
|
||||
logging.debug(f"{log_prefix} Applying B/C LUT...")
|
||||
|
||||
205
utils.py
205
utils.py
@ -13,6 +13,28 @@ Uses standardized logging prefixes. Drop counts are now managed within AppState.
|
||||
import queue
|
||||
import logging
|
||||
import math
|
||||
import os # Aggiunto
|
||||
import datetime # Aggiunto per timestamp
|
||||
import sys # Aggiunto per platform check
|
||||
import subprocess # Aggiunto per lanciare processi
|
||||
import shutil # Aggiunto per trovare eseguibili (opzionale)
|
||||
|
||||
# Importa le librerie KML e GEO, gestendo l'ImportError
|
||||
try:
|
||||
import simplekml
|
||||
_simplekml_available = True
|
||||
except ImportError:
|
||||
simplekml = None
|
||||
_simplekml_available = False
|
||||
logging.warning("[Utils KML] Library 'simplekml' not found. KML generation disabled. (pip install simplekml)")
|
||||
|
||||
try:
|
||||
import pyproj
|
||||
_pyproj_available = True
|
||||
except ImportError:
|
||||
pyproj = None
|
||||
_pyproj_available = False
|
||||
logging.warning("[Utils KML] Library 'pyproj' not found. KML generation requires it for corner calculation. (pip install pyproj)")
|
||||
|
||||
# Removed: threading (Lock is now in AppState)
|
||||
|
||||
@ -194,5 +216,188 @@ def decimal_to_dms(decimal_degrees, is_latitude):
|
||||
)
|
||||
return "Error DMS" # Return specific error string
|
||||
|
||||
def _calculate_geo_corners_for_kml(geo_info_radians):
|
||||
"""
|
||||
Helper interno per calcolare i corner geografici (gradi) da geo_info (radianti).
|
||||
Basato sulla logica di MapIntegrationManager._calculate_sar_corners_geo.
|
||||
Richiede pyproj.
|
||||
Restituisce lista di tuple (lon, lat) in gradi o None.
|
||||
"""
|
||||
if not _pyproj_available: return None
|
||||
log_prefix = "[Utils KML Calc]"
|
||||
try:
|
||||
geod = pyproj.Geod(ellps="WGS84")
|
||||
# Estrai dati necessari (gestisci KeyError)
|
||||
center_lat_rad = geo_info_radians['lat']
|
||||
center_lon_rad = geo_info_radians['lon']
|
||||
orient_rad = geo_info_radians['orientation']
|
||||
ref_x = geo_info_radians['ref_x']
|
||||
ref_y = geo_info_radians['ref_y']
|
||||
scale_x = geo_info_radians['scale_x']
|
||||
scale_y = geo_info_radians['scale_y']
|
||||
width = geo_info_radians['width_px']
|
||||
height = geo_info_radians['height_px']
|
||||
|
||||
orient_rad = -orient_rad #inverse angle
|
||||
|
||||
if not (scale_x > 0 and scale_y > 0 and width > 0 and height > 0):
|
||||
logging.error(f"{log_prefix} Invalid scale/dimensions in geo_info.")
|
||||
return None
|
||||
|
||||
# Calcola offset pixel e metri
|
||||
corners_pixel = [
|
||||
(0 - ref_x, ref_y - 0),
|
||||
(width - 1 - ref_x, ref_y - 0),
|
||||
(width - 1 - ref_x, ref_y - (height - 1)),
|
||||
(0 - ref_x, ref_y - (height - 1))
|
||||
]
|
||||
corners_meters = [(dx * scale_x, dy * scale_y) for dx, dy in corners_pixel]
|
||||
|
||||
# Applica rotazione
|
||||
corners_meters_rotated = []
|
||||
if abs(orient_rad) > 1e-6:
|
||||
cos_o = math.cos(orient_rad)
|
||||
sin_o = math.sin(orient_rad)
|
||||
for dx_m, dy_m in corners_meters:
|
||||
rot_dx = dx_m * cos_o - dy_m * sin_o
|
||||
rot_dy = dx_m * sin_o + dy_m * cos_o
|
||||
corners_meters_rotated.append((rot_dx, rot_dy))
|
||||
else:
|
||||
corners_meters_rotated = corners_meters
|
||||
|
||||
# Calcola coordinate geografiche finali
|
||||
sar_corners_geo_deg = []
|
||||
center_lon_deg = math.degrees(center_lon_rad)
|
||||
center_lat_deg = math.degrees(center_lat_rad)
|
||||
for dx_m_rot, dy_m_rot in corners_meters_rotated:
|
||||
distance_m = math.sqrt(dx_m_rot**2 + dy_m_rot**2)
|
||||
azimuth_rad = math.atan2(dx_m_rot, dy_m_rot)
|
||||
azimuth_deg = math.degrees(azimuth_rad)
|
||||
endlon, endlat, _ = geod.fwd(center_lon_deg, center_lat_deg, azimuth_deg, distance_m)
|
||||
sar_corners_geo_deg.append((endlon, endlat)) # (lon, lat)
|
||||
|
||||
if len(sar_corners_geo_deg) == 4:
|
||||
return sar_corners_geo_deg
|
||||
else:
|
||||
logging.error(f"{log_prefix} Failed to calculate all 4 corner coordinates.")
|
||||
return None
|
||||
|
||||
except KeyError as ke:
|
||||
logging.error(f"{log_prefix} Missing required key in geo_info_radians: {ke}")
|
||||
return None
|
||||
except Exception as e:
|
||||
logging.exception(f"{log_prefix} Error calculating geographic corners for KML:")
|
||||
return None
|
||||
|
||||
|
||||
def generate_sar_kml(geo_info_radians, output_path) -> bool:
|
||||
"""
|
||||
Genera un file KML rappresentante l'area SAR.
|
||||
|
||||
Args:
|
||||
geo_info_radians (dict): Dizionario GeoInfo con valori in radianti.
|
||||
output_path (str): Percorso completo dove salvare il file .kml.
|
||||
|
||||
Returns:
|
||||
bool: True se il KML è stato generato e salvato, False altrimenti.
|
||||
"""
|
||||
log_prefix = "[Utils KML Gen]"
|
||||
if not _simplekml_available or not _pyproj_available:
|
||||
logging.error(f"{log_prefix} Cannot generate KML: simplekml or pyproj library missing.")
|
||||
return False
|
||||
if not geo_info_radians or not geo_info_radians.get("valid", False):
|
||||
logging.warning(f"{log_prefix} Cannot generate KML: Invalid or missing GeoInfo.")
|
||||
return False
|
||||
|
||||
try:
|
||||
# Calcola i corner in gradi
|
||||
corners_deg = _calculate_geo_corners_for_kml(geo_info_radians)
|
||||
if corners_deg is None:
|
||||
logging.error(f"{log_prefix} Failed to calculate SAR corners for KML.")
|
||||
return False # Errore già loggato nella funzione helper
|
||||
|
||||
# Estrai centro e orientamento (converti in gradi per KML)
|
||||
center_lon_deg = math.degrees(geo_info_radians['lon'])
|
||||
center_lat_deg = math.degrees(geo_info_radians['lat'])
|
||||
orientation_deg = math.degrees(geo_info_radians['orientation']) # KML usa gradi
|
||||
|
||||
# Calcola dimensione approssimativa per l'altitudine della vista
|
||||
width_km = (geo_info_radians.get('scale_x', 1) * geo_info_radians.get('width_px', 1)) / 1000.0
|
||||
height_km = (geo_info_radians.get('scale_y', 1) * geo_info_radians.get('height_px', 1)) / 1000.0
|
||||
view_altitude_m = max(width_km, height_km) * 2000 # Altitudine vista = 2 * dimensione max in metri
|
||||
|
||||
# Crea oggetto KML
|
||||
kml = simplekml.Kml(name=f"SAR Image {datetime.datetime.now():%Y%m%d_%H%M%S}")
|
||||
|
||||
# Aggiungi LookAt per centrare la vista
|
||||
kml.document.lookat.longitude = center_lon_deg
|
||||
kml.document.lookat.latitude = center_lat_deg
|
||||
kml.document.lookat.range = view_altitude_m # Distanza in metri dalla coordinata
|
||||
kml.document.lookat.tilt = 45 # Angolo di vista (0=diretto verso il basso)
|
||||
kml.document.lookat.heading = orientation_deg # Orientamento della camera (0=Nord)
|
||||
|
||||
# Aggiungi un segnaposto al centro
|
||||
# placemark = kml.newpoint(name="SAR Center", coords=[(center_lon_deg, center_lat_deg)])
|
||||
|
||||
# Aggiungi poligono per il footprint SAR
|
||||
# Nota: simplekml si aspetta [(lon,lat,alt), (lon,lat,alt), ...]
|
||||
# L'altitudine è opzionale, la mettiamo a 0 rispetto al suolo.
|
||||
outer_boundary = [(lon, lat, 0) for lon, lat in corners_deg]
|
||||
pol = kml.newpolygon(name="SAR Footprint", outerboundaryis=outer_boundary)
|
||||
pol.style.linestyle.color = simplekml.Color.red # Colore linea
|
||||
pol.style.linestyle.width = 2 # Spessore linea
|
||||
pol.style.polystyle.color = simplekml.Color.changealphaint(100, simplekml.Color.red) # Rosso semi-trasparente per riempimento
|
||||
|
||||
# Salva il file KML
|
||||
logging.debug(f"{log_prefix} Saving KML to: {output_path}")
|
||||
kml.save(output_path)
|
||||
logging.info(f"{log_prefix} KML file saved successfully: {output_path}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logging.exception(f"{log_prefix} Error generating or saving KML file:")
|
||||
return False
|
||||
|
||||
|
||||
def launch_google_earth(kml_path):
|
||||
"""
|
||||
Tenta di aprire un file KML con l'applicazione predefinita del sistema
|
||||
(che dovrebbe essere Google Earth Pro se installato correttamente).
|
||||
|
||||
Args:
|
||||
kml_path (str): Percorso del file KML da aprire.
|
||||
"""
|
||||
log_prefix = "[Utils Launch GE]"
|
||||
if not os.path.exists(kml_path):
|
||||
logging.error(f"{log_prefix} Cannot launch: KML file not found at {kml_path}")
|
||||
return
|
||||
|
||||
logging.info(f"{log_prefix} Attempting to launch default KML handler for: {kml_path}")
|
||||
try:
|
||||
if sys.platform == "win32":
|
||||
os.startfile(kml_path) # Metodo standard Windows per aprire un file con l'app associata
|
||||
elif sys.platform == "darwin": # macOS
|
||||
subprocess.run(['open', kml_path], check=True)
|
||||
else: # Linux e altri Unix-like
|
||||
# Tenta di trovare google-earth-pro nel PATH
|
||||
google_earth_cmd = shutil.which('google-earth-pro')
|
||||
if google_earth_cmd:
|
||||
subprocess.Popen([google_earth_cmd, kml_path])
|
||||
logging.debug(f"{log_prefix} Launched using found command: {google_earth_cmd}")
|
||||
else:
|
||||
# Fallback: usa xdg-open che usa l'associazione MIME
|
||||
logging.debug(f"{log_prefix} 'google-earth-pro' not in PATH, using 'xdg-open'...")
|
||||
subprocess.run(['xdg-open', kml_path], check=True)
|
||||
|
||||
logging.info(f"{log_prefix} Launch command issued for {kml_path}.")
|
||||
|
||||
except FileNotFoundError:
|
||||
# Questo può accadere su Linux se né google-earth-pro né xdg-open sono trovati
|
||||
logging.error(f"{log_prefix} Launch command failed: Command not found (is Google Earth Pro installed and in PATH, or xdg-utils installed?)")
|
||||
except subprocess.CalledProcessError as e:
|
||||
# Errore da 'open' o 'xdg-open'
|
||||
logging.error(f"{log_prefix} Error launching KML handler: {e}")
|
||||
except Exception as e:
|
||||
logging.exception(f"{log_prefix} Unexpected error launching Google Earth:")
|
||||
|
||||
# --- END OF FILE utils.py ---
|
||||
|
||||
Loading…
Reference in New Issue
Block a user