92 lines
4.0 KiB
Python
92 lines
4.0 KiB
Python
"""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
|