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:
@ -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.
|
||||
|
||||
Reference in New Issue
Block a user