Documenter l'export PDF et le sidecar qualité
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@ -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)
|
- 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`
|
- 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`).
|
- 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
|
## 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.
|
- **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.
|
- **`_`-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`.
|
- **`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)
|
## Architecture Notes (from code audit 2025-09)
|
||||||
|
|
||||||
|
|||||||
49
docs/MAPS.md
49
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
|
principale, mode validé, opacités bornées à 0–1. Un fichier de l'ancienne pile
|
||||||
(`order`/`on`/`blend`) est ignoré, sauf le fond.
|
(`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** :
|
> **Licence** — LiDAR HD est diffusé sous **Licence Ouverte 2.0** :
|
||||||
> l'attribution IGN est obligatoire et doit rester visible chez le client.
|
> l'attribution IGN est obligatoire et doit rester visible chez le client.
|
||||||
> Avant d'utiliser ces rendus comme calque de **saisie** dans OpenStreetMap,
|
> Avant d'utiliser ces rendus comme calque de **saisie** dans OpenStreetMap,
|
||||||
|
|||||||
Reference in New Issue
Block a user