Skip to content

Phase 15 : téléchargement terrain et inventaire atomique #276

Phase 15 : téléchargement terrain et inventaire atomique

Phase 15 : téléchargement terrain et inventaire atomique #276

Workflow file for this run

name: CI cross-platform
on:
push:
paths:
- 'lidar2map.py'
- '_split_*.py'
- '_raster_formats.py'
- '_mbtiles_*.py'
- '_ombrages_*.py'
- '_shading_specs.py'
- '_bootstrap_*.py'
- '_smoketest.py'
- '_logging_helpers.py'
- '_tee_logger.py'
- '_log_activation.py'
- '_atomic_files.py'
- '_http_helpers.py'
- '_runtime_paths.py'
- '_disk_guard.py'
- '_wfs_pipeline.py'
- '_bdtopo_bulk.py'
- '_bdtopo_layers.py'
- '_vector_acquisition.py'
- '_vector_outputs.py'
- '_osm_*.py'
- '_terrain_*.py'
- '_terrain_zones.py'
- '_geojson_*.py'
- '_loader.py'
- 'deploy.py'
- 'update_app.py'
- 'tests/**'
- 'providers/**'
- 'tools/**'
- 'gui/**'
- 'docs/**'
- 'screenshots/**'
- 'README*.md'
- '*.spec'
- '*_build.*'
- 'setup_build_*'
- 'tagmapping-min.xml'
- 'pyproject.toml'
- '.github/workflows/ci.yml'
pull_request:
paths:
- 'lidar2map.py'
- '_split_*.py'
- '_raster_formats.py'
- '_mbtiles_*.py'
- '_ombrages_*.py'
- '_shading_specs.py'
- '_bootstrap_*.py'
- '_smoketest.py'
- '_logging_helpers.py'
- '_tee_logger.py'
- '_log_activation.py'
- '_atomic_files.py'
- '_http_helpers.py'
- '_runtime_paths.py'
- '_disk_guard.py'
- '_wfs_pipeline.py'
- '_bdtopo_bulk.py'
- '_bdtopo_layers.py'
- '_vector_acquisition.py'
- '_vector_outputs.py'
- '_osm_*.py'
- '_terrain_*.py'
- '_terrain_zones.py'
- '_geojson_*.py'
- '_loader.py'
- 'deploy.py'
- 'update_app.py'
- 'tests/**'
- 'providers/**'
- 'tools/**'
- 'gui/**'
- 'docs/**'
- 'screenshots/**'
- 'README*.md'
- '*.spec'
- '*_build.*'
- 'setup_build_*'
- 'tagmapping-min.xml'
- 'pyproject.toml'
- '.github/workflows/ci.yml'
workflow_dispatch:
jobs:
# Lint rapide (quelques secondes) : échoue avant de payer la matrice 3 OS.
# Règles F (pyflakes) seulement, config dans pyproject.toml : imports
# inutilisés, variables mortes, noms non définis. Dès son premier run
# local, a attrapé un F821 réel (variable supprimée encore référencée
# dans une branche que ni py_compile ni les tests n'exercent).
lint:
name: ruff (pyflakes)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Installer ruff
run: pip install ruff
- name: Ruff check
run: ruff check .
test:
name: ${{ matrix.os }}
needs: lint
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
env:
# Pas d'auto-install/venv dans le runner : les deps sont posées par pip
# ci-dessous, et un manque doit échouer net (message clair) plutôt que
# déclencher le bootstrap.
LIDAR2MAP_BOOTSTRAP: none
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Installer les dependances Python
# Liste alignée sur les deps OBLIGATOIRES de lidar2map (cf.
# _installer_deps) : depuis le refactor rasterio, ijson/rasterio/
# fiona/certifi sont requis à l'import — l'ancienne ligne (pyproj
# Pillow numpy scipy) est la raison des échecs CI d'avril 2026.
run: pip install pyproj Pillow numpy scipy ijson rasterio fiona certifi
- name: Syntaxe Python
run: python -c "import ast; ast.parse(open('lidar2map.py', encoding='utf-8').read()); print('OK')"
- name: Syntaxe _loader.py
run: python -c "import ast; ast.parse(open('_loader.py', encoding='utf-8').read()); print('OK')"
- name: Import complet sans erreur
run: |
python -c "
import sys, types
sys.argv = ['lidar2map.py', '--help']
try:
import lidar2map
except SystemExit:
pass
print('Import OK')
"
- name: help ignlidar
run: python lidar2map.py --ignlidar --help
- name: help ignraster
run: python lidar2map.py --ignraster --help
- name: help ignvecteur
run: python lidar2map.py --ignvecteur --help
- name: help fusionner
run: python lidar2map.py --fusionner --source dummy.geojson --help
- name: help osm
run: python lidar2map.py --osm --help
# Dispatch --remote-cli/--remote-gui : import de tools/rlidar2map_CLI.py
# et tools/rlidar2map_GUI.py (stdlib pur), avant le bootstrap. Valide
# les 3 OS sans VM ni secret : juste que le dispatch et l'import
# fonctionnent, pas une vraie connexion SSH.
- name: help remote-cli
run: python lidar2map.py --remote-cli --help
- name: help remote-gui
run: python lidar2map.py --remote-gui --help
- name: _parser_departements
run: |
python -c "
import sys; sys.argv=['lidar2map.py','--ignlidar','--help']
try: import lidar2map
except SystemExit: pass
assert lidar2map._parser_departements('83') == ['83']
assert lidar2map._parser_departements('1-3') == ['01','02','03']
assert lidar2map._parser_departements('30,35,75') == ['30','35','75']
assert lidar2map._parser_departements('1-3,75,83') == ['01','02','03','75','83']
assert lidar2map._parser_departements('2A') == ['2A']
assert lidar2map._parser_departements('971') == ['971']
print('_parser_departements OK')
"
- name: _epsilon_depuis_surface_km2
run: |
python -c "
import sys; sys.argv=['lidar2map.py','--ignlidar','--help']
try: import lidar2map
except SystemExit: pass
eps = lidar2map._epsilon_depuis_surface_km2
assert eps(100) == 3 / 111000
assert eps(500) == 8 / 111000
assert eps(9000) == 15 / 111000
assert eps(50000) == 25 / 111000
assert eps(200000)== 40 / 111000
print('_epsilon_depuis_surface_km2 OK')
"
- name: _hms
run: |
python -c "
import sys; sys.argv=['lidar2map.py','--ignlidar','--help']
try: import lidar2map
except SystemExit: pass
assert lidar2map._hms(5) == '5s'
assert lidar2map._hms(90) == '1m30s'
assert lidar2map._hms(3661) == '1h01m01s'
print('_hms OK')
"
- name: normaliser_nom
run: |
python -c "
import sys; sys.argv=['lidar2map.py','--ignlidar','--help']
try: import lidar2map
except SystemExit: pass
assert lidar2map.normaliser_nom('Gareoult') == 'gareoult'
assert lidar2map.normaliser_nom('Ile-de-France') == 'ile-de-france'
assert lidar2map.normaliser_nom('Saint Jean') == 'saint_jean'
print('normaliser_nom OK')
"
- name: _GEOFABRIK completude
run: |
python -c "
import sys; sys.argv=['lidar2map.py','--ignlidar','--help']
try: import lidar2map
except SystemExit: pass
codes = [f'{n:02d}' for n in range(1,20)] + [f'{n:02d}' for n in range(21,96)]
codes += ['2A','2B','971','972','973','974','976']
attendus = set(codes)
trouves = set(lidar2map._GEOFABRIK)
assert trouves == attendus, f'manquants={sorted(attendus-trouves)} extras={sorted(trouves-attendus)}'
print(f'_GEOFABRIK complet : {len(trouves)} codes')
"
- name: wgs84_to_lamb93_approx
# Référence pyproj plutôt que bornes en dur : les anciennes bornes
# (950000 < x) étaient fausses (X exact = 946880.8) et n'avaient
# jamais été exercées, la CI tombant avant sur l'import.
# Tolérance 50 m = précision documentée de l'approximation.
run: |
python -c "
import sys; sys.argv=['lidar2map.py','--ignlidar','--help']
try: import lidar2map
except SystemExit: pass
from pyproj import Transformer
t = Transformer.from_crs('EPSG:4326', 'EPSG:2154', always_xy=True)
xe, ye = t.transform(6.0423, 43.3156)
x, y = lidar2map.wgs84_to_lamb93_approx(6.0423, 43.3156)
assert abs(x - xe) < 50 and abs(y - ye) < 50, f'dx={x-xe:.1f}m dy={y-ye:.1f}m'
print(f'wgs84_to_lamb93_approx OK : X={x:.1f} Y={y:.1f} (pyproj a {abs(x-xe)*100:.0f}/{abs(y-ye)*100:.0f} cm)')
"
- name: lamb93_to_wgs84_approx
run: |
python -c "
import sys; sys.argv=['lidar2map.py','--ignlidar','--help']
try: import lidar2map
except SystemExit: pass
from pyproj import Transformer
t = Transformer.from_crs('EPSG:2154', 'EPSG:4326', always_xy=True)
lon_e, lat_e = t.transform(960000, 6258000)
lon, lat = lidar2map.lamb93_to_wgs84_approx(960000, 6258000)
assert abs(lon - lon_e) < 0.0007 and abs(lat - lat_e) < 0.0005, f'dlon={lon-lon_e} dlat={lat-lat_e}'
print(f'lamb93_to_wgs84_approx OK : lon={lon:.5f} lat={lat:.5f}')
"
- name: Historique lecture ecriture
run: |
python -c "
import sys, tempfile, os
from pathlib import Path
sys.argv=['lidar2map.py','--ignlidar','--help']
try: import lidar2map
except SystemExit: pass
tmp = Path(tempfile.mkdtemp())
lidar2map._HISTORIQUE_PATH = tmp / 'historique.json'
lidar2map._sauver_historique({'type':'osm','nom':'test','mode':'dep','dep':'83'}, 120, '/tmp/out')
hist = lidar2map._lire_historique()
assert len(hist) == 1
assert hist[0]['nom'] == 'test'
assert hist[0]['dep'] == '83'
assert hist[0]['duree'] == '2m00s'
print('Historique OK')
"
- name: Platform flags
run: |
python -c "
import sys; sys.argv=['lidar2map.py','--ignlidar','--help']
try: import lidar2map
except SystemExit: pass
import platform
expected_win = platform.system() == 'Windows'
expected_linux = platform.system() == 'Linux'
expected_mac = platform.system() == 'Darwin'
assert lidar2map.WINDOWS == expected_win, f'WINDOWS={lidar2map.WINDOWS}'
assert lidar2map.LINUX == expected_linux, f'LINUX={lidar2map.LINUX}'
assert lidar2map.MACOS == expected_mac, f'MACOS={lidar2map.MACOS}'
print(f'Platform flags OK : WINDOWS={lidar2map.WINDOWS} LINUX={lidar2map.LINUX} MACOS={lidar2map.MACOS}')
"
# Atomicité, contrats providers/façade, CLI distante, documentation et
# livraison. Exécutés sur les 3 OS ; les calculs Numba lourds restent
# dans le job Ubuntu ci-dessous.
- name: Regressions rapides hors reseau
run: python tests/run_tests.py fast
# Tests de régression des calculs scientifiques (kernels Horn/SVF/openness,
# LRM, RRIM, tuilage MBTiles/RMAP/SQLiteDB). Ubuntu uniquement : les calculs
# sont identiques sur les 3 OS (numpy/numba), inutile de payer 3× la
# compilation numba — le job 'test' ci-dessus couvre déjà la matrice OS
# pour la syntaxe et les imports.
tests-scientifiques:
name: tests scientifiques (ubuntu)
needs: lint
runs-on: ubuntu-latest
env:
LIDAR2MAP_BOOTSTRAP: none
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Installer les dependances calcul
# numba pour les kernels + la liste obligatoire de lidar2map
# (l'import sous bootstrap=none vérifie ijson/fiona/certifi aussi).
# laspy/lazrs : le test DFM (interactions section 9a, mur synthétique)
# doit TOURNER en CI, pas être sauté en ImportError (revue 2026-07-16).
# cloth-simulation-filter : idem pour le chemin --laz-ground csf (9a).
run: pip install numpy scipy rasterio numba Pillow pyproj ijson fiona certifi laspy lazrs cloth-simulation-filter
- name: Suite scientifique complete (sans reseau)
run: python tests/run_tests.py scientific