Le MNT de chaque dalle s'étend d'une bande de 100 m remplie avec les
points sol des 8 tuiles voisines (option « Raccord des bords ») : les
rendus à grand noyau (openness, SVF, LRM) deviennent continus d'une
dalle à l'autre, les images restent recadrées sur le kilomètre exact.
Pendant une génération, la carte encadre les dalles du run : orange
pulsant en cours de rendu, rouge en échec — les coins WGS84 sont portés
par /api/status pour toutes les dalles non terminées.
La file de génération trie les dalles du nord au sud et passe à 10
workers GPU.
Trois chantiers liés à la qualité et au coût des rendus :
- Calage vertical des faisceaux : les passes d'une tuile peuvent être biaisées
de quelques cm (±2,5 cm mesurés sur 1000_6882), créant des marches aux
recouvrements. Les offsets par PointSourceId sont mesurés sur les points
sol de la tuile (réf. médiane itérée) et retranchés ≥ 0,5 cm avant
rastérisation, avec sidecar de cache et application au plancher bare-earth.
- Openness décimée ×2 : lancé de rayons sur grille par blocs (max/min) puis
rééchantillonnage bilinéaire — 532 s → 40 s par tuile à 0,2 m sur CPU,
signal archéologique préservé. Réglable --openness-downsample.
- Génération webapp concentrée sur les couches affichées : les défauts
/api/generate, /api/preview et le sélecteur génèrent le panneau complet
(slope, aspect, pos_open) au lieu d'aspect seul.
- Sur la webapp légère, le rebuild ne voyait que le cache local : la
coquille HTML embarquait un sous-ensemble des tuiles et le premier
chargement n'affichait pas les tuiles récentes du worker — elles
n'arrivaient qu'au sondage /api/tiles suivant (15 s).
- _merge_remote_index() applique au rebuild la même fusion que
/api/tiles (l'index distant prime, le local complète) et réécrit
index_tiles.json ET la liste embarquée dans index.html.
- AGENTS.md : avertissement — le Pi de prod tourne en compose+override
(labels Traefik + /srv/lidar/output), jamais via serve-webapp.sh.
- Le Dockerfile principal (worker, --serve, local-2m) n'avait pas l'étape
de bake : le repli de webapp.py tentait d'écrire webapp_assets/ dans le
paquet installé (dist-packages, propriété de root) et faisait planter
l'import de la webapp sous l'utilisateur lidar.
- Même étape de bake que Dockerfile.webapp, avec cd /tmp pour cibler le
paquet installé (sinon la copie source /app capte l'import via sys.path).
- Le repli de webapp.py ne casse plus l'import si le dossier du paquet
n'est pas inscriptible : réécriture dans un répertoire temporaire.
- Les assets de l'interface étaient écrits dans le volume (output/assets)
et servis d'ici : sur la webapp légère, un app.js périmé restait servi
même après rebuild de l'image.
- app.js/app.css (+ Leaflet vendorisé) sont désormais bâchés dans l'image
au build (Dockerfile.webapp) et servis depuis l'image ; le volume ne
porte que les données (index.html, vignettes, tuiles).
- _write_ui_assets() génère les assets dans un dossier cible ; repli de
régénération au démarrage si non bâchés (dev). build_index continue
d'écrire output/assets/ pour le mode serveur statique.
- Le bouton « Vider » ne retirait que les tâches du côté client : au
rechargement, la « dernière génération » du serveur
(.generation.job.json) était réadoptée et les tâches finies
réappaissaient.
- Les runs retirés sont mémorisés par horodatage serveur (sessionStorage) ;
l'adoption au chargement les ignore désormais.
- Les tâches lancées immédiatement (sans passer par la file) captent
l'horodatage de leur run à la fin, pour être retirables sans réadoption.
- Clic sur la carte pour choisir des dalles précises, même sans données
existantes (via /api/cell) ; les zones dessinées s'ajoutent à la
sélection au lieu de la remplacer
- Une demande lancée pendant un run part en file d'attente côté serveur
et démarre à la fin du travail en cours (plus de refus 409), file
vidable depuis l'interface
- Tuiles et interface toujours fraîches : images servies sans cache
navigateur (revalidation 304), rechargement de la carte seulement une
fois le rebuild de l'index terminé
- serve-webapp.sh : sous-commande update retirée, documentation de
déploiement corrigée en conséquence
La bordure 1px des tuiles insettait l'image (box-sizing: border-box) et
creusait un espace vide entre deux tuiles ; la grille est désormais un
outline dessiné par-dessus le bord des images, sans consommer de place
(hover/sélection/export/régénération adaptés). Les URLs de app.css et
app.js portent un hash de contenu : chaque évolution force le
rechargement chez tous les navigateurs, même avec une entrée de cache
antérieure aux en-têtes no-cache.
Les marqueurs Leaflet glissent pendant l'animation de zoom sans changer
de taille : les tuiles LiDAR nageaient sur le fond OSM puis sautaient à
la fin du zoom. Elles vivent désormais dans un pane dédié (lidarTiles)
qui reçoit la même mise à l'échelle que les couches de dalles (translate
+ scale au zoomanim, transition CSS partagée leaflet-zoom-animated) ;
markerZoomAnimation est désactivé pour éviter le double mouvement.
Retire la couche Rugosité du panneau (DEFAULT_LAYERS restreint panneau,
presets et sélecteur de génération). Réduit la mémoire du navigateur :
vignettes de sous-tuiles à 160 px (régénérées sans ré-encoder les AVIF),
pleine résolution plafonnée à 24 images les plus proches du centre, src
des <img> libéré hors écran. Les changements de palier pré-décodent
l'image avant échange (zoom et pan sans flash).
Le z-score par tuile et le vmax au percentile 98 par tuile donnaient une
échelle de couleur non jointive (écart-type variant de 0,05 a 0,58 m selon
la dalle). Références de normalisation figées (médianes mesurées sur 20
dalles réelles) et vmax fixe 3,8 (p98 médian) : même rugosité physique =
même couleur partout.
Remplace NearestNDInterpolator (cKDTree sur les 25 M de points valides,
plusieurs secondes par dalle trouée en mode IGN pur) par
distance_transform_edt : plus proche voisin en une passe O(n).
Voisins équidistants arbitres differemment, sans effet sur les rendus.
Les presets portent désormais les niveaux de transparence complets :
opacité de chaque visualisation + fond de carte OSM (visibilité,
opacité, mode sombre). Les libellés du sélecteur affichent les
niveaux (ex. « Relief — Aspect 100% · Openness positive 65% · Fond
masqué ») et l'application d'un preset resynchronise la ligne du fond
de carte.
L'onglet Couches propose un sélecteur de presets : cinq presets
intégrés (Relief, Pentes, Cavités, Rugosité, Tout) définissent les
couches visibles, leurs transparences et l'ordre de pile. 💾 enregistre
l'état actuel comme preset personnel (persisté en localStorage), ✕ le
supprime depuis la liste « Mes presets ».