SXXXXXXX_GeoElevation/visualizer.py
VALLONGOL 2661f5bc42 add 2D and 3D view for tile
add 2D view for tile mosaik
2025-04-14 13:20:07 +02:00

199 lines
5.9 KiB
Python

# 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)