- Grille de calcul du volume adaptee dynamiquement au pas spatial de chaque niveau (au lieu d'une grille fixe derivee du niveau 0) : evite l'effondrement artificiel du "matching cells %" observe des le premier niveau de decimation (99% -> 58% -> 13% -> 3% -> 0.9% -> 0.2%), qui refletait un desalignement grille/densite plutot qu'une vraie perte d'information. - Authentification HTTP Basic sur toute l'app (UI + API), identifiants en env (AUTH_USERNAME / AUTH_PASSWORD_HASH hashe bcrypt), desactivable en dev local si non definis. /health reste public pour le healthcheck Docker/Coolify.
166 lines
8.8 KiB
Markdown
166 lines
8.8 KiB
Markdown
# Volume 2.5D & robustesse a la decimation
|
|
|
|
Application locale (Docker) qui, a partir d'un nuage de points (LAS/LAZ/COPC.laz/BIN) :
|
|
|
|
1. calcule son volume en 2.5D via CloudCompare (plan de reference = altitude minimale du nuage) ;
|
|
2. le decime progressivement en methode **spatiale**, facteur **x2** sur **5 etapes** (le pas spatial double
|
|
a chaque etape, en partant d'un pas initial estime automatiquement = distance mediane au plus proche
|
|
voisin, calculee par KD-tree sur un echantillon) ;
|
|
3. recalcule le volume a chaque niveau avec le **meme plan de reference**, et une **grille de calcul
|
|
adaptee dynamiquement a la densite de chaque niveau** (grid step = pas spatial du niveau x2) pour
|
|
eviter des grilles artificiellement vides quand la decimation depasse la resolution de depart ;
|
|
4. assemble les 6 nuages (complet + 5 decimations) dans un seul fichier `.bin` CloudCompare, nomme et
|
|
consultable dans CloudCompare Desktop ;
|
|
5. produit un dossier de resultats : rapport CSV/JSON, cartes de hauteur par niveau, graphiques de
|
|
synthese (volume, nombre de points, robustesse) ;
|
|
6. expose tout ca dans une UI web simple avec historique des runs.
|
|
|
|
## Demarrage
|
|
|
|
```bash
|
|
docker compose up -d --build
|
|
```
|
|
|
|
Puis ouvrir http://localhost:8080
|
|
|
|
Les donnees (uploads, resultats, base sqlite) sont persistees dans un volume Docker nomme `app-data`
|
|
(pas un bind mount vers `./data`, voir "Deploiement Coolify" ci-dessous pour la raison).
|
|
|
|
## Authentification
|
|
|
|
L'UI et l'API sont protegees par HTTP Basic Auth (identifiants dans `AUTH_USERNAME` /
|
|
`AUTH_PASSWORD_HASH`, hash bcrypt — jamais le mot de passe en clair). Seul `/health` reste public
|
|
(healthcheck Docker/Coolify).
|
|
|
|
**Si ces deux variables ne sont pas definies, l'auth est desactivee** (pratique pour du dev local,
|
|
un warning est logge au demarrage). En production/Coolify, toujours les definir.
|
|
|
|
Generer un hash pour un nouveau mot de passe :
|
|
|
|
```bash
|
|
node -e "console.log(require('bcryptjs').hashSync('VOTRE_MOT_DE_PASSE', 10))"
|
|
```
|
|
|
|
Puis definir dans Coolify (onglet "Environment Variables" de l'application, **pas** dans
|
|
`docker-compose.yml` du repo, pour ne pas committer le hash) :
|
|
|
|
```
|
|
AUTH_USERNAME=nico
|
|
AUTH_PASSWORD_HASH=$2a$10$... # sortie de la commande ci-dessus
|
|
```
|
|
|
|
En local avec `docker compose`, un fichier `.env` (gitignore) a la racine du repo fonctionne aussi :
|
|
|
|
```
|
|
AUTH_USERNAME=nico
|
|
AUTH_PASSWORD_HASH=$2a$10$...
|
|
```
|
|
|
|
## Deploiement Coolify
|
|
|
|
Le repo est pret pour un deploiement Coolify via webhook (push sur `main` -> build + deploy auto) :
|
|
|
|
- Le `Dockerfile` fait tourner **lint + typecheck + tests unitaires** avant de compiler — un commit qui
|
|
casse l'un de ces trois echoue le build, donc n'est jamais deploye.
|
|
- `HEALTHCHECK` integre au Dockerfile (`curl` sur `/health`, route publique non authentifiee) :
|
|
Coolify l'utilise pour verifier que le deploiement est reellement sain.
|
|
- Persistance : le volume `/data` doit etre monte comme un **volume nomme/gere par Coolify**, pas un
|
|
bind mount vers le checkout git (Coolify re-clone le repo a chaque deploiement — un bind mount
|
|
relatif au checkout perdrait toutes les donnees a chaque redeploy). Deux options :
|
|
- Resource **"Docker Compose"** dans Coolify pointant sur `docker-compose.yml` du repo : le volume
|
|
nomme `app-data` declare dedans est deja correct.
|
|
- Resource **"Application"** (Dockerfile) : configurer un volume persistant sur `/data` depuis
|
|
l'onglet "Storage" de Coolify, et le port expose sur `3000` (variable `PORT`, lue par l'appli).
|
|
- Variables d'environnement a definir dans Coolify : `AUTH_USERNAME` / `AUTH_PASSWORD_HASH`
|
|
(obligatoire, voir "Authentification" plus haut), et optionnellement `MAX_UPLOAD_MB`,
|
|
`STATS_SAMPLE_CAP`, `CC_TIMEOUT_MS` si besoin de changer les defauts (voir tableau plus bas).
|
|
- Le webhook de deploiement automatique est deja configure cote Coolify sur ce repo.
|
|
|
|
## Utilisation
|
|
|
|
1. "Nouveau run" -> choisir un fichier `.las`, `.laz`, `.copc.laz` ou `.bin`.
|
|
2. Optionnel : override du plan de reference Z (par defaut : altitude minimale d'un echantillon du
|
|
nuage) et/ou du pas spatial initial (par defaut : distance mediane au plus proche voisin).
|
|
3. Le run tourne en arriere-plan (une file d'attente sequentielle traite les runs un par un). La page
|
|
de detail se rafraichit automatiquement (polling 2s).
|
|
4. Une fois termine : tableau des 6 niveaux (points, volume, surface, % de cellules de grille
|
|
"matching"), cartes de hauteur, graphiques de synthese, et telechargements (`.bin` fusionne,
|
|
`.csv`, dossier complet en `.zip`).
|
|
|
|
### Lecture des resultats
|
|
|
|
- **% de cellules "matching"** (rapport de volume 2.5D de CloudCompare) : indique la part de la grille
|
|
de calcul ou les deux surfaces (nuage / plan de reference) sont effectivement comparables. Quand la
|
|
decimation depasse la resolution de la grille de calcul, ce pourcentage chute — c'est le signal
|
|
principal de perte de robustesse. Un niveau est marque "a verifier" des que ce taux passe sous 90 %
|
|
(configurable via `MATCHING_CELLS_WARN_THRESHOLD` cote code, `config.ts`).
|
|
- La decimation s'arrete automatiquement si un niveau tombe sous 25 points exploitables ; les niveaux
|
|
suivants sont marques "skipped" avec la raison.
|
|
|
|
## Parametres (variables d'environnement, voir `docker-compose.yml`)
|
|
|
|
| Variable | Defaut | Description |
|
|
|---|---|---|
|
|
| `PORT` | 3000 | port interne du serveur |
|
|
| `DATA_DIR` | `/data` | dossier de persistance (uploads, resultats, sqlite) |
|
|
| `AUTH_USERNAME` | _(non defini = auth off)_ | nom d'utilisateur HTTP Basic |
|
|
| `AUTH_PASSWORD_HASH` | _(non defini = auth off)_ | hash bcrypt du mot de passe |
|
|
| `MAX_UPLOAD_MB` | 2048 | taille max d'un nuage uploade |
|
|
| `STATS_SAMPLE_CAP` | 50000 | nb de points echantillonnes pour estimer le pas spatial initial |
|
|
| `CC_TIMEOUT_MS` | 600000 | timeout par appel CloudCompare CLI |
|
|
|
|
## Formats supportes
|
|
|
|
`.las`, `.laz`, `.copc.laz` (lu comme un LAZ standard — l'indexation octree COPC n'est pas utilisee,
|
|
seuls les points le sont), `.bin` (format natif CloudCompare).
|
|
|
|
**E57 non supporte pour l'instant** (exclu volontairement du perimetre initial).
|
|
|
|
## Architecture
|
|
|
|
- **Backend** : NestJS (TypeScript), file d'attente de jobs en memoire (un run a la fois), SQLite
|
|
(`better-sqlite3`) pour l'historique des runs.
|
|
- **Orchestration du pipeline CloudCompare** : chaque etape (estimation stats, conversion, decimation,
|
|
volume, export) est un appel independant au CLI `CloudCompare -SILENT ...` (le CLI fonctionne comme
|
|
une machine a etats sur UNE session de process, donc chaque etape = un process).
|
|
- **Scripts Python** (`python/`) : `stats_helper.py` (distance au plus proche voisin via scipy KD-tree),
|
|
`render_images.py` (cartes de hauteur + graphiques de synthese via matplotlib/numpy),
|
|
`las_to_xyz.py` (conversion LAS/LAZ/COPC -> ASCII, voir "Limitation connue" ci-dessous).
|
|
- **Frontend** : HTML/JS vanilla + Tailwind (CDN), servi en statique par NestJS. Pas de framework, pas
|
|
de build front separe.
|
|
- **Image Docker** : `debian:trixie-slim` + paquet apt `cloudcompare` (2.13.2, evite une compilation
|
|
depuis les sources) + `xvfb` (CloudCompare reste une appli Qt, meme en `-SILENT` elle a besoin d'un
|
|
display) + Node 20 + Python 3.
|
|
|
|
## Limitations connues
|
|
|
|
- **Le paquet apt `cloudcompare` (Debian) ne fournit pas le plugin LAS/LAZ** (uniquement le plugin
|
|
"Core I/O"). Consequence : les fichiers LAS/LAZ/COPC sont convertis en ASCII XYZ via `laspy` (Python)
|
|
*avant* d'etre transmis a CloudCompare, qui lit l'ASCII nativement sans plugin. Ca fonctionne bien
|
|
mais perd les attributs LAS annexes (intensite, classification, RGB...) — non utilises par ce calcul
|
|
de volume de toute facon.
|
|
- **Le meme paquet plante (assertion GDAL) sur l'export GeoTIFF natif** (`-RASTERIZE
|
|
-OUTPUT_RASTER_Z`). Les cartes de hauteur sont donc reconstruites nous-memes (export ASCII du nuage +
|
|
binning numpy/scipy + rendu matplotlib) plutot que de dependre du rasterizer CloudCompare.
|
|
- Le plan de reference Z et le pas de grille de volume sont **approximatifs** quand ils sont
|
|
auto-detectes (calcules sur un echantillon aleatoire de points, pas la totalite du nuage) — largement
|
|
suffisant pour comparer les 6 niveaux entre eux, mais a corriger via l'override si une valeur exacte
|
|
est necessaire.
|
|
- Un seul run traite a la fois (file d'attente sequentielle) : adapte a un usage local mono-utilisateur,
|
|
pas concu pour un usage concurrent multi-utilisateurs.
|
|
|
|
## Developpement local (sans Docker)
|
|
|
|
Necessite CloudCompare installe localement + Python 3 avec `pip install -r python/requirements.txt`.
|
|
|
|
```bash
|
|
npm install
|
|
npm run lint # eslint
|
|
npm run typecheck # tsc --noEmit
|
|
npm run test # jest (tests unitaires)
|
|
npm run build
|
|
CLOUDCOMPARE_BIN="/chemin/vers/CloudCompare" PYTHON_BIN=python3 DATA_DIR=./data node dist/main.js
|
|
```
|
|
|
|
Voir [CLAUDE.md](CLAUDE.md) pour le detail des comportements CloudCompare CLI verifies empiriquement
|
|
(pieges non documentes, limitations du paquet apt Debian, etc.) — a lire avant de toucher au pipeline.
|