L'ancienne carte n'est pas une carte à tuiles : une div Leaflet par dalle,
rotée en CSS pour coller la grille Lambert 93 sur le Web Mercator, trois
paliers d'images choisis à la main, un plafond d'images pleine résolution et
une mosaïque d'overview pour boucher les trous au dézoom. Des centaines de
nœuds DOM, des AVIF de 2500² à 5000² décodés dans le navigateur, un LOD
maison — et rien de réutilisable hors de cette page.
Nouvelle image légère (Pillow + pyproj, ni GDAL ni PDAL, port 8975) servant
une pyramide XYZ EPSG:3857 au schéma OpenStreetMap, rendue à la demande
depuis les dalles et mise en cache dans output/index_xyz/ :
- tiles.py : grille XYZ, reprojection par transformation projective dalle par
dalle (calage mesuré < 1 px), choix du palier source parmi ceux que le
pipeline produit déjà (vignette, intermédiaire, quadrant, dalle), cache
périmé dès qu'une dalle contributrice est plus récente, marqueur .empty
pour les zones sans donnée, cache d'images sources à budget mémoire ;
- mapserve.py : /tiles/{couche}/{z}/{x}/{y}.png (256 px canonique) et
@2x.webp (512 px, interface), TileJSON, WMTS, josm.imagery.xml, CORS —
les rendus deviennent un fond d'imagerie pour JOSM, iD, QGIS, uMap ;
- mapui.py : une L.tileLayer par couche dans un pane isolé — LOD, cache et
animation natifs de Leaflet ; pile réordonnable (glisser avec barre
d'insertion, ou boutons ▲▼ au doigt), modes de fusion CSS par couche,
configuration figeable comme défaut de tous les navigateurs.
Deux amonts pour les déploiements en deux machines : LIDAR_SOURCE_URL
(webapp du pipeline — inventaire complet, dalles rapatriées à la demande) et
LIDAR_MAPS_URL (autre instance carte). Charge bornée et réglable, taillée
par défaut pour une petite machine : 2 rendus et 2 téléchargements
simultanés, 192 Mo de cache de sources.
Mesuré sur 849 dalles réelles : première tuile d'une zone 0,9–3,2 s
(téléchargement compris), ~1 ms ensuite ; un chemin OSM se superpose
exactement à la trace du rendu de pente, et les coutures entre tuiles
restent sous le bruit naturel du terrain.
L'interface historique (port 8973) n'est pas touchée : génération et export
y restent, les deux cartes coexistent.
472 lines
22 KiB
Bash
Executable File
472 lines
22 KiB
Bash
Executable File
#!/bin/bash
|
|
# Pipeline LiDAR Archéologique
|
|
# Utilisation: ./run.sh [options]
|
|
#
|
|
# Options:
|
|
# -r RESOLUTION Résolution en m/px, ou multiples séparées par virgules (défaut: 0.5, ex: 0.5,0.2)
|
|
# -w WORKERS Nombre de workers parallèles (défaut: 1)
|
|
# -g [LISTE] Sélectionner le(s) GPU(s) : -g (auto), -g 0, -g 0,2, -g all
|
|
# -v Mode verbeux
|
|
# --debug Mode debug
|
|
# -f / --force Régénérer tous les fichiers
|
|
# --keep-tif Conserver les fichiers TIFF
|
|
# --force-classification
|
|
# --ground-classification {auto,ign,smrf,csf}
|
|
# --ign-classes CLASSES
|
|
# Classes LAS pour le MNT IGN (ex: sol,unclassified ; défaut: sol)
|
|
# --quality N Qualité image 1-100 (défaut: 98)
|
|
# --lossless Compression lossless
|
|
# --format FMT Format de sortie : avif (défaut) ou webp
|
|
# --only VIZ... Générer uniquement ces visualisations
|
|
# --skip VIZ... Exclure ces visualisations
|
|
# --file NOM... Traiter un ou plusieurs fichiers LAZ
|
|
# --test Exécuter les tests unitaires
|
|
# --serve [PORT] Carte + API de génération (image complète, défaut: 8973)
|
|
# --serve-webapp [PORT]
|
|
# Conteneur webapp seul (image légère) : carte servie depuis
|
|
# le cache local de tuiles, génération déléguée à la machine
|
|
# de traitement (LIDAR_GENERATION_URL), tuiles à la demande
|
|
# -h Afficher l'aide complète
|
|
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
INPUT_DIR="${SCRIPT_DIR}/input"
|
|
OUTPUT_DIR="${SCRIPT_DIR}/output"
|
|
IMAGE_NAME="lidar-lidar"
|
|
|
|
# Afficher l'aide si aucun argument
|
|
if [ $# -eq 0 ]; then
|
|
echo "Pipeline LiDAR Archéologique"
|
|
echo ""
|
|
echo "Usage: $0 [options]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " -r RESOLUTION Résolution en m/px, ou multiples (défaut: 0.5, ex: 0.5,0.2)"
|
|
echo " -w WORKERS Nombre de workers CPU parallèles (défaut: 1)"
|
|
echo " -g [LISTE] Sélectionner le(s) GPU(s) : -g (auto), -g 0, -g 0,2, -g all"
|
|
echo " -v Mode verbeux (timestamps + niveaux)"
|
|
echo " --debug Mode debug (détails internes fichier:ligne)"
|
|
echo " -f / --force Régénérer tous les fichiers même si les AVIF existent"
|
|
echo " --force-classification"
|
|
echo " Reclassifier le sol même si le fichier .las existe"
|
|
echo " --keep-tif Conserver les TIFF pour régénérer les AVIF"
|
|
echo " --ground-classification {auto,ign,smrf,csf}"
|
|
echo " Méthode de classification du sol (défaut: auto = pré-classification IGN)"
|
|
echo " --ign-classes CLASSES"
|
|
echo " Classes LAS pour le MNT IGN : sol,unclassified,eau,virtuel,pont,sursol"
|
|
echo " ou codes (ex: 2,1). Mode pur, aucune retouche. (défaut: sol)"
|
|
echo " --quality N Qualité image 1-100 (défaut: 98, 100=lossless)"
|
|
echo " --lossless Compression lossless (équivalent à --quality 100)"
|
|
echo " --format FMT Format de sortie : avif (défaut) ou webp"
|
|
echo " --only VIZ... Générer uniquement ces visualisations"
|
|
echo " --skip VIZ... Exclure ces visualisations"
|
|
echo " --file NOM... Traiter un ou plusieurs fichiers LAZ (nom complet sans .laz/.las)"
|
|
echo " --test Exécuter les tests unitaires"
|
|
echo " -h Afficher cette aide"
|
|
echo ""
|
|
echo "Visualisations disponibles:"
|
|
echo " hillshade slope mslrm sailore pos_open neg_open"
|
|
echo " svf aniso_open roughness wavelet flow_acc"
|
|
echo " ortho topo"
|
|
echo ""
|
|
echo "Exemples:"
|
|
echo " $0 -g # GPU auto (premier disponible)"
|
|
echo " $0 -g 0 # GPU index 0"
|
|
echo " $0 -g 0,2 # GPUs 0 et 2"
|
|
echo " $0 -g all # Tous les GPUs"
|
|
echo " $0 -g -w 4 # GPU + 4 workers"
|
|
echo " $0 -g -r 0.2 # Haute résolution"
|
|
echo " $0 -g -r 0.5,0.2 # Multi-résolution (0.5m + 0.2m)"
|
|
echo " $0 -g --force # Régénérer AVIF (DTM conservé si --keep-tif)"
|
|
echo " $0 -g --force-classification # Reclassifier le sol seulement"
|
|
echo " $0 -g --ground-classification csf # Forcer CSF (terrain complexe)"
|
|
echo " $0 -g --file LHD_...IGN69.copc # Un fichier"
|
|
exit 0
|
|
fi
|
|
|
|
RESOLUTION=0.5
|
|
WORKERS=1
|
|
GPU_FLAG=""
|
|
GPU_ARG=""
|
|
VERBOSE_FLAG=""
|
|
FORCE_FLAG=""
|
|
FILE_ARGS=""
|
|
GROUND_METHOD=""
|
|
IGN_CLASSES_FLAG=""
|
|
FORCE_CLASSIFY_FLAG=""
|
|
KEEP_TIF_FLAG=""
|
|
QUALITY=""
|
|
FORMAT_FLAG=""
|
|
ONLY_FLAG=""
|
|
SKIP_FLAG=""
|
|
TEST_FLAG=0
|
|
REBUILD_INDEX_FLAG=""
|
|
NO_INDEX_FLAG=""
|
|
FETCH_TILES_ARGS=""
|
|
SERVE_FLAG=0
|
|
SERVE_PORT=8973
|
|
WEBAPP_FLAG=0
|
|
WEBAPP_SERVE_PORT=8973
|
|
WEBAPP_IMAGE="lidar-webapp"
|
|
MAPS_FLAG=0
|
|
MAPS_SERVE_PORT=8975
|
|
MAPS_IMAGE="lidar-maps"
|
|
|
|
# Parse arguments manually (more robust than getopts for mixed short/long options)
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
-r) RESOLUTION="$2"; shift 2 ;;
|
|
-w) WORKERS="$2"; shift 2 ;;
|
|
-g)
|
|
GPU_FLAG="--gpus all"
|
|
# Check if next arg is a GPU selector (not another flag)
|
|
if [ $# -gt 1 ] && [[ ! "$2" =~ ^- ]]; then
|
|
GPU_ARG="$2"
|
|
GPU_FLAG="--gpus all"
|
|
shift 2
|
|
else
|
|
shift
|
|
fi
|
|
;;
|
|
-v) VERBOSE_FLAG="-v"; shift ;;
|
|
-f) FORCE_FLAG="--force"; shift ;;
|
|
--debug) VERBOSE_FLAG="--debug"; shift ;;
|
|
--force) FORCE_FLAG="--force"; shift ;;
|
|
--force-classification) FORCE_CLASSIFY_FLAG="--force-classification"; shift ;;
|
|
--keep-tif) KEEP_TIF_FLAG="--keep-tif"; shift ;;
|
|
--ground-classification) GROUND_METHOD="$2"; shift 2 ;;
|
|
--ground-classification=*) GROUND_METHOD="${1#--ground-classification=}"; shift ;;
|
|
--ign-classes) IGN_CLASSES_FLAG="--ign-classes $2"; shift 2 ;;
|
|
--ign-classes=*) IGN_CLASSES_FLAG="--ign-classes=${1#--ign-classes=}"; shift ;;
|
|
--quality) QUALITY="--quality $2"; shift 2 ;;
|
|
--lossless) QUALITY="--lossless"; shift ;;
|
|
--format) FORMAT_FLAG="--format $2"; shift 2 ;;
|
|
--only) shift; ONLY_FLAG="--only"; while [ $# -gt 0 ] && [[ ! "$1" =~ ^- ]]; do ONLY_FLAG="$ONLY_FLAG $1"; shift; done ;;
|
|
--skip) shift; SKIP_FLAG="--skip"; while [ $# -gt 0 ] && [[ ! "$1" =~ ^- ]]; do SKIP_FLAG="$SKIP_FLAG $1"; shift; done ;;
|
|
--file) shift; while [ $# -gt 0 ] && [[ ! "$1" =~ ^- ]]; do FILE_ARGS="$FILE_ARGS $1"; shift; done ;;
|
|
--rebuild-index) REBUILD_INDEX_FLAG="--rebuild-index"; shift ;;
|
|
--no-index) NO_INDEX_FLAG="--no-index"; shift ;;
|
|
--fetch-tiles) shift; while [ $# -gt 0 ] && [[ ! "$1" =~ ^- ]]; do FETCH_TILES_ARGS="$FETCH_TILES_ARGS $1"; shift; done ;;
|
|
--test) TEST_FLAG=1; shift ;;
|
|
--serve)
|
|
SERVE_FLAG=1; shift
|
|
if [ $# -gt 0 ] && [[ ! "$1" =~ ^- ]]; then SERVE_PORT="$1"; shift; fi
|
|
;;
|
|
--serve-webapp)
|
|
WEBAPP_FLAG=1; shift
|
|
if [ $# -gt 0 ] && [[ ! "$1" =~ ^- ]]; then WEBAPP_SERVE_PORT="$1"; shift; fi
|
|
;;
|
|
--serve-maps)
|
|
MAPS_FLAG=1; shift
|
|
if [ $# -gt 0 ] && [[ ! "$1" =~ ^- ]]; then MAPS_SERVE_PORT="$1"; shift; fi
|
|
;;
|
|
-h|--help|-help)
|
|
echo "Pipeline LiDAR Archéologique"
|
|
echo ""
|
|
echo "Usage: $0 [options]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " -r RESOLUTION Résolution en m/px (défaut: 0.5)"
|
|
echo " -w WORKERS Nombre de workers CPU parallèles (défaut: 1)"
|
|
echo " -g [LISTE] Sélectionner le(s) GPU(s) : -g (auto), -g 0, -g 0,2, -g all"
|
|
echo " -v Mode verbeux (timestamps + niveaux)"
|
|
echo " --debug Mode debug (détails internes fichier:ligne)"
|
|
echo " -f / --force Régénérer tous les fichiers même si les AVIF existent"
|
|
echo " --force-classification"
|
|
echo " Reclassifier le sol même si le fichier .las existe"
|
|
echo " --keep-tif Conserver les TIFF pour régénérer les AVIF"
|
|
echo " --ground-classification {auto,ign,smrf,csf}"
|
|
echo " Méthode de classification du sol (défaut: auto = pré-classification IGN)"
|
|
echo " --ign-classes CLASSES"
|
|
echo " Classes LAS pour le MNT IGN : sol,unclassified,eau,virtuel,pont,sursol"
|
|
echo " ou codes (ex: 2,1). Mode pur, aucune retouche. (défaut: sol)"
|
|
echo " --quality N Qualité image 1-100 (défaut: 98, 100=lossless)"
|
|
echo " --lossless Compression lossless (équivalent à --quality 100)"
|
|
echo " --format FMT Format de sortie : avif (défaut) ou webp"
|
|
echo " --only VIZ... Générer uniquement ces visualisations"
|
|
echo " --skip VIZ... Exclure ces visualisations"
|
|
echo " --file NOM... Traiter un ou plusieurs fichiers LAZ"
|
|
echo " --fetch-tiles COL,ROW..."
|
|
echo " Télécharger ces dalles depuis l'IGN puis les traiter"
|
|
echo " --serve [PORT] Servir la carte + API de génération de zones (défaut: 8973)"
|
|
echo " --serve-webapp [PORT]"
|
|
echo " Conteneur webapp seul (image légère, sans PDAL/GPU) :"
|
|
echo " carte servie depuis le cache local de tuiles, génération"
|
|
echo " déléguée via LIDAR_GENERATION_URL (tuiles à la demande)"
|
|
echo " (cf. docs/DEPLOY_WEBAPP.md)"
|
|
echo " --serve-maps [PORT]"
|
|
echo " Carte à tuiles XYZ (image légère lidar-maps, défaut 8975) :"
|
|
echo " schéma OpenStreetMap, réutilisable dans JOSM/QGIS/iD"
|
|
echo " (cf. docs/MAPS.md)"
|
|
echo " --test Exécuter les tests unitaires"
|
|
echo " -h Afficher cette aide"
|
|
echo ""
|
|
echo "Visualisations disponibles:"
|
|
echo " hillshade slope mslrm sailore pos_open neg_open"
|
|
echo " svf aniso_open roughness wavelet flow_acc"
|
|
echo " ortho topo"
|
|
echo ""
|
|
echo "Exemples:"
|
|
echo " $0 -g # GPU auto (premier disponible)"
|
|
echo " $0 -g 0 # GPU index 0"
|
|
echo " $0 -g 0,2 # GPUs 0 et 2"
|
|
echo " $0 -g all # Tous les GPUs"
|
|
echo " $0 -g -w 4 # GPU + 4 workers"
|
|
echo " $0 -g -v # GPU + verbeux"
|
|
echo " $0 -g -r 0.2 # Haute résolution"
|
|
echo " $0 -g -r 0.5,0.2 # Multi-résolution (0.5m + 0.2m)"
|
|
echo " $0 -g --force # Régénérer AVIF"
|
|
echo " $0 -g --only hillshade svf # Seulement 2 visualisations"
|
|
echo " $0 -g --skip ortho topo # Sans les overlays IGN"
|
|
echo " $0 -g --lossless # AVIF lossless"
|
|
echo " $0 -g --quality 90 # AVIF qualité 90"
|
|
echo " $0 -g --ground-classification csf # Forcer CSF (terrain complexe)"
|
|
echo " $0 -g --file LHD_...IGN69.copc # Un fichier"
|
|
exit 0
|
|
;;
|
|
*) echo "Option invalide: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
# Trim FILE_ARGS whitespace
|
|
FILE_ARGS=$(echo "$FILE_ARGS" | xargs)
|
|
FETCH_TILES_ARGS=$(echo "$FETCH_TILES_ARGS" | xargs)
|
|
|
|
# input/ en lecture seule sauf si --fetch-tiles (téléchargement IGN dans input/)
|
|
INPUT_RO=":ro"
|
|
if [ -n "$FETCH_TILES_ARGS" ]; then
|
|
INPUT_RO=""
|
|
fi
|
|
|
|
# Build systématique de l'image avant tout docker run. Le cache Docker rend
|
|
# l'opération quasi instantanée si le code n'a pas changé ; en cas d'échec,
|
|
# le journal complet est affiché. Ça évite de lancer tests/serveur/pipeline
|
|
# sur une image périmée (code baké à la dernière édition).
|
|
build_image() {
|
|
local log="/tmp/lidar_build_$$.log"
|
|
echo "Mise à jour de l'image Docker (cache)..."
|
|
if ! docker build -t "$IMAGE_NAME" "$SCRIPT_DIR" >"$log" 2>&1; then
|
|
cat "$log" >&2
|
|
echo "Échec du build de l'image (journal : $log)" >&2
|
|
exit 1
|
|
fi
|
|
rm -f "$log"
|
|
}
|
|
|
|
# Image webapp légère (conteneur webapp seul — cf. Dockerfile.webapp)
|
|
build_webapp_image() {
|
|
local log="/tmp/lidar_webapp_build_$$.log"
|
|
echo "Mise à jour de l'image Docker webapp (cache)..."
|
|
if ! docker build -f Dockerfile.webapp -t "$WEBAPP_IMAGE" "$SCRIPT_DIR" >"$log" 2>&1; then
|
|
cat "$log" >&2
|
|
echo "Échec du build de l'image webapp (journal : $log)" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Image carte à tuiles XYZ (cf. Dockerfile.maps)
|
|
build_maps_image() {
|
|
local log="/tmp/lidar_maps_build_$$.log"
|
|
echo "Mise à jour de l'image Docker carte (cache)..."
|
|
if ! docker build -f Dockerfile.maps -t "$MAPS_IMAGE" "$SCRIPT_DIR" >"$log" 2>&1; then
|
|
cat "$log" >&2
|
|
echo "Échec du build de l'image carte (journal : $log)" >&2
|
|
exit 1
|
|
fi
|
|
rm -f "$log"
|
|
}
|
|
|
|
# Check for --test flag first
|
|
if [ "$TEST_FLAG" -eq 1 ]; then
|
|
build_image
|
|
echo "============================================"
|
|
echo " Tests unitaires LiDAR Pipeline"
|
|
echo "============================================"
|
|
docker run --rm --init $GPU_FLAG \
|
|
"$IMAGE_NAME" \
|
|
python3 -m pytest -v --pyargs lidar_pipeline.tests
|
|
exit $?
|
|
fi
|
|
|
|
build_image
|
|
|
|
# Créer les répertoires s'ils n'existent pas
|
|
mkdir -p "$INPUT_DIR" "$OUTPUT_DIR"
|
|
|
|
# Mode --serve : carte + API de génération (input/ en écriture)
|
|
if [ "$SERVE_FLAG" -eq 1 ]; then
|
|
echo "============================================"
|
|
echo " Carte LiDAR + API de génération"
|
|
echo "============================================"
|
|
echo " http://127.0.0.1:${SERVE_PORT}/"
|
|
echo " GPU : $([ -n "$GPU_FLAG" ] && echo 'OUI' || echo 'non')"
|
|
echo " Bouton « + Zone » : dessiner un rectangle → téléchargement IGN + rendu"
|
|
echo "============================================"
|
|
GPU_ENV_FLAG=""
|
|
[ -n "$GPU_FLAG" ] && GPU_ENV_FLAG="-e LIDAR_GPU=1"
|
|
if [ -n "$GPU_ARG" ] && [ "$GPU_ARG" != "all" ]; then
|
|
GPU_ENV_FLAG="-e LIDAR_GPU=1 -e CUDA_VISIBLE_DEVICES=${GPU_ARG} --gpus '""device=${GPU_ARG}""'"
|
|
fi
|
|
# Réseau autorisé à lancer les générations (/api/generate) : la valeur
|
|
# vient de l'environnement hôte LIDAR_REGEN_CIDR ; sans elle, le défaut
|
|
# de la webapp s'applique (localhost + RFC1918). Chaîne VIDE pour lever
|
|
# complètement la restriction.
|
|
REGEN_ENV_FLAG="-e LIDAR_REGEN_CIDR=${LIDAR_REGEN_CIDR-127.0.0.0/8,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16}"
|
|
exec docker run --rm --init $GPU_FLAG \
|
|
--user 1000:1000 \
|
|
-p "${SERVE_PORT}:8973" \
|
|
-v "${INPUT_DIR}:/data/input" \
|
|
-v "${OUTPUT_DIR}:/data/output" \
|
|
-e LIDAR_INPUT_DIR=/data/input -e LIDAR_OUTPUT_DIR=/data/output \
|
|
$GPU_ENV_FLAG $REGEN_ENV_FLAG \
|
|
"$IMAGE_NAME" \
|
|
python3 -m uvicorn lidar_pipeline.webapp:app --host 0.0.0.0 --port 8973
|
|
fi
|
|
|
|
# Mode --serve-maps : carte à tuiles XYZ (image légère lidar-maps).
|
|
# Les tuiles suivent le schéma OpenStreetMap ({z}/{x}/{y}.png) : la carte est
|
|
# consultable ici et les mêmes URLs servent de fond d'imagerie dans JOSM, iD,
|
|
# QGIS ou uMap. Variables optionnelles depuis l'environnement hôte :
|
|
# LIDAR_MAPS_URL serveur de tuiles amont (carte déportée)
|
|
# LIDAR_ATTRIBUTION mention d'attribution servie aux clients
|
|
if [ "$MAPS_FLAG" -eq 1 ]; then
|
|
build_maps_image
|
|
mkdir -p "$OUTPUT_DIR"
|
|
echo "============================================"
|
|
echo " Carte LiDAR — tuiles XYZ (image légère)"
|
|
echo "============================================"
|
|
echo " Carte : http://127.0.0.1:${MAPS_SERVE_PORT}/"
|
|
echo " Tuiles XYZ : http://127.0.0.1:${MAPS_SERVE_PORT}/tiles/{couche}/{z}/{x}/{y}.png"
|
|
echo " JOSM / QGIS : /tiles/josm.imagery.xml, /tiles/wmts.xml"
|
|
echo " Amont : ${LIDAR_MAPS_URL:-aucun (rendu local)}"
|
|
echo "============================================"
|
|
MAPS_ENV=()
|
|
[ -n "${LIDAR_MAPS_URL:-}" ] && MAPS_ENV+=(-e LIDAR_MAPS_URL="$LIDAR_MAPS_URL")
|
|
[ -n "${LIDAR_ATTRIBUTION:-}" ] && MAPS_ENV+=(-e LIDAR_ATTRIBUTION="$LIDAR_ATTRIBUTION")
|
|
exec docker run --rm --init \
|
|
--user 1000:1000 \
|
|
-p "${MAPS_SERVE_PORT}:8975" \
|
|
-v "${OUTPUT_DIR}:/data/output" \
|
|
-e LIDAR_OUTPUT_DIR=/data/output \
|
|
"${MAPS_ENV[@]}" \
|
|
"$MAPS_IMAGE"
|
|
fi
|
|
|
|
# Mode --serve-webapp : conteneur webapp SEUL (image légère, sans PDAL/GPU).
|
|
# La carte est servie depuis le cache local de tuiles (output/) ; la
|
|
# génération est déléguée à la machine de traitement et les tuiles sont
|
|
# servies à la demande depuis LIDAR_GENERATION_URL (plus de rsync).
|
|
# Variables passées telles quelles depuis l'environnement hôte :
|
|
# LIDAR_GENERATION_URL machine de traitement (ex: http://192.168.1.50:8973)
|
|
# LIDAR_REMOTE_TOKEN jeton si la machine exige LIDAR_API_TOKEN
|
|
# LIDAR_REGEN_CIDR réseau autorisé à lancer les générations
|
|
# (défaut 192.168.1.0/24, vide = restriction levée)
|
|
if [ "$WEBAPP_FLAG" -eq 1 ]; then
|
|
build_webapp_image
|
|
mkdir -p "$OUTPUT_DIR"
|
|
echo "============================================"
|
|
echo " Carte LiDAR — webapp seule (image légère)"
|
|
echo "============================================"
|
|
echo " http://127.0.0.1:${WEBAPP_SERVE_PORT}/"
|
|
echo " Génération distante : ${LIDAR_GENERATION_URL:-non configurée (LIDAR_GENERATION_URL)}"
|
|
echo " Cache local tuiles : à la demande (LIDAR_GENERATION_URL)"
|
|
echo "============================================"
|
|
WEBAPP_ENV=()
|
|
[ -n "$LIDAR_GENERATION_URL" ] && WEBAPP_ENV+=(-e LIDAR_GENERATION_URL="$LIDAR_GENERATION_URL")
|
|
[ -n "$LIDAR_REMOTE_TOKEN" ] && WEBAPP_ENV+=(-e LIDAR_REMOTE_TOKEN="$LIDAR_REMOTE_TOKEN")
|
|
[ -n "$LIDAR_API_TOKEN" ] && WEBAPP_ENV+=(-e LIDAR_API_TOKEN="$LIDAR_API_TOKEN")
|
|
[ -n "${LIDAR_REGEN_CIDR:-}" ] && WEBAPP_ENV+=(-e LIDAR_REGEN_CIDR="$LIDAR_REGEN_CIDR")
|
|
# Clé SSH du hôte (déploiement, git pull distant) : montée en lecture seule
|
|
# si présente. LIDAR_MOUNT_SSH=0 pour ne PAS l'exposer au conteneur.
|
|
WEBAPP_SSH=()
|
|
[ "${LIDAR_MOUNT_SSH:-1}" = "1" ] && [ -d "${HOME}/.ssh" ] && WEBAPP_SSH+=(-v "${HOME}/.ssh:/home/lidar/.ssh:ro")
|
|
exec docker run --rm --init \
|
|
--user 1000:1000 \
|
|
-p "${WEBAPP_SERVE_PORT}:8973" \
|
|
-v "${OUTPUT_DIR}:/data/output" \
|
|
"${WEBAPP_SSH[@]}" \
|
|
-e LIDAR_OUTPUT_DIR=/data/output \
|
|
"${WEBAPP_ENV[@]}" \
|
|
"$WEBAPP_IMAGE"
|
|
fi
|
|
|
|
# Lancer le pipeline
|
|
echo "============================================"
|
|
echo " Pipeline LiDAR Archéologique"
|
|
echo "============================================"
|
|
echo " Résolution : ${RESOLUTION}m/px"
|
|
echo " Workers : ${WORKERS}"
|
|
echo " GPU : $([ -n "$GPU_FLAG" ] && echo "OUI${GPU_ARG:+ ($GPU_ARG)}" || echo 'non')"
|
|
echo " Verbeux : $([ -n "$VERBOSE_FLAG" ] && echo 'OUI' || echo 'non')"
|
|
echo " Force : $([ -n "$FORCE_FLAG" ] && echo 'OUI' || echo 'non')"
|
|
echo " Force classif.: $([ -n "$FORCE_CLASSIFY_FLAG" ] && echo 'OUI' || echo 'non')"
|
|
echo " Keep TIFF : $([ -n "$KEEP_TIF_FLAG" ] && echo 'OUI' || echo 'non')"
|
|
echo " Qualité image : $([ -n "$QUALITY" ] && echo "$QUALITY" || echo '98')"
|
|
echo " Format : $([ -n "$FORMAT_FLAG" ] && echo "${FORMAT_FLAG#--format }" || echo 'avif')"
|
|
echo " Classification sol : $([ -n "$GROUND_METHOD" ] && echo "$GROUND_METHOD" || echo 'auto')$([ -n "$IGN_CLASSES_FLAG" ] && echo " ${IGN_CLASSES_FLAG#--ign-classes }")"
|
|
if [ -n "$ONLY_FLAG" ]; then
|
|
echo " Visualisations: uniquement${ONLY_FLAG#--only}"
|
|
elif [ -n "$SKIP_FLAG" ]; then
|
|
echo " Visualisations: tout sauf${SKIP_FLAG#--skip}"
|
|
fi
|
|
if [ -n "$FILE_ARGS" ]; then
|
|
echo " Fichiers :${FILE_ARGS}"
|
|
fi
|
|
if [ -n "$FETCH_TILES_ARGS" ]; then
|
|
echo " Dalles IGN :${FETCH_TILES_ARGS} (input/ monté en écriture)"
|
|
fi
|
|
echo "============================================"
|
|
|
|
CMD_ARGS="-o /data/output -r $RESOLUTION -w $WORKERS $VERBOSE_FLAG $FORCE_FLAG $FORCE_CLASSIFY_FLAG $KEEP_TIF_FLAG $QUALITY $FORMAT_FLAG"
|
|
if [ -n "$GPU_ARG" ]; then
|
|
CMD_ARGS="$CMD_ARGS -g $GPU_ARG"
|
|
fi
|
|
if [ -z "$GPU_ARG" ] && [ -n "$GPU_FLAG" ]; then
|
|
CMD_ARGS="$CMD_ARGS -g"
|
|
fi
|
|
if [ -n "$GROUND_METHOD" ]; then
|
|
CMD_ARGS="$CMD_ARGS --ground-classification $GROUND_METHOD"
|
|
fi
|
|
if [ -n "$IGN_CLASSES_FLAG" ]; then
|
|
CMD_ARGS="$CMD_ARGS $IGN_CLASSES_FLAG"
|
|
fi
|
|
if [ -n "$ONLY_FLAG" ]; then
|
|
CMD_ARGS="$CMD_ARGS $ONLY_FLAG"
|
|
fi
|
|
if [ -n "$SKIP_FLAG" ]; then
|
|
CMD_ARGS="$CMD_ARGS $SKIP_FLAG"
|
|
fi
|
|
if [ -n "$FILE_ARGS" ]; then
|
|
CMD_ARGS="$CMD_ARGS --file $FILE_ARGS"
|
|
fi
|
|
if [ -n "$REBUILD_INDEX_FLAG" ]; then
|
|
CMD_ARGS="$CMD_ARGS $REBUILD_INDEX_FLAG"
|
|
fi
|
|
if [ -n "$NO_INDEX_FLAG" ]; then
|
|
CMD_ARGS="$CMD_ARGS $NO_INDEX_FLAG"
|
|
fi
|
|
if [ -n "$FETCH_TILES_ARGS" ]; then
|
|
CMD_ARGS="$CMD_ARGS --fetch-tiles $FETCH_TILES_ARGS"
|
|
fi
|
|
|
|
# Build CUDA_VISIBLE_DEVICES env var from GPU_ARG
|
|
CUDA_ENV_FLAG=""
|
|
if [ -n "$GPU_ARG" ]; then
|
|
# Convert GPU_ARG (e.g. "0,2" or "1" or "all") to CUDA_VISIBLE_DEVICES
|
|
if [ "$GPU_ARG" = "all" ]; then
|
|
CUDA_VISIBLE="0,1,2,3"
|
|
else
|
|
CUDA_VISIBLE="$GPU_ARG"
|
|
fi
|
|
CUDA_ENV_FLAG="-e CUDA_VISIBLE_DEVICES=${CUDA_VISIBLE}"
|
|
fi
|
|
|
|
docker run --rm --init $GPU_FLAG $CUDA_ENV_FLAG \
|
|
--user 1000:1000 \
|
|
-v "${INPUT_DIR}:/data/input${INPUT_RO}" \
|
|
-v "${OUTPUT_DIR}:/data/output" \
|
|
"$IMAGE_NAME" \
|
|
python3 -m lidar_pipeline /data/input \
|
|
$CMD_ARGS
|