Calculer le sidecar qualité après le DTM et ajouter --quality-backfill

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Antoine Jacquin
2026-09-27 15:17:32 +02:00
parent 1db98c733d
commit 7d5b5ecd42
4 changed files with 200 additions and 0 deletions

View File

@ -89,3 +89,89 @@ def test_quality_module_imports_without_numpy():
"import lidar_pipeline.quality as q; print(q.QUALITY_VERSION)")
r = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True)
assert r.returncode == 0, r.stderr
def _write_las(path, x, y, cls, t, adjusted=True, creation=None):
"""LAS minimal (format 6 : gps_time) pour les tests."""
import laspy
import numpy as np
header = laspy.LasHeader(point_format=6, version="1.4")
header.scales = [0.01, 0.01, 0.01]
header.offsets = [652000.0, 6861000.0, 0.0]
if adjusted:
header.global_encoding.gps_time_type = laspy.header.GpsTimeType.STANDARD
if creation is not None:
header.creation_date = creation
las = laspy.LasData(header)
las.x = np.asarray(x, float); las.y = np.asarray(y, float)
las.z = np.zeros(len(x))
las.classification = np.asarray(cls, np.uint8)
las.return_number = np.ones(len(x), np.uint8)
las.number_of_returns = np.ones(len(x), np.uint8)
las.gps_time = np.asarray(t, float)
las.write(str(path))
def test_quality_from_las_filters_classes(tmp_path):
from lidar_pipeline.quality import quality_from_las
p = tmp_path / "LHD_FXX_0652_6862_PTS_LAMB93_IGN69.laz"
t = _gps_seconds(2022, 6, 1)
_write_las(p, [652100, 652200, 652300], [6861100, 6861200, 6861300],
[2, 2, 5], [t, t, t])
q = quality_from_las(p, BOUNDS, codes=(2,))
assert abs(q["ground_density"] - 2e-6) < 1e-9
assert q["acq_start"] == "2022-06-01" and q["acq_source"] == "gps"
def test_quality_from_las_week_time_uses_header_date(tmp_path):
from datetime import date
from lidar_pipeline.quality import quality_from_las
p = tmp_path / "x.las"
_write_las(p, [652100], [6861100], [2], [1000.0], adjusted=False,
creation=date(2021, 9, 2))
q = quality_from_las(p, BOUNDS)
assert q["acq_source"] == "header" and q["acq_start"] == "2021-09-02"
def test_quality_from_las_unreadable_returns_none(tmp_path):
from lidar_pipeline.quality import quality_from_las
p = tmp_path / "bad.laz"; p.write_bytes(b"pas un LAS")
assert quality_from_las(p, BOUNDS) is None
def test_ensure_quality_skips_existing_and_unparsable(tmp_path):
from lidar_pipeline.quality import ensure_quality, read_quality, write_quality
base = "LHD_FXX_0652_6862_PTS_LAMB93_IGN69"
p = tmp_path / f"{base}_ground.las"
_write_las(p, [652100], [6861100], [2], [_gps_seconds(2022, 6, 1)])
assert ensure_quality(p, base, tmp_path) is True
assert read_quality(tmp_path, base)["acq_start"] == "2022-06-01"
write_quality(tmp_path, base, dict(read_quality(tmp_path, base), ground_density=99.0))
assert ensure_quality(p, base, tmp_path) is True
assert read_quality(tmp_path, base)["ground_density"] == 99.0 # non recalculé
assert ensure_quality(p, "nom_libre", tmp_path) is False
def test_backfill_quality_reads_input_laz(tmp_path):
from lidar_pipeline.quality import backfill_quality, read_quality
inp = tmp_path / "input"; inp.mkdir()
out = tmp_path / "output"
base = "LHD_FXX_0652_6862_PTS_LAMB93_IGN69"
_write_las(inp / f"{base}.copc.laz", [652100, 652200], [6861100, 6861200],
[2, 6], [_gps_seconds(2022, 6, 1)] * 2)
assert backfill_quality(inp, out) == 1
assert abs(read_quality(out, base)["ground_density"] - 1e-6) < 1e-9
assert backfill_quality(inp, out) == 0 # déjà à jour
def test_cli_quality_backfill(tmp_path):
import subprocess, sys
inp = tmp_path / "input"; inp.mkdir()
out = tmp_path / "output"
base = "LHD_FXX_0652_6862_PTS_LAMB93_IGN69"
_write_las(inp / f"{base}.laz", [652100], [6861100], [2], [_gps_seconds(2022, 6, 1)])
r = subprocess.run([sys.executable, "-m", "lidar_pipeline", str(inp), "-o", str(out),
"--quality-backfill"], capture_output=True, text=True, timeout=180)
assert r.returncode == 0, r.stderr
assert (out / "quality" / f"{base}.json").is_file()
assert not (out / "DTM").exists()