Mettre à jour la pyramide pendant le rendu et réduire son stockage

Le pipeline régénère l'inventaire après chaque dalle par défaut (tous
lanceurs, sauf --no-index), avec une passe différée quand l'anti-rebond
ignore une dalle. La carte surveille l'inventaire toutes les 10 s et place
les tuiles des dalles modifiées en tête de file de maintenance : la
pyramide suit le rendu au lieu d'attendre la fin du lot.

Stockage réduit pour le Raspberry Pi : seuls les niveaux standard pairs
jusqu'à z16 sont écrits sur disque, en AVIF ; les autres, dont le niveau
le plus fin (~75 % de la pyramide), sont rendus à la volée avec un cache
mémoire, y compris en mode cache seule. L'interface ne demande que les
niveaux pairs et réduit ceux du niveau supérieur aux zooms impairs.
Mesuré sur 8 dalles : ~24 Mo de pyramide avant, 1,2 Mo après.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Antoine Jacquin
2026-09-27 02:31:11 +02:00
parent f836408d6f
commit 434b7f12fd
11 changed files with 299 additions and 35 deletions

View File

@ -11,6 +11,7 @@ import logging
import multiprocessing
import os
import shutil
import threading
import time
from concurrent.futures import ProcessPoolExecutor, as_completed, TimeoutError as FuturesTimeoutError
from datetime import datetime
@ -156,11 +157,16 @@ class LidarArchaeoPipeline:
self.output_format = output_format
self.gpu_ids = gpu_ids
self.no_index = no_index
self.incremental_index = incremental_index
# Inventaire mis à jour après chaque dalle par défaut : la carte (et sa
# maintenance de pyramide) suit le rendu en cours, quel que soit le
# lanceur (carte, compose, run.sh). --no-index le désactive.
self.incremental_index = bool(incremental_index) or not no_index
self.strip_align = strip_align
self.openness_downsample = openness_downsample
self.edge_buffer = float(edge_buffer)
self._last_index_rebuild = 0.0
self._index_timer = None
self._index_lock = threading.Lock()
self.temp_dir = self.output_dir / "temp"
if not self.input_dir.exists():
@ -681,20 +687,32 @@ class LidarArchaeoPipeline:
"""
if self.no_index:
return
now = time.time()
if now - self._last_index_rebuild < 3.0:
wait = 3.0 - (time.time() - self._last_index_rebuild)
if wait > 0:
# Anti-rebond : la dalle n'est pas oubliée, une passe différée la
# couvre dès la fin de l'intervalle (sans attendre la dalle suivante).
with self._index_lock:
if self._index_timer is None:
self._index_timer = threading.Timer(wait, self._rebuild_index_now)
self._index_timer.daemon = True
self._index_timer.start()
return
self._last_index_rebuild = now
try:
from .index import build_index
saved_level = logger.getEffectiveLevel()
logger.setLevel(logging.WARNING)
self._rebuild_index_now()
def _rebuild_index_now(self):
with self._index_lock:
self._index_timer = None
self._last_index_rebuild = time.time()
try:
build_index(self.output_dir, self.output_format)
finally:
logger.setLevel(saved_level)
except Exception as e:
logger.debug(f"Rebuild incrémental de l'index ignoré : {e}")
from .index import build_index
saved_level = logger.getEffectiveLevel()
logger.setLevel(logging.WARNING)
try:
build_index(self.output_dir, self.output_format)
finally:
logger.setLevel(saved_level)
except Exception as e:
logger.debug(f"Rebuild incrémental de l'index ignoré : {e}")
def process_all(self, files=None):
"""Process all LAZ files in input directory (or an explicit list)."""