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:
@ -54,6 +54,14 @@ def tile_filename(col, row):
|
||||
return f"LHD_FXX_{col:04d}_{row:04d}_PTS_LAMB93_IGN69.copc.laz"
|
||||
|
||||
|
||||
def _report_download(output_dir, tile, state, detail=None):
|
||||
"""Émet un événement de téléchargement pour la file de génération (best-effort)."""
|
||||
if output_dir is None:
|
||||
return
|
||||
from .progress import report_event
|
||||
report_event(output_dir, tile, "download", state, detail=detail)
|
||||
|
||||
|
||||
def _bbox_wgs84(col, row):
|
||||
"""Bbox WGS84 de la dalle (col,row) pour la requête STAC (peut être élargie)."""
|
||||
try:
|
||||
@ -117,46 +125,76 @@ def download_file(url, dest_path, timeout=120, chunk=1024 * 1024):
|
||||
return done
|
||||
|
||||
|
||||
def fetch_tiles(input_dir, specs, output_dir=None):
|
||||
"""Télécharge les dalles IGN spécifiées, sauf celles déjà présentes/générées.
|
||||
def fetch_tiles(input_dir, specs, output_dir=None, only_viz=None, resolutions=(0.5,), force=False):
|
||||
"""Télécharge les dalles IGN spécifiées, sauf celles inutiles au traitement.
|
||||
|
||||
Args:
|
||||
input_dir: dossier des fichiers LAZ (écriture autorisée requise).
|
||||
specs: liste de tuples (col, row).
|
||||
output_dir: dossier de sortie (optionnel) — permet d'ignorer les
|
||||
tuiles dont les visualisations existent déjà.
|
||||
tuiles déjà complètes.
|
||||
only_viz: noms d'étapes des visualisations demandées (ex: ['aspect']).
|
||||
Si fourni, une tuile n'est ignorée que si elle possède déjà
|
||||
toutes ces visualisations aux résolutions demandées —
|
||||
sinon son LAZ est (re)téléchargé pour compléter les
|
||||
visualisations manquantes.
|
||||
resolutions: résolutions attendues (m/px) pour considérer une tuile
|
||||
complète quand only_viz est fourni.
|
||||
force: True pour télécharger même les tuiles complètes (régénération :
|
||||
leur LAZ est requis pour retraiter avec --force).
|
||||
|
||||
Returns:
|
||||
Liste des chemins téléchargés.
|
||||
"""
|
||||
input_dir = Path(input_dir)
|
||||
complete = None
|
||||
if output_dir is not None and only_viz and not force:
|
||||
from .index import cells_with_all_viz, step_to_keyword
|
||||
keys = [step_to_keyword(v) for v in only_viz]
|
||||
complete = cells_with_all_viz(Path(output_dir) / "visualisations",
|
||||
keys, resolutions)
|
||||
downloaded = []
|
||||
for col, row in specs:
|
||||
name = tile_filename(col, row)
|
||||
tile = name[:-len(".copc.laz")] if name.endswith(".copc.laz") else name
|
||||
dest = input_dir / name
|
||||
if dest.exists():
|
||||
logger.info(f" {name} : déjà présent dans input/ — aucun téléchargement")
|
||||
_report_download(output_dir, tile, "skip", "déjà dans input/")
|
||||
continue
|
||||
if output_dir is not None:
|
||||
vis_dir = Path(output_dir) / "visualisations"
|
||||
if list(vis_dir.glob(f"LHD_FXX_{col:04d}_{row:04d}_PTS*")):
|
||||
logger.info(f" {name} : visualisations déjà générées — ignorée")
|
||||
continue
|
||||
if output_dir is not None and not force:
|
||||
if complete is not None:
|
||||
if (col, row) in complete:
|
||||
logger.info(f" {name} : visualisations déjà complètes — ignorée")
|
||||
_report_download(output_dir, tile, "skip", "visualisations déjà complètes")
|
||||
continue
|
||||
else:
|
||||
vis_dir = Path(output_dir) / "visualisations"
|
||||
if list(vis_dir.glob(f"LHD_FXX_{col:04d}_{row:04d}_PTS*")):
|
||||
logger.info(f" {name} : visualisations déjà générées — ignorée")
|
||||
_report_download(output_dir, tile, "skip", "visualisations déjà générées")
|
||||
continue
|
||||
logger.info(f" {name} : recherche dans le catalogue IGN...")
|
||||
_report_download(output_dir, tile, "start", "catalogue IGN")
|
||||
try:
|
||||
url = find_tile_url(col, row)
|
||||
except Exception as e:
|
||||
logger.warning(f" ✗ {name} : erreur catalogue ({e})")
|
||||
_report_download(output_dir, tile, "fail", f"erreur catalogue : {e}")
|
||||
continue
|
||||
if not url:
|
||||
logger.warning(f" ✗ {name} : introuvable dans le catalogue IGN (zone non publiée ?)")
|
||||
_report_download(output_dir, tile, "fail", "introuvable dans le catalogue IGN")
|
||||
continue
|
||||
logger.info(f" {name} : téléchargement depuis la géoplateforme...")
|
||||
_report_download(output_dir, tile, "start", "géoplateforme")
|
||||
try:
|
||||
download_file(url, dest)
|
||||
logger.info(f" ✓ {name} téléchargée")
|
||||
_report_download(output_dir, tile, "ok")
|
||||
downloaded.append(dest)
|
||||
except Exception as e:
|
||||
dest.unlink(missing_ok=True)
|
||||
logger.warning(f" ✗ {name} : échec du téléchargement ({e})")
|
||||
_report_download(output_dir, tile, "fail", f"échec du téléchargement : {e}")
|
||||
return downloaded
|
||||
|
||||
Reference in New Issue
Block a user