calcul de volume pour margaux automatique
Find a file
Nicolas Fryder c61a6bfea1 Ajoute .env.example pour la config des variables d'environnement
- .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).
2026-07-10 15:37:29 +02:00
docker Implementation initiale : calcul de volume 2.5D CloudCompare et test de robustesse par decimation spatiale progressive 2026-07-10 13:55:25 +02:00
public Mode comparaison a 2 nuages + remplissage de trous + fix critique de parsing 2026-07-10 15:21:37 +02:00
python Mode comparaison a 2 nuages + remplissage de trous + fix critique de parsing 2026-07-10 15:21:37 +02:00
src Mode comparaison a 2 nuages + remplissage de trous + fix critique de parsing 2026-07-10 15:21:37 +02:00
.dockerignore Implementation initiale : calcul de volume 2.5D CloudCompare et test de robustesse par decimation spatiale progressive 2026-07-10 13:55:25 +02:00
.env.example Ajoute .env.example pour la config des variables d'environnement 2026-07-10 15:37:29 +02:00
.gitattributes Implementation initiale : calcul de volume 2.5D CloudCompare et test de robustesse par decimation spatiale progressive 2026-07-10 13:55:25 +02:00
.gitignore Auth HTTP Basic + grille de volume adaptative par niveau 2026-07-10 14:34:27 +02:00
CLAUDE.md Mode comparaison a 2 nuages + remplissage de trous + fix critique de parsing 2026-07-10 15:21:37 +02:00
docker-compose.yml Auth HTTP Basic + grille de volume adaptative par niveau 2026-07-10 14:34:27 +02:00
Dockerfile Auth HTTP Basic + grille de volume adaptative par niveau 2026-07-10 14:34:27 +02:00
eslint.config.mjs Implementation initiale : calcul de volume 2.5D CloudCompare et test de robustesse par decimation spatiale progressive 2026-07-10 13:55:25 +02:00
nest-cli.json Implementation initiale : calcul de volume 2.5D CloudCompare et test de robustesse par decimation spatiale progressive 2026-07-10 13:55:25 +02:00
package-lock.json Auth HTTP Basic + grille de volume adaptative par niveau 2026-07-10 14:34:27 +02:00
package.json Auth HTTP Basic + grille de volume adaptative par niveau 2026-07-10 14:34:27 +02:00
README.md Ajoute .env.example pour la config des variables d'environnement 2026-07-10 15:37:29 +02:00
tsconfig.build.json Implementation initiale : calcul de volume 2.5D CloudCompare et test de robustesse par decimation spatiale progressive 2026-07-10 13:55:25 +02:00
tsconfig.json Implementation initiale : calcul de volume 2.5D CloudCompare et test de robustesse par decimation spatiale progressive 2026-07-10 13:55:25 +02:00

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

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 :

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 :

cp .env.example .env
# puis editer .env avec un vrai AUTH_PASSWORD_HASH genere via la commande ci-dessus

.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.

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.