From 1d722ff10c945402de47758b16db707412fd4e0a Mon Sep 17 00:00:00 2001 From: Antoine Jacquin Date: Sun, 20 Sep 2026 01:05:08 +0200 Subject: [PATCH] =?UTF-8?q?Ne=20pas=20g=C3=A9n=C3=A9rer=20d=20overview=20l?= =?UTF-8?q?ocale=20sur=20la=20webapp=20l=C3=A9g=C3=A8re=20(g=C3=A9om=C3=A9?= =?UTF-8?q?trie=20divergente=20du=20worker)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lidar_pipeline/index.py | 11 +++++++--- lidar_pipeline/tests/test_webapp.py | 31 ++++++++++++++++++++++++++--- lidar_pipeline/webapp.py | 6 +++++- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/lidar_pipeline/index.py b/lidar_pipeline/index.py index abc3769..2be4ec7 100644 --- a/lidar_pipeline/index.py +++ b/lidar_pipeline/index.py @@ -1100,7 +1100,7 @@ def _collect_tile_metadata(tile, dtm_dir): return meta -def build_index(output_dir, output_format='avif'): +def build_index(output_dir, output_format='avif', generate_overview=True): """Génère la carte interactive HTML des tuiles traitées, organisée par zones. Scanne output_dir/visualisations/, collecte les métadonnées de génération, @@ -1110,6 +1110,10 @@ def build_index(output_dir, output_format='avif'): Args: output_dir: dossier de sortie racine (contient visualisations/). output_format: format des images ('avif' ou 'webp') — pour info. + generate_overview: False sur la webapp légère (LIDAR_GENERATION_URL) : + ses mosaïques, bâties sur un cache partiel, auraient une géométrie + différente de celles du worker sous le même nom de fichier — + l'index distant fusionné après coup fournit les bonnes. Returns: Path vers index.html si succès, None si échec ou aucune tuile. @@ -1278,8 +1282,9 @@ def build_index(output_dir, output_format='avif'): logger.info(f" {n_sous} sous-tuiles générée(s) pour {n_dalles} dalle(s)") # Mosaïques d'overview (vue dézoomée) : une image par zone et par couche, - # assemblée depuis les vignettes des dalles de la zone. - overview = _build_overview(zone_records, output_dir) + # assemblée depuis les vignettes des dalles de la zone. Webapp légère : + # générées par la machine de traitement (cf. generate_overview). + overview = _build_overview(zone_records, output_dir) if generate_overview else {} if overview: n_items = sum(len(v['items']) for v in overview.values()) first = next(iter(overview.values()))['items'][0] diff --git a/lidar_pipeline/tests/test_webapp.py b/lidar_pipeline/tests/test_webapp.py index 4c1dadd..8a5b82f 100644 --- a/lidar_pipeline/tests/test_webapp.py +++ b/lidar_pipeline/tests/test_webapp.py @@ -340,7 +340,7 @@ def test_rebuild_flag_synchronous(monkeypatch): go = threading.Event() - def slow_build(out): + def slow_build(out, **kw): go.wait(2) # rebuild artificiellement long monkeypatch.setattr(index_mod, "build_index", slow_build) @@ -855,13 +855,38 @@ def test_available_layers_scans_disk(tmp_path, monkeypatch): assert "aspect" not in layers +def test_local_rebuild_skips_overview(tmp_path, monkeypatch): + """Webapp légère (GENERATION_URL) : pas de mosaïques d'overview locales. + + Bâties sur un cache partiel, leur géométrie diffère (autre px/km, autre + étendue) de celles du worker sous le MÊME nom de fichier : plus récentes + (mtime), elles seraient servies à la place et désaligneraient le payload. + """ + import time + import lidar_pipeline.webapp as webapp + import lidar_pipeline.index as index_mod + + monkeypatch.setattr(webapp, "OUTPUT_DIR", tmp_path) + monkeypatch.setattr(webapp, "GENERATION_URL", "http://distant:8973") + seen = {} + monkeypatch.setattr(index_mod, "build_index", + lambda out, **kw: seen.update(kw) or None) + webapp._rebuild.update({"running": False}) + assert webapp._start_rebuild()["demarré"] is True + for _ in range(200): + if not webapp._rebuild["running"]: + break + time.sleep(0.02) + assert seen.get("generate_overview") is False + + def test_rebuild_index_background(tmp_path, monkeypatch): """POST /api/rebuild lance build_index en arrière-plan puis termine.""" import time as _time import lidar_pipeline.webapp as webapp import lidar_pipeline.index as index_mod calls = [] - monkeypatch.setattr(index_mod, "build_index", lambda out: calls.append(out)) + monkeypatch.setattr(index_mod, "build_index", lambda out, **kw: calls.append(out)) assert webapp.rebuild_index()["demarré"] is True for _ in range(100): if not webapp.rebuild_status()["running"]: @@ -1024,7 +1049,7 @@ def test_sync_is_rebuild_only(tmp_path, monkeypatch): import lidar_pipeline.webapp as webapp import lidar_pipeline.index as index_mod calls = [] - monkeypatch.setattr(index_mod, "build_index", lambda out: calls.append(out)) + monkeypatch.setattr(index_mod, "build_index", lambda out, **kw: calls.append(out)) webapp._rebuild["done"] = None # ignore l'état d'un test précédent assert webapp.sync_and_rebuild()["demarré"] is True for _ in range(200): diff --git a/lidar_pipeline/webapp.py b/lidar_pipeline/webapp.py index 8728d30..f6c57bd 100644 --- a/lidar_pipeline/webapp.py +++ b/lidar_pipeline/webapp.py @@ -1153,7 +1153,11 @@ def _start_rebuild(): def _run(): try: from .index import build_index - build_index(OUTPUT_DIR) + # Overview : générée par la machine de traitement — une mosaïque + # locale (cache partiel, autre géométrie) sous le même nom de + # fichier serait servie à la place de la sienne (mtime plus + # récente) et désalignerait le payload distant. + build_index(OUTPUT_DIR, generate_overview=not GENERATION_URL) # Mode deux machines : le rebuild local ne voit que le cache — # on y fusionne l'index du worker pour que coquille embarquée et # /api/tiles servent la même vue complète dès le 1er chargement.