Add visualization picker to zone generation and unify tile colors
- Web map: multi-select picker in the generation bar (aspect, slope, positive openness, anisotropic openness, wavelet) passed to the API; the layer panel is restricted to the same shortlist (PANEL_VIZ) and a refresh button rebuilds the index when new layers appear on disk; jobs started outside the UI are now adopted into the visible queue - Uniform colors across tiles: openness/anisotropic/sailore now store local z-scores, and all renderers use fixed ranges (0-3 sigma, SVF 0-1 physical, slope 0-30 deg) instead of per-tile percentile stretches - Ray-tracing falls back to CPU when VRAM is exhausted so openness and SVF no longer fail silently on shared GPUs - build_index merges visualizations available at only one resolution into the displayed tile so in-progress layers stay visible - 11 new tests (142 passing)
This commit is contained in:
@ -291,7 +291,7 @@ def _prepare_dem_for_raycast(dem_file, shared, resolution):
|
||||
return dem, dem_np, rows, cols, res, nan_mask, transform, crs
|
||||
|
||||
|
||||
def _ray_trace_horizons(dem, rows, cols, res, n_dirs, max_dist, radii_m=None):
|
||||
def _ray_trace_horizons_core(dem, rows, cols, res, n_dirs, max_dist, radii_m=None):
|
||||
"""Core ray-tracing: compute max zenith/nadir angles per direction and radius.
|
||||
|
||||
For each pixel, in each direction, traces rays outward up to max_dist steps,
|
||||
@ -410,6 +410,25 @@ def _ray_trace_horizons(dem, rows, cols, res, n_dirs, max_dist, radii_m=None):
|
||||
return pos_angles, neg_angles
|
||||
|
||||
|
||||
def _ray_trace_horizons(dem, rows, cols, res, n_dirs, max_dist, radii_m=None):
|
||||
"""Ray-tracing avec repli CPU automatique si la VRAM est insuffisante.
|
||||
|
||||
Les dalles 0,2 m (5000×5000 px) multi-rayons peuvent dépasser la VRAM
|
||||
disponible (GPU partagé avec d'autres services) : plutôt que d'abandonner
|
||||
la visualisation, on désactive le GPU pour ce worker et on relance le
|
||||
calcul sur CPU.
|
||||
"""
|
||||
try:
|
||||
return _ray_trace_horizons_core(dem, rows, cols, res, n_dirs, max_dist, radii_m)
|
||||
except Exception as e:
|
||||
if _gpu_mod.is_gpu_active() and "out of memory" in str(e).lower():
|
||||
logger.warning(" ⚠ VRAM insuffisante (ray-tracing) — repli CPU pour ce worker")
|
||||
_gpu_mod.disable_gpu()
|
||||
gpu_cleanup()
|
||||
return _ray_trace_horizons_core(dem, rows, cols, res, n_dirs, max_dist, radii_m)
|
||||
raise
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Core terrain visualizations
|
||||
# ============================================================
|
||||
@ -629,11 +648,12 @@ def generate_openness(dem_file, basename, vis_dir, resolution, positive=True, sh
|
||||
openness_result = np.degrees(openness).astype(np.float32)
|
||||
openness_result[nan_mask] = np.nan
|
||||
|
||||
# Std normalization for cross-tile comparability
|
||||
# Z-score (écarts locaux en sigmas) : unités comparables entre tuiles,
|
||||
# plage de rendu fixe → mosaïque de couleur homogène
|
||||
valid = openness_result[~nan_mask]
|
||||
if len(valid) > 0:
|
||||
std_val = max(np.nanstd(valid), 0.01)
|
||||
openness_result = openness_result / std_val
|
||||
openness_result = (openness_result - np.nanmean(valid)) / std_val
|
||||
|
||||
_save_tif(output, openness_result, transform, crs)
|
||||
logger.info(f" ✓ {name} terminé ({time.time()-t0:.1f}s){' [GPU]' if _gpu_mod.HAS_GPU else ''}")
|
||||
@ -791,6 +811,13 @@ def generate_sailore(dem_file, basename, vis_dir, resolution, shared=None):
|
||||
sailore = (w_fine * lrm_fine + w_medium * lrm_medium + w_coarse * lrm_coarse) / w_total
|
||||
sailore[nan_mask] = np.nan
|
||||
|
||||
# Z-score (σ locales) : unités comparables entre tuiles, plage de
|
||||
# rendu fixe ±3σ → mosaïque de couleur homogène
|
||||
valid = sailore[~nan_mask]
|
||||
if len(valid) > 0:
|
||||
std_val = max(np.nanstd(valid), 0.01)
|
||||
sailore = (sailore - np.nanmean(valid)) / std_val
|
||||
|
||||
_save_tif(output, sailore.astype(np.float32), transform, crs)
|
||||
logger.info(f" ✓ SAILORE terminé ({time.time()-t0:.1f}s){' [GPU]' if _gpu_mod.HAS_GPU else ''}")
|
||||
return output
|
||||
@ -1248,11 +1275,12 @@ def generate_aniso_open(dem_file, basename, vis_dir, resolution, shared=None):
|
||||
aniso_result = np.degrees(pos_combined - neg_combined).astype(np.float32)
|
||||
aniso_result[nan_mask] = np.nan
|
||||
|
||||
# Std normalization for cross-tile comparability
|
||||
# Z-score (écarts locaux en sigmas) : unités comparables entre tuiles,
|
||||
# plage de rendu fixe → mosaïque de couleur homogène
|
||||
valid = aniso_result[~nan_mask]
|
||||
if len(valid) > 0:
|
||||
std_val = max(np.nanstd(valid), 0.01)
|
||||
aniso_result = aniso_result / std_val
|
||||
aniso_result = (aniso_result - np.nanmean(valid)) / std_val
|
||||
|
||||
_save_tif(output, aniso_result, transform, crs)
|
||||
logger.info(f" ✓ Openness anisotropique terminé ({time.time()-t0:.1f}s){' [GPU]' if _gpu_mod.HAS_GPU else ''}")
|
||||
|
||||
Reference in New Issue
Block a user