- .env.example committe, documente toutes les variables (auth, uploads, timeouts) avec instructions pour generer un hash bcrypt. - .env local (gitignore) pour docker compose : attention, echapper chaque \$ en \$\$ dans le hash bcrypt sinon docker compose l'interprete comme de la substitution de variable et le tronque silencieusement (verifie : $2a$10$xxx devenait $2a$10 tronque sans l'echappement).
186 lines
10 KiB
Markdown
186 lines
10 KiB
Markdown
# Volume 2.5D & robustesse a la decimation
|
|
|
|
Application locale (Docker) qui, a partir d'un ou deux nuages de points (LAS/LAZ/COPC.laz/BIN) :
|
|
|
|
1. calcule un volume en 2.5D via CloudCompare, selon 2 modes au choix :
|
|
- **Plan de reference Z constant** : un seul nuage, compare a un plan horizontal (altitude minimale
|
|
par defaut). Fiable uniquement si le nuage ne contient QUE l'objet mesure (pas de terrain/contexte
|
|
environnant) ;
|
|
- **Comparaison a un second nuage** : deux nuages, la **surface superieure** (sommet de l'amas) et
|
|
la **limite inferieure** (base/socle) — les densites sont d'abord appariees (le plus dense decime
|
|
pour matcher l'autre), puis les deux sont decimes ensemble a chaque etape ;
|
|
2. dans les deux modes, 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 une **grille de calcul adaptee dynamiquement a la densite
|
|
de chaque niveau** (grid step = pas spatial du niveau x2), et **comble les trous de scan** avant
|
|
chaque calcul (interpolation Delaunay bornee par une distance max, pour ne pas combler les parties
|
|
concaves du contour de l'objet) ;
|
|
4. assemble tous les nuages exploitables (complet + 5 decimations, x1 ou x2 selon le mode) 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 (et par nuage en
|
|
mode comparaison), 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, jamais committe) a la racine du repo
|
|
fonctionne aussi — `docker compose` le charge automatiquement :
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
# puis editer .env avec un vrai AUTH_PASSWORD_HASH genere via la commande ci-dessus
|
|
```
|
|
|
|
[.env.example](.env.example) est committe et documente toutes les variables disponibles ; `.env`
|
|
(vos vraies valeurs) ne l'est jamais.
|
|
|
|
## 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 le type de comparaison :
|
|
- **Plan de reference Z constant** : un fichier `.las`, `.laz`, `.copc.laz` ou `.bin`.
|
|
- **Comparaison a un second nuage** : deux fichiers — "Surface superieure" (sommet de l'amas) et
|
|
"Limite inferieure" (base/socle de l'amas).
|
|
2. Optionnel (options avancees) : override du plan de reference Z (mode Z constant uniquement, par
|
|
defaut altitude minimale d'un echantillon), du pas spatial initial (par defaut : distance mediane
|
|
au plus proche voisin) et/ou de la distance max de remplissage des trous (par defaut : grille du
|
|
niveau x3 — augmenter si des trous de scan legitimes restent vides, diminuer si des creux du contour
|
|
de l'objet sont a tort combles).
|
|
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 niveaux (points par nuage, volume, surface, % de cellules de grille
|
|
"matching"), cartes de hauteur (par nuage en mode comparaison), 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 sont effectivement comparables. Un niveau est marque "a verifier" des
|
|
que ce taux passe sous 90 % (configurable via `matchingCellsWarnThreshold`, `config.ts`).
|
|
- La decimation s'arrete automatiquement si un niveau (ou l'un des deux nuages en mode comparaison)
|
|
tombe sous 25 points exploitables ; les niveaux suivants sont marques "skipped" avec la raison.
|
|
- **Choix du mode** : le mode "Z constant" n'est fiable que si le nuage uploade ne contient QUE
|
|
l'objet a mesurer. S'il contient aussi du terrain/contexte environnant (scan de site complet par
|
|
exemple), le plan Z constant (altitude minimale de tout le nuage) n'a souvent aucun rapport avec la
|
|
base reelle de l'objet et donne un volume trompeur — utiliser "Comparaison a un second nuage" avec
|
|
une base dediee dans ce cas.
|
|
|
|
## 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.
|