Ajouter la mesure de qualité par dalle (densité sol, date d'acquisition) et ses sidecars

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Antoine Jacquin
2026-09-27 15:11:47 +02:00
parent 90dbe61f3b
commit 1db98c733d
2 changed files with 241 additions and 0 deletions

View File

@ -0,0 +1,91 @@
"""Tests de la mesure de qualité des dalles (densité sol, date d'acquisition)."""
BOUNDS = (652000.0, 6861000.0, 653000.0, 6862000.0)
def _gps_seconds(year, month, day):
"""Temps GPS ajusté standard (secondes GPS − 1e9) d'un midi UTC."""
from datetime import datetime, timezone
epoch = datetime(1980, 1, 6, tzinfo=timezone.utc)
return (datetime(year, month, day, 12, tzinfo=timezone.utc) - epoch).total_seconds() - 1e9
def test_gps_adjusted_to_date():
from lidar_pipeline.quality import gps_adjusted_to_date
assert gps_adjusted_to_date(_gps_seconds(2023, 3, 15)) == "2023-03-15"
def test_compute_quality_density_grid_and_dates():
import numpy as np
from lidar_pipeline.quality import compute_quality
# 1 point par m² sur la moitié nord, rien au sud ; 2 dates de vol.
xs, ys = np.meshgrid(np.arange(652000.5, 653000.0, 1.0),
np.arange(6861500.5, 6862000.0, 1.0))
x, y = xs.ravel(), ys.ravel()
t = np.where(x < 652500, _gps_seconds(2023, 3, 15), _gps_seconds(2023, 3, 17))
q = compute_quality(x, y, t, BOUNDS)
assert q["version"] == 1
assert abs(q["ground_density"] - 0.5) < 1e-6
grid = q["density_grid"]
assert len(grid) == 20 and all(len(r) == 20 for r in grid)
assert grid[0][0] == 1.0 # ligne 0 = nord : couverte
assert grid[19][0] == 0.0 # sud : vide
assert q["acq_start"] == "2023-03-15" and q["acq_end"] == "2023-03-17"
assert q["acq_source"] == "gps"
# 1 point par m² : 1 pixel 0,2 m sur 25 occupé au nord, aucun au sud.
assert abs(q["empty_fraction"] - (1 - 0.5 / 25)) < 1e-3
def test_compute_quality_ignores_points_outside_bounds():
import numpy as np
from lidar_pipeline.quality import compute_quality
x = np.array([652500.0, 660000.0]); y = np.array([6861500.0, 6861500.0])
q = compute_quality(x, y, None, BOUNDS, header_date="2024-05-03")
assert abs(q["ground_density"] - 1e-6) < 1e-9
assert q["acq_start"] == q["acq_end"] == "2024-05-03"
assert q["acq_source"] == "header"
def test_compute_quality_week_time_falls_back_to_header():
import numpy as np
from lidar_pipeline.quality import compute_quality
x = np.array([652500.0]); y = np.array([6861500.0])
q = compute_quality(x, y, np.array([345600.0]), BOUNDS, gps_adjusted=False,
header_date="2024-05-03")
assert q["acq_source"] == "header" and q["acq_start"] == "2024-05-03"
def test_compute_quality_empty_tile():
import numpy as np
from lidar_pipeline.quality import compute_quality
q = compute_quality(np.array([]), np.array([]), np.array([]), BOUNDS)
assert q["ground_density"] == 0.0 and q["empty_fraction"] == 1.0
assert q["acq_start"] is None and q["acq_end"] is None and q["acq_source"] is None
def test_sidecar_roundtrip_and_table(tmp_path):
from lidar_pipeline.quality import (load_quality_table, quality_path,
read_quality, write_quality)
base = "LHD_FXX_0652_6862_PTS_LAMB93_IGN69"
data = {"version": 1, "ground_density": 4.2}
assert write_quality(tmp_path, base, data) is True
assert write_quality(tmp_path, base, data) is False # contenu identique : pas réécrit
assert quality_path(tmp_path, base).is_file()
assert read_quality(tmp_path, base) == data
assert load_quality_table(tmp_path) == {base: data}
def test_read_quality_rejects_other_version(tmp_path):
from lidar_pipeline.quality import read_quality, write_quality
base = "LHD_FXX_0652_6862_PTS_LAMB93_IGN69"
write_quality(tmp_path, base, {"version": 0})
assert read_quality(tmp_path, base) is None
def test_quality_module_imports_without_numpy():
"""L'image légère n'a pas numpy : le module ne doit pas l'importer au chargement."""
import subprocess, sys
code = ("import sys; sys.modules['numpy'] = None; "
"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