diff --git a/lidar_pipeline/dtm.py b/lidar_pipeline/dtm.py index 82b5e98..c298955 100644 --- a/lidar_pipeline/dtm.py +++ b/lidar_pipeline/dtm.py @@ -285,10 +285,25 @@ def create_dtm_fast(las_file, basename, dtm_dir, resolution): dtm = stat.statistic.T dtm = dtm[::-1, :] # Flip Y so north is at top - # No interpolation: keep NaN for zones without LiDAR data + # Interpolate NaN gaps using distance-weighted nearest-neighbor fill nan_count = np.count_nonzero(np.isnan(dtm)) if nan_count > 0: - logger.info(f" {nan_count:,} pixels sans données (conservés en NaN)") + total = dtm.size + nan_pct = 100.0 * nan_count / total + logger.info(f" {nan_count:,} pixels sans données ({nan_pct:.1f}%) — interpolation...") + + from rasterio.fill import fillnodata + # rasterio.fill.fillnodata uses GDAL's interpolation: + # fills gaps from surrounding valid pixels with distance weighting + dtm_filled = fillnodata(dtm.astype(np.float32), mask=~np.isnan(dtm), + max_search_distance=max(width, height) // 4) + dtm = dtm_filled.astype(np.float64) + + remaining = np.count_nonzero(np.isnan(dtm)) + if remaining > 0: + logger.warning(f" {remaining:,} pixels encore sans données après interpolation") + else: + logger.info(f" ✓ Interpolation terminée — tous les trous comblés") # Save as GeoTIFF output_tif = dtm_dir / f"{basename}_dtm.tif" diff --git a/lidar_pipeline/rendering.py b/lidar_pipeline/rendering.py index 5aadc48..eaae0c0 100644 --- a/lidar_pipeline/rendering.py +++ b/lidar_pipeline/rendering.py @@ -324,8 +324,10 @@ def tif_to_png(tif_file, vis_dir, resolution): # Apply colormap data, cmap, title, legend_label, description, is_rgb_result = _apply_colormap(data, tif_file) - # Create figure - fig_width = 20 + # Create figure — adapt width to data resolution for sharp rendering + # At high res (5000+px wide), we need a larger figure to avoid downsampling artifacts + fig_width = max(20, width / 150) + fig_width = min(fig_width, 40) # cap at 40 inches map_aspect = height / width fig = plt.figure(figsize=(fig_width, fig_width * map_aspect * 0.7 + 2.5), facecolor='white') @@ -335,9 +337,11 @@ def tif_to_png(tif_file, vis_dir, resolution): ax = fig.add_subplot(gs[0]) if is_rgb: - im = ax.imshow(data, aspect='equal', origin='upper') + im = ax.imshow(data, aspect='equal', origin='upper', + interpolation='bilinear') else: - im = ax.imshow(data, cmap=cmap, aspect='equal', origin='upper') + im = ax.imshow(data, cmap=cmap, aspect='equal', origin='upper', + interpolation='bilinear') ax.set_title(f"{title}\n{description}", fontsize=15, fontweight='bold', pad=10) @@ -450,9 +454,10 @@ def tif_to_png(tif_file, vis_dir, resolution): fig.patch.set_facecolor('white') - # Save as PNG then convert to WebP + # Save as PNG then convert to WebP — use higher DPI for large data + save_dpi = 200 if width > 3000 else 150 png_temp = vis_dir / f"{tif_file.stem}_temp.png" - plt.savefig(png_temp, dpi=150, bbox_inches='tight', pad_inches=0.15, + plt.savefig(png_temp, dpi=save_dpi, bbox_inches='tight', pad_inches=0.15, facecolor='white', format='png') plt.close()