Rewrite README and docs in English for GitHub, add MIT license, remove internal files

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Antoine Jacquin
2026-09-27 22:31:54 +02:00
parent cef070ad77
commit d3704d6714
11 changed files with 810 additions and 923 deletions

View File

@ -1,104 +1,103 @@
# Déploiement carte légère + machine de traitement
# Two-machine deployment: lightweight map + processing worker
Architecture deux machines : la **carte** (interface, pyramide de tuiles XYZ,
API) tourne sur un Raspberry Pi ; la **génération de tuiles** (téléchargement
IGN, PDAL, GPU) tourne sur une machine puissante. Les deux exécutent le même
serveur (`lidar_pipeline.mapserve`, image `lidar-maps`), la machine puissante
en version complète (pipeline inclus).
Two-machine architecture: the **map** (interface, XYZ tile pyramid, API) runs
on a Raspberry Pi; **tile generation** (IGN downloads, PDAL, GPU) runs on a
powerful machine. Both run the same server (`lidar_pipeline.mapserve`, image
`lidar-maps`), the powerful machine in its full version (pipeline included).
```
Navigateur ──HTTP──▶ Raspberry Pi (image légère lidar-maps, port 8975)
│ sert la carte + la pyramide (cache seule + maintenance
│ de fond, LIDAR_TILE_CACHE_ONLY / LIDAR_TILE_BACKGROUND)
│ /api/generate, /api/preview, /api/status
│ └─ transmis à ──▶ machine de traitement
│ dalles rapatriées du worker (LIDAR_SOURCE_URL :
│ inventaire /api/tiles + statiques versionnées)
▼
Machine de traitement (image complète,
docker-compose.worker.yml service `worker`) :
téléchargement IGN + pipeline GPU (générateur de tuiles)
+ pyramide de tuiles + inventaire des dalles
Browser ──HTTP──▶ Raspberry Pi (lightweight lidar-maps image, port 8975)
│ serves the map + the pyramid (cache-only + background
│ maintenance, LIDAR_TILE_CACHE_ONLY / LIDAR_TILE_BACKGROUND)
│ /api/generate, /api/preview, /api/status
│ └─ forwarded to ──▶ processing worker
│ tiles fetched back from the worker (LIDAR_SOURCE_URL:
│ /api/tiles inventory + versioned static assets)
▼
Processing worker (full image,
docker-compose.worker.yml service `worker`):
IGN download + GPU pipeline (tile generator)
+ tile pyramid + tile inventory
```
## Machine de traitement (générateur de tuiles)
See also `docs/MAPS.md` for the map/tile-pyramid internals referenced below.
Le service `worker` joue le rôle de générateur : il accepte les demandes de
génération envoyées par les cartes distantes (téléchargement IGN + traitement
PDAL/GPU), sert sa propre pyramide de tuiles et l'inventaire des dalles
(`/api/tiles` + `visualisations/`, `index_thumbs/`, `index_subtiles/` en
statique) que les machines légères rapatrient à la demande.
## Processing worker (tile generator)
The `worker` service acts as the generator: it accepts generation requests
sent by remote maps (IGN download + PDAL/GPU processing), serves its own tile
pyramid and tile inventory (`/api/tiles` + static `visualisations/`,
`index_thumbs/`, `index_subtiles/`) that lightweight machines fetch on demand.
```bash
docker compose -f docker-compose.worker.yml up -d --build # API sur http://<ip-machine>:8973
docker compose -f docker-compose.worker.yml up -d --build # API at http://<worker-ip>:8973
docker compose -f docker-compose.worker.yml logs -f worker
```
Traitement batch ponctuel des dalles déjà présentes dans `input/` :
One-off batch processing of tiles already present in `input/`:
```bash
docker compose -f docker-compose.worker.yml run --rm --build process [-r 0.2 | --force]
```
Sur une machine sans GPU : retirer les lignes `gpus: all` (traitement CPU,
plus lent). Optionnel mais recommandé si le réseau n'est pas de confiance :
protéger l'API avec un jeton partagé — décommenter dans
`docker-compose.worker.yml` :
On a machine without a GPU: remove the `gpus: all` lines (CPU processing,
slower). Optional but recommended if the network isn't trusted: protect the
API with a shared token — uncomment in `docker-compose.worker.yml`:
```yaml
environment:
- LIDAR_API_TOKEN=un-secret-à-partager
- LIDAR_API_TOKEN=a-secret-to-share
```
## Raspberry Pi (carte légère)
## Raspberry Pi (lightweight map)
### 0. Prérequis sur le Pi
### 0. Prerequisites on the Pi
- **Architecture** : image construite nativement sur la machine (ARM64 ou
x86_64, vérifier avec `uname -m`). Le build se fait sur le Pi lui-même
(`Dockerfile.maps` : base `python:3.12-slim`, ~200 Mo, sans PDAL/GDAL).
- **Docker + plugin compose** : installation officielle
- **Architecture**: the image is built natively on the machine (ARM64 or
x86_64, check with `uname -m`). The build happens on the Pi itself
(`Dockerfile.maps`: `python:3.12-slim` base, ~200 MB, no PDAL/GDAL).
- **Docker + compose plugin**: official install
[docs.docker.com/engine/install](https://docs.docker.com/engine/install/)
(tester avec `docker compose version`).
- **Espace disque** : prévoir la taille du cache (dalles rapatriées + tuiles
rendues ; compter la taille de `output/` sur la machine de traitement).
(verify with `docker compose version`).
- **Disk space**: plan for the cache size (fetched tiles + rendered tiles;
use the size of `output/` on the processing machine as a reference).
### 1. Copier le code sur le Pi (git clone)
### 1. Copy the code onto the Pi (git clone)
```bash
git clone ssh://git@git.example.fr:2222/code_public/lidar_rendu.git lidar
git clone https://github.com/<you>/lidar_rendu.git lidar
cd lidar
```
`output/` et `input/` sont ignorés par git : le dépôt ne contient que le
code, le cache se remplit à la demande depuis la machine de traitement.
`output/` and `input/` are git-ignored: the repository only holds the code,
the cache fills on demand from the processing machine.
### 2. Lancer la carte
### 2. Start the map
`docker-compose.maps.yml` (versionné) sert de base ; la configuration locale
du Pi vit dans un **override** `docker-compose.maps.override.yml` (non
versionné) :
`docker-compose.maps.yml` (versioned) serves as the base; the Pi's local
configuration lives in an **override** file `docker-compose.maps.override.yml`
(not versioned):
```yaml
name: lidar-maps
services:
maps:
volumes: !override # Compose >= 2.24 (remplace ./output)
volumes: !override # Compose >= 2.24 (replaces ./output)
- /srv/lidar/output:/data/output
mem_limit: 1g
memswap_limit: 1g
environment:
- TZ=Europe/Paris
- LIDAR_SOURCE_URL=http://192.168.1.50:8973 # worker (dalles + inventaire)
- LIDAR_GENERATION_URL=http://192.168.1.50:8973 # worker (génération déléguée)
# - LIDAR_REMOTE_TOKEN=un-secret-à-partager # si LIDAR_API_TOKEN côté worker
- LIDAR_SOURCE_URL=http://192.168.1.50:8973 # worker (tiles + inventory)
- LIDAR_GENERATION_URL=http://192.168.1.50:8973 # worker (delegated generation)
# - LIDAR_REMOTE_TOKEN=a-secret-to-share # if LIDAR_API_TOKEN is set on the worker
- LIDAR_TILE_WORKERS=1
- LIDAR_TILE_SOURCE_CACHE_MB=64
- LIDAR_TILE_CACHE_ONLY=1 # la navigation ne rend RIEN
- LIDAR_TILE_BACKGROUND=1 # la pyramide est entretenue en tâche de fond
- LIDAR_TILE_CACHE_ONLY=1 # browsing renders NOTHING
- LIDAR_TILE_BACKGROUND=1 # the pyramid is maintained as a background task
- LIDAR_TILE_BACKGROUND_PAUSE=1.0
networks: [webapp, proxy]
labels: # routage Traefik éventuel
labels: # optional Traefik routing
- "traefik.enable=true"
- "traefik.http.routers.lidar-maps.rule=Host(`lidar.example.fr`)"
- "traefik.http.routers.lidar-maps.entrypoints=websecure"
@ -115,76 +114,60 @@ networks:
```
```bash
mkdir -p /srv/lidar/output # appartenant à l'uid 1000 (conteneur)
mkdir -p /srv/lidar/output # owned by uid 1000 (container)
docker compose -f docker-compose.maps.yml -f docker-compose.maps.override.yml up -d --build
```
La carte est sur `http://<ip-pi>:8975/` (et via Traefik si configuré).
The map is available at `http://<pi-host>:8975/` (and via Traefik if
configured).
`LIDAR_TILE_CACHE_ONLY=1` + `LIDAR_TILE_BACKGROUND=1` inversent la charge sur
un petit Pi : la navigation ne rend rien (tuile absente = transparente,
`X-Tile-Pending`), une tâche de fond surveille les dalles nouvelles ou
régénérées et entretient la pyramide à basse priorité — cf. `docs/MAPS.md`
§ « Cache seule + maintenance de fond ».
`LIDAR_TILE_CACHE_ONLY=1` + `LIDAR_TILE_BACKGROUND=1` flip the load pattern on
a small Pi: browsing renders nothing (a missing tile is served transparent,
with `X-Tile-Pending`), and a background task watches for new or regenerated
tiles and maintains the pyramid at low priority — see `docs/MAPS.md`, section
"Cache-only + background maintenance".
### 3. Génération de tuiles depuis la carte
### 3. Generating tiles from the map
Les boutons **+ Zone** (dessiner un rectangle → téléchargement IGN + run sur
le worker), **⤒ Compléter** (dalles déjà téléchargées incomplètes) et
**↻ Générer cette dalle** (fiche d'infos au clic) sont masqués si :
The **+ Zone** ("Add area": draw a rectangle → IGN download + run on the
worker), **⤒ Compléter** ("Complete": already-downloaded but incomplete
tiles) and **↻ Générer cette dalle** ("Generate this tile": info panel on
click) buttons are hidden if:
- le navigateur vient d'une IP hors `LIDAR_REGEN_CIDR` (défaut : boucle
locale + plages privées RFC1918 ; liste de CIDR séparés par virgules,
chaîne vide pour lever la restriction). L'IP est celle de la connexion
(conservée par le DNAT Docker pour les clients du LAN) ; derrière un
reverse proxy local (dans le réseau autorisé), `X-Forwarded-For` désigne
le client réel ;
- le worker est injoignable ET aucun pipeline local n'existe.
- the browser's IP is outside `LIDAR_REGEN_CIDR` (default: loopback + private
RFC1918 ranges; a comma-separated list of CIDRs, or an empty string to lift
the restriction). The IP is the one seen on the connection (preserved by
Docker's DNAT for LAN clients); behind a local reverse proxy (itself inside
the allowed network), `X-Forwarded-For` designates the real client;
- the worker is unreachable AND no local pipeline exists.
Une demande lancée pendant un run part en **file d'attente** côté worker
(jamais de coupure du travail en place) ; la progression s'affiche dalle par
dalle (cadres orange/bleu/rouge sur la carte) et le bouton **Arrêter** envoie
un SIGTERM au pipeline. À la fin du run, la carte se rafraîchit et la
maintenance de pyramide repart automatiquement.
A request submitted while a run is already in progress is placed in a
**queue** on the worker side (an in-progress job is never interrupted);
progress is displayed tile by tile (orange/blue/red frames on the map) and the
**Arrêter** ("Stop") button sends a SIGTERM to the pipeline. At the end of the
run, the map refreshes and pyramid maintenance resumes automatically.
### 4. Centrer la carte sur la position GPS (téléphone)
### 4. Centering the map on the GPS position (phone)
Les navigateurs n'exposent l'API Geolocation qu'en **contexte sécurisé**
(HTTPS) : servir la carte derrière Traefik (TLS) comme ci-dessus, ou en
dernier recours en HTTPS direct (l'entrée autonome `python -m
lidar_pipeline.mapserve` honore `LIDAR_SSL_CERTFILE` / `LIDAR_SSL_KEYFILE`).
Browsers only expose the Geolocation API in a **secure context** (HTTPS):
serve the map behind Traefik (TLS) as above, or, as a last resort, directly
over HTTPS (the standalone entry point `python -m lidar_pipeline.mapserve`
honors `LIDAR_SSL_CERTFILE` / `LIDAR_SSL_KEYFILE`).
## Mise à jour
## Updating
### Machine de traitement
### Processing machine
```bash
cd <checkout> && git pull && docker compose -f docker-compose.worker.yml up -d --build
```
### Pi (depuis le poste de pilotage)
Le poste de pilotage interdit l'appel `ssh` direct : le wrapper
`ssh` fournit l'accès autorisé avec transfert d'agent (`-A`) —
les `git pull` distants utilisent la clé locale :
### Pi
```bash
#!/bin/bash
set -euo pipefail
HOST="${1:-192.168.3.10}" # machine carte par défaut (le Pi)
shift || true
exec ssh -A -o BatchMode=yes -o ConnectTimeout=5 \
-o StrictHostKeyChecking=accept-new "$HOST" "$@"
ssh <pi-host> "cd <checkout> && git pull && docker compose -f docker-compose.maps.yml -f docker-compose.maps.override.yml up -d --build"
```
Mise à jour complète du Pi (checkout git en `/srv/lidar_rendu`,
avec l'override Traefik) :
```bash
ssh 192.168.3.10 "cd /srv/lidar_rendu && git pull && \
docker compose -f docker-compose.maps.yml -f docker-compose.maps.override.yml up -d --build"
```
Le code de l'interface est bâché dans l'image (`Dockerfile.maps`) : **TOUT**
changement d'interface exige le rebuild (`--build`), sans lui l'ancien code
tourne.
The interface code is baked into the image (`Dockerfile.maps`): **ANY**
interface change requires a rebuild (`--build`) — without it, the old code
keeps running.