- webapp.py: FastAPI serving the continuous map (port 8973) with /api/preview, /api/generate and /api/status; tiles are downloaded from IGN and processed in a logged subprocess, tracked live in a side "File de génération" panel that survives page reloads - fetch_ign.py: download missing 1 km LiDAR HD tiles from the IGN geoplateforme before processing - index.py: tile thumbnails and 500 m subtiles are now invalidated by mtime so regenerating a tile refreshes its cached images; progress logging per tile - dtm.py: back to the historical gap handling (small gaps filled by fillnodata only, larger holes left as nodata rendered black); lowest-return floor only via --bare-earth, IGN class selection via --ign-classes - cli.py: positional input now optional (--rebuild-index works alone) - docker-compose.yml: serve (GPU, port 8973) and process services; launch via docker compose only (documented in AGENTS.md/AGENTS.md) - tests: 131 passing, incl. regressions for thumbnail staleness, --rebuild-index without input, and nodata rendering
99 lines
3.7 KiB
Python
99 lines
3.7 KiB
Python
"""Tests du téléchargement des dalles LiDAR HD de l'IGN (fetch_ign)."""
|
|
|
|
|
|
def test_parse_tile_specs():
|
|
"""Accepte 'col,row', 'col:row' et ignore les espaces."""
|
|
from lidar_pipeline.fetch_ign import parse_tile_specs
|
|
assert parse_tile_specs(["1055,6882"]) == [(1055, 6882)]
|
|
assert parse_tile_specs(["1055:6883", " 651 , 6630 "]) == [(1055, 6883), (651, 6630)]
|
|
|
|
|
|
def test_parse_tile_specs_rejects_invalid():
|
|
"""Les spécifications mal formées lèvent une erreur explicite."""
|
|
import pytest
|
|
from lidar_pipeline.fetch_ign import parse_tile_specs
|
|
with pytest.raises(ValueError):
|
|
parse_tile_specs(["1055"])
|
|
with pytest.raises(ValueError):
|
|
parse_tile_specs(["abc,def"])
|
|
with pytest.raises(ValueError):
|
|
parse_tile_specs(["1055,6882,9999"])
|
|
|
|
|
|
def test_tile_filename_pads_coordinates():
|
|
"""Le nom de fichier DALLE utilise des coordonnées à 4 chiffres."""
|
|
from lidar_pipeline.fetch_ign import tile_filename
|
|
assert tile_filename(1055, 6882) == "LHD_FXX_1055_6882_PTS_LAMB93_IGN69.copc.laz"
|
|
assert tile_filename(651, 6630) == "LHD_FXX_0651_6630_PTS_LAMB93_IGN69.copc.laz"
|
|
|
|
|
|
def test_match_feature_uses_coordonnees_nw():
|
|
"""La correspondance se fait sur lidarhd:coordonnees_NW (format 0816-6847)."""
|
|
from lidar_pipeline.fetch_ign import match_feature
|
|
features = [
|
|
{"id": "LHD_FXX_1054_6882_PTS_LAMB93_IGN69_NE",
|
|
"properties": {"lidarhd:coordonnees_NW": "1054-6882"},
|
|
"assets": {"data": {"href": "https://example.org/a.copc.laz"}}},
|
|
{"id": "LHD_FXX_1055_6882_PTS_LAMB93_IGN69_NE",
|
|
"properties": {"lidarhd:coordonnees_NW": "1055-6882"},
|
|
"assets": {"data": {"href": "https://example.org/b.copc.laz"}}},
|
|
]
|
|
assert match_feature(features, 1055, 6882) is features[1]
|
|
assert match_feature(features, 9999, 9999) is None
|
|
|
|
|
|
def test_find_tile_url_matches_and_returns_href(monkeypatch):
|
|
"""find_tile_url interroge le STAC et retourne l'href de l'asset data."""
|
|
from lidar_pipeline import fetch_ign
|
|
|
|
class FakeResponse:
|
|
def __init__(self, payload):
|
|
self._payload = payload.encode("utf-8")
|
|
|
|
def read(self):
|
|
return self._payload
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, *args):
|
|
return False
|
|
|
|
payload = ('{"features": [{"properties": {"lidarhd:coordonnees_NW": "1055-6882"},'
|
|
'"assets": {"data": {"href": "https://data.geopf.fr/x.copc.laz"}}}]}')
|
|
captured = {}
|
|
|
|
def fake_urlopen(req, timeout=None):
|
|
captured["url"] = req.full_url
|
|
return FakeResponse(payload)
|
|
|
|
monkeypatch.setattr(fetch_ign.urllib.request, "urlopen", fake_urlopen)
|
|
url = fetch_ign.find_tile_url(1055, 6882)
|
|
assert url == "https://data.geopf.fr/x.copc.laz"
|
|
assert "api.stac.teledetection.fr" in captured["url"]
|
|
assert "bbox=" in captured["url"]
|
|
|
|
|
|
def test_fetch_tiles_skips_existing_and_generated(tmp_path, monkeypatch):
|
|
"""Pas de téléchargement si le LAZ existe ou si les visualisations existent."""
|
|
from lidar_pipeline import fetch_ign
|
|
|
|
input_dir = tmp_path / "input"
|
|
input_dir.mkdir()
|
|
output_dir = tmp_path / "output"
|
|
vis_dir = output_dir / "visualisations" / "LHD_FXX_1054_6882_PTS_LAMB93_IGN69"
|
|
vis_dir.mkdir(parents=True)
|
|
|
|
# 1054,6882 : visualisations déjà générées
|
|
# 1055,6882 : LAZ déjà présent
|
|
existing = input_dir / fetch_ign.tile_filename(1055, 6882)
|
|
existing.write_bytes(b"naze")
|
|
|
|
def fail_download(*args, **kwargs):
|
|
raise AssertionError("ne doit pas être appelé")
|
|
|
|
monkeypatch.setattr(fetch_ign, "find_tile_url", fail_download)
|
|
result = fetch_ign.fetch_tiles(input_dir, [(1054, 6882), (1055, 6882)],
|
|
output_dir=output_dir)
|
|
assert result == []
|