diff --git a/AGENTS.md b/AGENTS.md index 50aa880..b05239c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,6 +15,7 @@ - test rapide sans rebuild (code monté par-dessus l'image) : `docker run --rm -e PYTHONPATH=/app -v $(pwd)/lidar_pipeline:/app/lidar_pipeline lidar-lidar python3 -m pytest --pyargs lidar_pipeline.tests -q` (~3 min ; ajouter `timeout 600` devant, et PAS de pipe `| tail` qui masque la progression) - debug: `./run.sh --debug` (file:line logging); container shell: `docker run --rm -it -v $(pwd)/input:/data/input -v $(pwd)/output:/data/output --entrypoint bash lidar-lidar` - mise à jour du Pi de prod (192.168.3.10, checkout `/srv/lidar_rendu`, override maps + Traefik) : `ssh` — `ssh 192.168.3.10 "cd /srv/lidar_rendu && git pull && docker compose -f docker-compose.maps.yml -f docker-compose.maps.override.yml up -d --build"` (procédure dans `docs/DEPLOY_WEBAPP.md`). +- rattrapage des sidecars qualité (dalles traitées avant l'ajout du sidecar) : la commande fixe de `process` (compose) est remplacée en entier dès qu'un argument suit le nom du service, donc `--quality-backfill` seul ne fonctionne pas — utiliser `docker compose run --rm --build process python3 -m lidar_pipeline /data/input -o /data/output --quality-backfill`. ## Conventions @@ -56,6 +57,8 @@ - **Tests use lazy imports inside each test function**, never at module top, to avoid importing CuPy/GDAL at import time. - **`_`-prefixed names are critical private**: `_create_ground_pipeline`, `_fallback_to_smrf`, `_fill_nans`, `_init_gpu`, `_process_file_standalone` — do not call from outside their module. - **`build_index()` écrit l'inventaire + les paliers sources** : `output/index_tiles.json` (dalles, couches, URLs versionnées — servi par `/api/tiles`), vignettes `index_thumbs/` (≈3,9 m/px + `_mid` 1,56 m/px) et quadrants `index_subtiles/` — paliers de la pyramide XYZ (`tiles.py`). Chaque tuile du run en cours porte ses coins WGS84 pour les cadres de progression. Plus d'HTML : l'interface vit dans `mapui.py`. +- **Export PDF (`export_pdf.py`)** : Pillow + pyproj + reportlab, sans numpy — tourne dans l'image légère seule. `/api/export/frame` (cadre) et `/api/export/pdf` (planche) dans `mapserve.py`, jamais délégués à `LIDAR_GENERATION_URL`, verrou d'export unique (`_export_lock`, 429 si occupé). Composition directe en L93 (`tiles.sources_in_bbox`/`load_source`, recadrage + rééchantillonnage Lanczos), sans passer par les tuiles XYZ. Planche : quadrillage L93 (pas selon l'échelle), coins WGS84, flèche du nord tenant compte de la convergence du méridien, échelle graphique et numérique, rose des orientations 8 points, texte de légende tiré de `VIZ_LEGENDS`, encart qualité (hachures blanches = « non renseigné », ligne « Donnée manquante (sans relief) » pour les dalles hors couverture), 300 dpi en A4 / 250 dpi en A3 (borne la mémoire du Pi). `NODATA_RGB` recopie `visualizations.RELIEF_NODATA_RGB` (numpy absent de l'image légère) ; hors emprise des dalles = blanc hachuré. +- **Sidecar qualité (`quality.py`)** : `output/quality/{basename}.json` (`QUALITY_VERSION`), densité sol par maille 50 m et dates d'acquisition, calculé après le DTM (`ensure_quality`, y compris sur un DTM déjà en cache — mesure alors directement le LAZ d'entrée) ; rattrapage batch `--quality-backfill` (cf. Workflow) ; recopié dans la table `quality` de `index_tiles.json` (`build_index`) et sur les machines légères (`tiles._persist_remote_quality`, depuis l'inventaire amont). ## Architecture Notes (from code audit 2025-09) diff --git a/docs/MAPS.md b/docs/MAPS.md index b7772e9..3bddb14 100644 --- a/docs/MAPS.md +++ b/docs/MAPS.md @@ -167,6 +167,55 @@ filtrées : couche inconnue (ou la précision elle-même) refusée comme principale, mode validé, opacités bornées à 0–1. Un fichier de l'ancienne pile (`order`/`on`/`blend`) est ignoré, sauf le fond. +## Export PDF (planche d'impression terrain) + +Le bouton **⎙** ouvre une carte de réglages (format A4/A3, paysage/portrait, +échelle 1:1 000 à 1:10 000, titre optionnel) et affiche un **cadre jaune en +pointillés** qui montre la zone qui sera imprimée. Le cadre reste toujours +centré sur la vue : c'est la carte qu'on déplace en dessous, pas le cadre — +il se recalcule (géométrie Lambert 93 exacte, `GET /api/export/frame`) à +chaque déplacement et à chaque changement de réglage. Les réglages sont +mémorisés dans `localStorage` du navigateur et rechargés à l'ouverture +suivante. **Exporter le PDF** télécharge la planche (`GET +/api/export/pdf`), nommée `relief_{x_km}_{y_km}_1-{échelle}.pdf` (centre +Lambert 93 en km, à trois décimales). + +La planche (module `lidar_pipeline/export_pdf.py`) est composée directement +en Lambert 93 depuis les sources déjà rendues (pas de passage par les tuiles +XYZ), puis dessinée en vectoriel (texte, grille, légende) avec `reportlab` — +Pillow + pyproj + reportlab uniquement, **sans numpy** : elle tourne aussi +bien sur l'image complète que sur l'image légère du Pi seul. Contenu : + +- la carte du **relief orienté**, recadrée à l'échelle demandée (300 dpi en + A4, 250 dpi en A3 — borne la mémoire du Pi, ~36 Mo en A3) ; hors emprise des + dalles disponibles, la zone reste blanche et hachurée ; +- un **quadrillage Lambert 93** (pas 100 m aux échelles 1:1 000/1:2 000, 500 m + au 1:5 000, 1 000 m au 1:10 000) gradué en marge, et les **coins WGS84** de + la zone imprimée aux quatre angles ; +- une **flèche du nord géographique** tenant compte de la convergence du + méridien (la carte est orientée sur le nord du quadrillage L93, pas le nord + géographique — l'écart est indiqué en degrés) ; +- une **échelle graphique** (barre alternée) et l'**échelle numérique** ; +- une **rose des orientations à 8 points** (N/NE/E/SE/S/SO/O/NO), même + formule CIELAB que la rose de la fiche de dalle ; +- le texte de légende du relief orienté, repris de `VIZ_LEGENDS` (source + unique avec l'interface et le TileJSON) ; +- un **encart qualité** : miniature de la densité de points sol (mailles + 50 m, classes de couleur), chiffres clés (densité moyenne, maille la plus + faible, part de surface interpolée, période d'acquisition), zones **hachurées + en blanc** là où la qualité n'est pas renseignée, et une ligne « **Donnée + manquante (sans relief)** » listant les dalles de la zone qui n'ont pas + encore été générées ; +- un cartouche : titre (par défaut, liste des dalles couvertes), échelle, + format, dpi, centre L93, taille de la zone, date d'export et mention de + source IGN. + +Un seul export PDF à la fois : un second appel pendant qu'un export tourne +reçoit `429` (réessayer). L'export n'est **jamais délégué** à +`LIDAR_GENERATION_URL` : contrairement à la génération de tuiles, la carte +légère seule (Pi sans worker) sait exporter par elle-même, à partir des +sources déjà rapatriées sur disque. + > **Licence** — LiDAR HD est diffusé sous **Licence Ouverte 2.0** : > l'attribution IGN est obligatoire et doit rester visible chez le client. > Avant d'utiliser ces rendus comme calque de **saisie** dans OpenStreetMap,