Écrire le sidecar qualité aussi quand le DTM primaire vient du cache
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@ -287,6 +287,77 @@ class TestEffectiveGroundMethod:
|
||||
assert p._effective_ground_method() == "smrf"
|
||||
|
||||
|
||||
class TestQualityCacheHit:
|
||||
"""Sidecar qualité écrit même quand le DTM primaire est réutilisé depuis
|
||||
le cache (classification sol sautée, pas de LAS sol disponible)."""
|
||||
|
||||
@staticmethod
|
||||
def _write_las(path, x, y, cls, t):
|
||||
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]
|
||||
header.global_encoding.gps_time_type = laspy.header.GpsTimeType.STANDARD
|
||||
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_cache_hit_writes_quality_sidecar(self, tmp_path, monkeypatch):
|
||||
import numpy as np
|
||||
import rasterio
|
||||
from rasterio.transform import from_bounds
|
||||
from lidar_pipeline.pipeline import LidarArchaeoPipeline
|
||||
import lidar_pipeline.pipeline as pipeline_mod
|
||||
from lidar_pipeline.dtm import GAP_FILL_TAG, GAP_FILL_VERSION
|
||||
from lidar_pipeline.quality import read_quality
|
||||
|
||||
input_dir = tmp_path / "input"
|
||||
input_dir.mkdir()
|
||||
base = "LHD_FXX_0652_6862_PTS_LAMB93_IGN69"
|
||||
laz = input_dir / f"{base}.laz"
|
||||
import datetime
|
||||
epoch = datetime.datetime(1980, 1, 6, tzinfo=datetime.timezone.utc)
|
||||
gps = (datetime.datetime(2022, 6, 1, 12, tzinfo=datetime.timezone.utc) - epoch).total_seconds() - 1e9
|
||||
self._write_las(laz, [652100, 652200, 652300], [6861100, 6861200, 6861300],
|
||||
[2, 2, 6], [gps, gps, gps])
|
||||
|
||||
pipeline = LidarArchaeoPipeline(
|
||||
str(input_dir), str(tmp_path / "output"), ground_method='ign',
|
||||
ign_classes="sol", strip_align=False, edge_buffer=0.0)
|
||||
pipeline.viz_steps = [] # aucune visualisation à calculer (hors périmètre)
|
||||
|
||||
# DTM déjà en cache, compatible avec la config du run (pas de calage,
|
||||
# pas de raccord, comblement à la version courante) : le run doit
|
||||
# emprunter la branche « DTM existant » sans reclassifier ni régénérer.
|
||||
dtm_path = pipeline.dtm_dir / f"{base}_dtm.tif"
|
||||
with rasterio.open(
|
||||
dtm_path, "w", driver="GTiff", height=10, width=10, count=1,
|
||||
dtype="float32", crs="EPSG:2154",
|
||||
transform=from_bounds(652000.0, 6861995.0, 652005.0, 6862000.0, 10, 10),
|
||||
) as dst:
|
||||
dst.write(np.zeros((10, 10), dtype="float32"), 1)
|
||||
dst.update_tags(**{GAP_FILL_TAG: GAP_FILL_VERSION})
|
||||
|
||||
def boom(*a, **k):
|
||||
raise AssertionError("cache hit attendu : ne doit pas reclassifier/régénérer le DTM")
|
||||
|
||||
monkeypatch.setattr(pipeline_mod, "classify_ground", boom)
|
||||
monkeypatch.setattr(pipeline_mod, "create_dtm_fast", boom)
|
||||
|
||||
assert pipeline.process_file(laz) is True
|
||||
|
||||
data = read_quality(pipeline.output_dir, base)
|
||||
assert data is not None
|
||||
assert abs(data["ground_density"] - 2e-6) < 1e-9 # 2 points de classe 2 sur 1 km²
|
||||
assert data["acq_start"] == "2022-06-01" and data["acq_source"] == "gps"
|
||||
|
||||
|
||||
class TestResolveWorkers:
|
||||
def test_auto_scales_with_cpus(self):
|
||||
from lidar_pipeline.pipeline import resolve_workers
|
||||
|
||||
Reference in New Issue
Block a user