Isoler les dalles voisines de raccord dans input/edge_neighbors/
This commit is contained in:
@ -1043,8 +1043,14 @@ def _interpolate_holes(dtm, downsample=8):
|
||||
# images finales sont recadrées sur la dalle exacte (cf. rendering.py).
|
||||
EDGE_BUFFER_TAG = "LIDAR_EDGE_BUFFER" # tag GeoTIFF : tampon utilisé (m)
|
||||
|
||||
# Sous-dossier de input/ où sont isolées les dalles voisines téléchargées
|
||||
# pour le seul raccord des bords : elles ne font pas partie du corpus de
|
||||
# tuiles à rendre (les scans globaux n'énumèrent que input/ à plat).
|
||||
EDGE_NEIGHBORS_DIRNAME = "edge_neighbors"
|
||||
|
||||
# Décalages des 8 voisines d'une dalle (col, row) en km
|
||||
_NEIGHBOR_OFFSETS = [(-1, -1), (0, -1), (1, -1), (-1, 0),
|
||||
|
||||
(1, 0), (-1, 1), (0, 1), (1, 1)]
|
||||
|
||||
|
||||
@ -1055,12 +1061,19 @@ def _tile_coords(name):
|
||||
|
||||
|
||||
def _neighbor_laz_files(source_laz):
|
||||
"""Liste les 8 fichiers LAZ/LAS adjacents à `source_laz` dans son dossier."""
|
||||
"""Liste les 8 fichiers LAZ/LAS adjacents à `source_laz`.
|
||||
|
||||
Recherche dans le dossier de la source, puis dans le sous-dossier
|
||||
edge_neighbors/ : les dalles voisines téléchargées pour le seul raccord
|
||||
des bords y sont isolées — sinon elles s'accumulent à plat dans input/ et
|
||||
chaque passe globale annexe un anneau de tuiles à rendre en plus.
|
||||
"""
|
||||
coords = _tile_coords(source_laz)
|
||||
if coords is None:
|
||||
return []
|
||||
col, row = coords
|
||||
directory = Path(source_laz).parent
|
||||
search_dirs = [directory, directory / EDGE_NEIGHBORS_DIRNAME]
|
||||
neighbors = []
|
||||
for dcol, drow in _NEIGHBOR_OFFSETS:
|
||||
nc, nr = col + dcol, row + drow
|
||||
@ -1068,9 +1081,10 @@ def _neighbor_laz_files(source_laz):
|
||||
# on accepte aussi la variante sans remplissage pour les dalles exotiques.
|
||||
names = {f"LHD_FXX_{nc:04d}_{nr:04d}", f"LHD_FXX_{nc}_{nr}"}
|
||||
matches = []
|
||||
for name in names:
|
||||
matches += list(directory.glob(f"{name}_*.las"))
|
||||
matches += list(directory.glob(f"{name}_*.laz"))
|
||||
for search_dir in search_dirs:
|
||||
for name in names:
|
||||
matches += list(search_dir.glob(f"{name}_*.las"))
|
||||
matches += list(search_dir.glob(f"{name}_*.laz"))
|
||||
if matches:
|
||||
neighbors.append(sorted(matches)[0])
|
||||
else:
|
||||
|
||||
@ -456,17 +456,20 @@ class LidarArchaeoPipeline:
|
||||
"""Télécharge les dalles LAZ voisines manquantes (raccord des bords).
|
||||
|
||||
La bande de raccord lit les 8 LAZ adjacentes de chaque tuile ; celles
|
||||
absentes de input/ sont téléchargées depuis le catalogue IGN avant de
|
||||
lancer les workers, pour que la bande soit remplie jusqu'au bord de la
|
||||
zone même pour des tuiles jamais rendues. La liste est dédupliquée sur
|
||||
absentes sont téléchargées depuis le catalogue IGN avant de lancer
|
||||
les workers, dans le sous-dossier input/edge_neighbors/ : elles ne
|
||||
doivent PAS rejoindre input/ à plat, sinon les passes globales
|
||||
(« tout input/ ») les comptent comme des tuiles à rendre et la zone
|
||||
grandit d'un anneau à chaque relance. La liste est dédupliquée sur
|
||||
tout le lot : la couronne d'un bloc contigu ne coûte qu'un passage.
|
||||
Une dalle introuvable (zone non publiée) ou en échec laisse simplement
|
||||
la bande vide — le rendu continue (best-effort).
|
||||
"""
|
||||
if self.edge_buffer <= 0:
|
||||
return
|
||||
from .dtm import _tile_coords, _NEIGHBOR_OFFSETS
|
||||
from .dtm import _tile_coords, _NEIGHBOR_OFFSETS, EDGE_NEIGHBORS_DIRNAME
|
||||
from .fetch_ign import fetch_tiles, tile_filename
|
||||
edge_dir = self.input_dir / EDGE_NEIGHBORS_DIRNAME
|
||||
wanted = set()
|
||||
for laz_file in files:
|
||||
coords = _tile_coords(Path(laz_file).name)
|
||||
@ -477,16 +480,19 @@ class LidarArchaeoPipeline:
|
||||
wanted.add((col + dcol, row + drow))
|
||||
missing = sorted(
|
||||
cr for cr in wanted
|
||||
if not (self.input_dir / tile_filename(cr[0], cr[1])).exists())
|
||||
if not (self.input_dir / tile_filename(cr[0], cr[1])).exists()
|
||||
and not (edge_dir / tile_filename(cr[0], cr[1])).exists())
|
||||
if not missing:
|
||||
return
|
||||
edge_dir.mkdir(parents=True, exist_ok=True)
|
||||
logger.info(f"Raccord des bords : {len(missing)} dalle(s) voisine(s) "
|
||||
f"absente(s) de input/ — téléchargement depuis le catalogue IGN")
|
||||
f"absente(s) — téléchargement dans {EDGE_NEIGHBORS_DIRNAME}/ "
|
||||
f"(catalogue IGN)")
|
||||
t0 = time.time()
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
with ThreadPoolExecutor(max_workers=4) as pool:
|
||||
fetched = [path for batch in pool.map(
|
||||
lambda spec: fetch_tiles(self.input_dir, [spec]), missing)
|
||||
lambda spec: fetch_tiles(edge_dir, [spec]), missing)
|
||||
for path in batch]
|
||||
logger.info(f"Raccord des bords : {len(fetched)}/{len(missing)} dalle(s) "
|
||||
f"voisine(s) téléchargée(s) ({time.time() - t0:.0f} s)")
|
||||
|
||||
@ -720,6 +720,24 @@ class TestEdgeBuffer:
|
||||
src.touch()
|
||||
assert _neighbor_laz_files(src) == []
|
||||
|
||||
def test_neighbor_found_in_edge_subdir(self, tmp_output_dir):
|
||||
"""Une voisine isolée dans edge_neighbors/ est trouvée ; la priorité
|
||||
reste à une dalle à plat dans input/."""
|
||||
from lidar_pipeline.dtm import _neighbor_laz_files, EDGE_NEIGHBORS_DIRNAME
|
||||
base = "LHD_FXX_0637_6627_PTS_LAMB93_IGN69"
|
||||
(tmp_output_dir / f"{base}.copc.laz").touch()
|
||||
edge = tmp_output_dir / EDGE_NEIGHBORS_DIRNAME
|
||||
edge.mkdir()
|
||||
# Voisine uniquement dans le sous-dossier de raccord
|
||||
(edge / "LHD_FXX_0638_6628_PTS_LAMB93_IGN69.copc.laz").touch()
|
||||
# Voisine présente aux deux endroits : la version input/ gagne
|
||||
(edge / "LHD_FXX_0636_6626_PTS_LAMB93_IGN69.copc.laz").touch()
|
||||
(tmp_output_dir / "LHD_FXX_0636_6626_PTS_LAMB93_IGN69.copc.laz").touch()
|
||||
found = _neighbor_laz_files(tmp_output_dir / f"{base}.copc.laz")
|
||||
by_name = {f.name: f for f in found}
|
||||
assert by_name["LHD_FXX_0638_6628_PTS_LAMB93_IGN69.copc.laz"].parent == edge
|
||||
assert by_name["LHD_FXX_0636_6626_PTS_LAMB93_IGN69.copc.laz"].parent == tmp_output_dir
|
||||
|
||||
def test_buffered_dtm_extends_into_neighbor(self, tmp_output_dir):
|
||||
"""MNT 24x24 (dalle 20x20 + bande 100 m), bande EST remplie à z=20 par la voisine."""
|
||||
from lidar_pipeline.dtm import create_dtm_fast, read_dtm_edge_buffer, EDGE_BUFFER_TAG
|
||||
|
||||
Reference in New Issue
Block a user