diff --git a/lidar_pipeline/rendering.py b/lidar_pipeline/rendering.py index 86c07b8..b27146a 100644 --- a/lidar_pipeline/rendering.py +++ b/lidar_pipeline/rendering.py @@ -189,7 +189,8 @@ for _key, _info in RGB_LEGENDS.items(): def _apply_colormap(data, tif_file, resolution=None): """Apply the registered colormap normalization to data based on filename. - Returns (data, cmap, title, legend_label, description, is_rgb). + Returns (data, cmap, title, legend_label, description, is_rgb, vmin, vmax) + où vmin/vmax sont les bornes physiques de la plage rendue (None si sans objet). """ name = str(tif_file).lower() @@ -197,7 +198,7 @@ def _apply_colormap(data, tif_file, resolution=None): for key in RGB_LEGENDS: if key in name: info = RGB_LEGENDS[key] - return data, None, info['title'], info['legend'], info['description'], True + return data, None, info['title'], info['legend'], info['description'], True, None, None # Find matching colormap — sort by key length descending so 'mslrm' matches before 'lrm' for key in sorted(COLORMAPS.keys(), key=len, reverse=True): @@ -208,7 +209,7 @@ def _apply_colormap(data, tif_file, resolution=None): if len(valid_data) == 0: logger.warning(f" Aucune donnée valide pour {Path(tif_file).name} — colormap ignorée") - return data, 'terrain', Path(tif_file).stem.replace('_', ' ').title(), '', '', False + return data, 'terrain', Path(tif_file).stem.replace('_', ' ').title(), '', '', False, None, None vmin = vmax = None @@ -253,7 +254,7 @@ def _apply_colormap(data, tif_file, resolution=None): data = np.clip((data - vmin) / max(vmax - vmin, 0.001), 0, 1) legend = info['legend'].format(vmin=vmin or 0, vmax=vmax or 0) - return data, info['cmap'], info['title'], legend, info['description'], False + return data, info['cmap'], info['title'], legend, info['description'], False, vmin, vmax # Default: terrain colormap with percentile stretch valid_data = np.asarray(data.compressed() if hasattr(data, 'compressed') else data.flatten()) @@ -265,7 +266,7 @@ def _apply_colormap(data, tif_file, resolution=None): span = max(p98 - p2, 1e-6) data = np.clip((data - p2) / span, 0, 1) title = Path(tif_file).stem.replace('_', ' ').title() - return data, 'terrain', title, 'Altitude normalisée', '', False + return data, 'terrain', title, 'Altitude normalisée', '', False, p2, p98 def _download_location_map(min_x, max_x, min_y, max_y): @@ -453,7 +454,7 @@ def tif_to_png(tif_file, vis_dir, resolution, keep_tif=False, source_info=None, nan_mask = nan_mask # keep for later # Apply colormap - data, cmap, title, legend_label, description, is_rgb_result = _apply_colormap(data, tif_file, resolution=resolution) + data, cmap, title, legend_label, description, is_rgb_result, cmap_vmin, cmap_vmax = _apply_colormap(data, tif_file, resolution=resolution) # Apply NaN mask: make zones without data transparent has_nan_mask = nan_mask is not None and not is_rgb_result @@ -461,8 +462,12 @@ def tif_to_png(tif_file, vis_dir, resolution, keep_tif=False, source_info=None, # data is normalized 0-1 from _apply_colormap; apply cmap to get RGBA # Save the colormap for colorbar before converting to RGBA saved_cmap = plt.get_cmap(cmap) if isinstance(cmap, str) else cmap - saved_vmin = float(np.nanmin(data)) if not nan_mask.all() else 0 - saved_vmax = float(np.nanmax(data)) if not nan_mask.all() else 1 + # Bornes physiques de la colorbar (unités réelles, pas 0-1) + if cmap_vmin is not None and cmap_vmax is not None: + saved_vmin, saved_vmax = float(cmap_vmin), float(cmap_vmax) + else: + saved_vmin = float(np.nanmin(data)) if not nan_mask.all() else 0 + saved_vmax = float(np.nanmax(data)) if not nan_mask.all() else 1 rgba = saved_cmap(data) # (H, W, 4) float RGBA rgba[nan_mask, 3] = 0.0 # transparent where no data data = rgba @@ -806,9 +811,14 @@ def tif_to_crop(tif_file, vis_dir, resolution, keep_tif=False, quality=98, outpu data = np.moveaxis(data, 0, -1) else: data = src.read(1) + # Nodata → NaN : sinon les pixels bord (ex. -9999, 3.4e38) + # polluent les percentiles d'étalonnage de la colormap + if src.nodata is not None: + data = data.astype(np.float32, copy=False) + data[data == src.nodata] = np.nan # Apply colormap normalization - data, cmap_name, title, legend_label, description, is_rgb_result = _apply_colormap(data, tif_file, resolution=resolution) + data, cmap_name, title, legend_label, description, is_rgb_result, _cvmin, _cvmax = _apply_colormap(data, tif_file, resolution=resolution) if not is_rgb_result: data = np.where(np.isnan(data), 0.0, data)