From b5f3faa14d2105b531ec22777301950741b949a7 Mon Sep 17 00:00:00 2001 From: Antoine Jacquin Date: Sun, 20 Sep 2026 00:45:45 +0200 Subject: [PATCH] Projeter les coins du bbox de zone (PROJ) au lieu d exiger un rectangle plein --- lidar_pipeline/index.py | 59 ++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/lidar_pipeline/index.py b/lidar_pipeline/index.py index 6d3564d..b8b6150 100644 --- a/lidar_pipeline/index.py +++ b/lidar_pipeline/index.py @@ -917,19 +917,44 @@ def _build_subtiles(tile, offered_viz_keys, output_dir, sub_dir_name): return usable +def _l93_points_to_wgs84_corners(xs, ys): + """Transforme des points L93 en [[lat, lon], …] (ordre conservé). + + rasterio.warp (PROJ exact) si disponible, sinon pyproj, sinon + l'approximation affine (webapp légère sans GDAL — précision ~km). + Même transformation que attach_gps_bounds : les coins produits + coïncident exactement avec ceux des dalles. + """ + try: + from rasterio.warp import transform as warp_transform + lons, lats = warp_transform('EPSG:2154', 'EPSG:4326', list(xs), list(ys)) + except ImportError: + try: + from pyproj import Transformer + tr = Transformer.from_crs('EPSG:2154', 'EPSG:4326', always_xy=True) + lons, lats = tr.transform(list(xs), list(ys)) + except ImportError: + pairs = [_approx_l93_to_wgs84(x, y) for x, y in zip(xs, ys)] + lons = [p[0] for p in pairs] + lats = [p[1] for p in pairs] + return [[la, lo] for la, lo in zip(lats, lons)] + + def _build_overview(zone_records, output_dir): """Assemble les mosaïques d'overview par couche pour la vue dézoomée. - Une image WebP par (zone géographique × couche), couvrant la grille L93 + Une image WebP par (zone géographique × couche), couvrant le bbox L93 des dalles de la zone (cases sans dalle transparentes), assemblée depuis les vignettes index_thumbs (256 px/km) : au dézoom l'interface l'affiche à la place des vignettes individuelles — 1 requête et ~30 Mo décodés par couche et par zone au lieu de centaines. Une mosaïque par zone : les grilles distantes (plusieurs secteurs, parfois à des centaines de km) - n'ont pas de coins partagés et surtout pas la même échelle utile. - Résolution plafonnée à 256 px/km et 3072 px de côté : l'overview n'est - affiché que tant qu'il reste au moins 1:1 avec l'écran, jamais étiré - donc jamais flou. + n'ont pas de coins partagés et surtout pas la même échelle utile. Les + coins du bbox sont projetés par la même transformation que les dalles — + la mosaïque s'emboîte exactement sur les vignettes, même si la zone + n'est pas un rectangle plein. Résolution plafonnée à 256 px/km et + 3072 px de côté : l'overview n'est affiché que tant qu'il reste au + moins 1:1 avec l'écran, jamais étiré donc jamais flou. Args: zone_records: enregistrements par zone ({'tiles': [dalle...]}) avec @@ -960,28 +985,18 @@ def _build_overview(zone_records, output_dir): max_col = max(t['col'] for t in have) min_row = min(t['row'] for t in have) max_row = max(t['row'] for t in have) - # Lattice exact des coins (km L93 → WGS84) : les coins partagés de - # dalles voisines proviennent de la même transformation pyproj → - # cohérents, la mosaïque s'emboîte exactement comme les vignettes. - lattice = {} - for t in have: - c = t['corners'] - lattice[(t['col'], t['row'])] = c[0] # SW - lattice[(t['col'] + 1, t['row'])] = c[1] # SE - lattice[(t['col'] + 1, t['row'] + 1)] = c[2] # NE - lattice[(t['col'], t['row'] + 1)] = c[3] # NW - quad = [(min_col, min_row), (max_col + 1, min_row), - (max_col + 1, max_row + 1), (min_col, max_row + 1)] - if any(k not in lattice for k in quad): - logger.debug(f"Overview : coins de grille absents pour " - f"{zone.get('label', '?')} — zone ignorée") - continue km_w = max_col + 1 - min_col km_h = max_row + 1 - min_row px_per_km = max(4, min(_OVERVIEW_PX_PER_KM, _OVERVIEW_MAX_PX // max(km_w, km_h))) w, h = km_w * px_per_km, km_h * px_per_km - corners = [lattice[k] for k in quad] + # Coins SW, SE, NE, NW du bbox L93 (dalle (col,row) : Y ∈ [(row−1), + # row] km — cf. attach_gps_bounds), projetés comme les coins de dalles. + xs = [min_col * 1000, (max_col + 1) * 1000, + (max_col + 1) * 1000, min_col * 1000] + ys = [(min_row - 1) * 1000, (min_row - 1) * 1000, + max_row * 1000, max_row * 1000] + corners = _l93_points_to_wgs84_corners(xs, ys) viz_keys = [] for t in have: for k in t['viz']: