- Backend NestJS orchestrant CloudCompare CLI (stats, decimation x2 sur 5 etapes, volume, assemblage .bin) - Scripts Python (laspy, scipy, matplotlib) pour la conversion LAS/LAZ et la generation d'images - Frontend web simple (Tailwind CDN) avec historique des runs - Image Docker (debian:trixie-slim + cloudcompare apt + xvfb) prete pour deploiement Coolify - Lint/typecheck/tests unitaires integres comme portes de qualite au build Docker - CLAUDE.md documentant les comportements CloudCompare CLI verifies empiriquement
7.4 KiB
Volume 2.5D & robustesse a la decimation
Application locale (Docker) qui, a partir d'un nuage de points (LAS/LAZ/COPC.laz/BIN) :
- calcule son volume en 2.5D via CloudCompare (plan de reference = altitude minimale du nuage) ;
- 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) ;
- recalcule le volume a chaque niveau avec la meme grille et le meme plan de reference, pour rendre les 6 mesures comparables ;
- assemble les 6 nuages (complet + 5 decimations) dans un seul fichier
.binCloudCompare, nomme et consultable dans CloudCompare Desktop ; - produit un dossier de resultats : rapport CSV/JSON, cartes de hauteur par niveau, graphiques de synthese (volume, nombre de points, robustesse) ;
- expose tout ca dans une UI web simple avec historique des runs.
Demarrage
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).
Deploiement Coolify
Le repo est pret pour un deploiement Coolify via webhook (push sur main -> build + deploy auto) :
- Le
Dockerfilefait 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. HEALTHCHECKintegre au Dockerfile (curlsur/) : Coolify l'utilise pour verifier que le deploiement est reellement sain.- Persistance : le volume
/datadoit 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.ymldu repo : le volume nommeapp-datadeclare dedans est deja correct. - Resource "Application" (Dockerfile) : configurer un volume persistant sur
/datadepuis l'onglet "Storage" de Coolify, et le port expose sur3000(variablePORT, lue par l'appli).
- Resource "Docker Compose" dans Coolify pointant sur
- Variables d'environnement optionnelles a definir dans Coolify si besoin de les changer par rapport
aux defauts (voir tableau plus bas) :
MAX_UPLOAD_MB,STATS_SAMPLE_CAP,CC_TIMEOUT_MS. - Le webhook de deploiement automatique est deja configure cote Coolify sur ce repo.
Utilisation
- "Nouveau run" -> choisir un fichier
.las,.laz,.copc.lazou.bin. - 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).
- 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).
- Une fois termine : tableau des 6 niveaux (points, volume, surface, % de cellules de grille
"matching"), cartes de hauteur, graphiques de synthese, et telechargements (
.binfusionne,.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_THRESHOLDcote 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) |
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 aptcloudcompare(2.13.2, evite une compilation depuis les sources) +xvfb(CloudCompare reste une appli Qt, meme en-SILENTelle 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 vialaspy(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.
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 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.