Split webapp for Raspberry Pi deployment, remote generation API and sync

La webapp (carte + vignettes) et la génération de tuiles se déploient sur
deux machines : image légère Dockerfile.webapp (FastAPI + Pillow AVIF
natif + pyproj) sur Raspberry Pi, pipeline complet sur la machine de
traitement. LIDAR_GENERATION_URL délègue /api/generate, /api/preview et
/api/status ; /api/sync ramène les tuiles par rsync puis régénère
vignettes et index localement. Token partagé optionnel
(LIDAR_API_TOKEN/LIDAR_REMOTE_TOKEN). Retire du dépôt les journaux
internes (.swival, audit-findings) et les données (data/, notebooks/).
Doc : docs/DEPLOY_WEBAPP.md.
This commit is contained in:
Antoine Jacquin
2026-09-02 19:44:39 +02:00
parent ed3e90ea89
commit 422f58d772
24 changed files with 2390 additions and 329 deletions

View File

@ -238,6 +238,12 @@ def main():
action="store_true",
help="Ne pas générer la carte globale à la fin du traitement"
)
parser.add_argument(
"--incremental-index",
action="store_true",
help="Régénérer la carte après chaque tuile terminée (affichage en direct "
"dans la webapp, sans attendre la fin du run)"
)
args = parser.parse_args()
@ -286,20 +292,11 @@ def main():
logger.warning("Aucune tuile traitée trouvée — carte globale non générée")
return
# Téléchargement des dalles IGN manquantes avant le traitement
if args.fetch_tiles:
from .fetch_ign import fetch_tiles, parse_tile_specs
try:
specs = parse_tile_specs(args.fetch_tiles)
except ValueError as e:
logger.error(str(e))
return
logger.info(f"Téléchargement de {len(specs)} dalle(s) LiDAR HD depuis l'IGN...")
fetched = fetch_tiles(args.input, specs, args.output)
if fetched:
logger.info(f"{len(fetched)} dalle(s) téléchargée(s) — traitement...")
else:
logger.warning("Aucune dalle téléchargée (déjà présentes ou introuvables)")
# Nouveau run : remise à zéro du journal d'événements (file de
# génération). Ici, avant le téléchargement — les événements download
# survivent au démarrage du traitement (process_all ne tronque plus).
from .progress import reset_events
reset_events(args.output)
quality = 100 if args.lossless else args.quality
# Parse --only and --skip: accept comma-separated values
@ -309,6 +306,27 @@ def main():
skip_viz = None
if args.skip:
skip_viz = [v.strip() for item in args.skip for v in item.split(',')]
resolutions = [float(r.strip()) for r in args.resolution.split(',') if r.strip()]
# Téléchargement des dalles IGN manquantes avant le traitement.
# only_viz/resolutions : une tuile n'est ignorée que si elle possède
# déjà toutes les visualisations demandées — une tuile incomplète est
# (re)téléchargée pour générer ses visualisations manquantes.
if args.fetch_tiles:
from .fetch_ign import fetch_tiles, parse_tile_specs
try:
specs = parse_tile_specs(args.fetch_tiles)
except ValueError as e:
logger.error(str(e))
return
logger.info(f"Téléchargement de {len(specs)} dalle(s) LiDAR HD depuis l'IGN...")
fetched = fetch_tiles(args.input, specs, args.output,
only_viz=only_viz, resolutions=resolutions,
force=args.force or args.force_classification)
if fetched:
logger.info(f"{len(fetched)} dalle(s) téléchargée(s) — traitement...")
else:
logger.warning("Aucune dalle téléchargée (déjà présentes ou introuvables)")
pipeline = LidarArchaeoPipeline(
input_dir=args.input,
output_dir=args.output,
@ -326,6 +344,7 @@ def main():
output_format=args.format,
gpu_ids=gpu_ids,
no_index=args.no_index,
incremental_index=args.incremental_index,
)
# If --file is specified, process only matching files