# visualizer.py import logging import os from typing import Optional, Union # Rimuovi import cv2 se non serve più ad altro # try: # import cv2 # import numpy as np # OpenCV richiede numpy # OPENCV_AVAILABLE = True # except ImportError: # OPENCV_AVAILABLE = False # class np: pass # logging.warning("OpenCV (cv2) or NumPy not found. OpenCV visualization disabled.") try: import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D # Per 3D import numpy as np # Matplotlib richiede numpy MATPLOTLIB_AVAILABLE = True except ImportError: MATPLOTLIB_AVAILABLE = False class np: pass class plt: # Dummy class def figure(self, figsize): return self def add_subplot(self, *args, **kwargs): return self def plot_surface(self, *args, **kwargs): return self def set_xlabel(self, *args): pass def set_ylabel(self, *args): pass def set_zlabel(self, *args): pass def set_title(self, *args): pass def set_zlim(self, *args): pass def colorbar(self, *args, **kwargs): pass def show(self): pass def subplots(self): return self, self # Dummy fig, ax def imshow(self, *args): pass def axis(self, *args): pass logging.warning("Matplotlib not found. 2D/3D plot visualization will be disabled.") try: from PIL import Image PIL_AVAILABLE_VIS = True except ImportError: PIL_AVAILABLE_VIS = False class Image: pass # Dummy # === RIMOSSA: show_image_cv2 === # === NUOVA FUNZIONE: show_image_matplotlib === def show_image_matplotlib( image_source: Union[str, np.ndarray, Image.Image], title: str = "Image Preview" ): """ Displays an image (from path, NumPy array, or PIL Image) in a separate Matplotlib window with interactive zoom/pan. Requires Matplotlib and NumPy. PIL is needed if input is PIL Image or path. """ if not MATPLOTLIB_AVAILABLE: logging.error("Cannot display image: Matplotlib not available.") return if not PIL_AVAILABLE_VIS and isinstance(image_source, (str, Image.Image)): logging.error( "Cannot display image: Pillow (PIL) not available for loading/conversion." ) return img_display_np = None try: if isinstance(image_source, str): # Path if not os.path.exists(image_source): logging.error( f"Matplotlib display: Image path not found: {image_source}" ) return with Image.open(image_source) as img_pil: # Converti in NumPy array (Matplotlib preferisce NumPy) img_display_np = np.array(img_pil) elif isinstance(image_source, np.ndarray): # NumPy array img_display_np = ( image_source.copy() ) # Usa direttamente (o copia per sicurezza) elif PIL_AVAILABLE_VIS and isinstance(image_source, Image.Image): # PIL Image img_display_np = np.array(image_source) else: logging.error( f"Matplotlib display: Unsupported image source type: {type(image_source)}" ) return if img_display_np is None: logging.error("Failed to load or convert image to NumPy array.") return # Crea figura e asse fig, ax = plt.subplots(figsize=(8, 8)) # Puoi aggiustare figsize ax.imshow(img_display_np) # Abbellimenti opzionali ax.set_title(title) ax.axis("off") # Nasconde assi numerici e tick # Mostra la finestra interattiva (BLOCCANTE) # La GUI chiamante deve gestire l'esecuzione in un thread. plt.show() except Exception as e: logging.error( f"Error displaying image with Matplotlib ('{title}'): {e}", exc_info=True ) # === Funzione show_3d_matplotlib rimane invariata === def show_3d_matplotlib( hgt_array: Optional[np.ndarray], title: str = "3D Elevation View", subsample: int = 5, ): """Displays HGT data as a 3D surface plot using Matplotlib.""" if not MATPLOTLIB_AVAILABLE: logging.error("Cannot display 3D plot: Matplotlib not available.") return if hgt_array is None or hgt_array.size == 0: logging.error("Cannot display 3D view: Input data array is None or empty.") return try: # ... (implementazione 3D come prima) ... logging.info( f"Generating 3D plot for data shape: {hgt_array.shape} with subsampling={subsample}" ) rows, cols = hgt_array.shape x = np.arange(0, cols, subsample) y = np.arange(0, rows, subsample) X, Y = np.meshgrid(x, y) Z = hgt_array[::subsample, ::subsample].astype(float) common_nodata = -32768 Z[Z == common_nodata] = np.nan fig = plt.figure(figsize=(10, 7)) ax = fig.add_subplot(111, projection="3d") z_min, z_max = np.nanmin(Z), np.nanmax(Z) if np.isnan(z_min) or np.isnan(z_max): z_min, z_max = 0, 100 # Fallback surf = ax.plot_surface( X, Y, Z, cmap="terrain", linewidth=0, antialiased=False, vmin=z_min, vmax=z_max, ) ax.set_xlabel("Pixel Col Index (Subsampled)") ax.set_ylabel("Pixel Row Index (Subsampled)") ax.set_zlabel("Elevation (m)") ax.set_title(title) ax.set_zlim(z_min - (z_max - z_min) * 0.1, z_max + (z_max - z_min) * 0.1) fig.colorbar(surf, shrink=0.6, aspect=10, label="Elevation (m)") plt.show() # BLOCCANTE except Exception as e: logging.error(f"Error generating 3D plot ('{title}'): {e}", exc_info=True)