Diffuser la qualité des dalles dans l'inventaire et la conserver sur la carte légère
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@ -1220,11 +1220,15 @@ def build_index(output_dir, output_format='avif'):
|
|||||||
for k in _VIZ_FALLBACK_ORDER if k in all_viz_keys}
|
for k in _VIZ_FALLBACK_ORDER if k in all_viz_keys}
|
||||||
for k in sorted(all_viz_keys):
|
for k in sorted(all_viz_keys):
|
||||||
viz_meta.setdefault(k, {'label': VIZ_LABELS.get(k, k)})
|
viz_meta.setdefault(k, {'label': VIZ_LABELS.get(k, k)})
|
||||||
|
from .quality import load_quality_table
|
||||||
inventory_path = output_dir / 'index_tiles.json'
|
inventory_path = output_dir / 'index_tiles.json'
|
||||||
inventory_path.write_text(json.dumps({
|
inventory_path.write_text(json.dumps({
|
||||||
'tiles': display_tiles,
|
'tiles': display_tiles,
|
||||||
'viz_meta': viz_meta,
|
'viz_meta': viz_meta,
|
||||||
'stats': {'n_tiles': len(display_tiles)},
|
'stats': {'n_tiles': len(display_tiles)},
|
||||||
|
# Qualité par dalle (encart de l'export PDF) : recopiée sur les
|
||||||
|
# machines légères pour rester disponible amont éteint.
|
||||||
|
'quality': load_quality_table(output_dir),
|
||||||
}, ensure_ascii=False), encoding='utf-8')
|
}, ensure_ascii=False), encoding='utf-8')
|
||||||
|
|
||||||
logger.info(f"Inventaire généré : {inventory_path} "
|
logger.info(f"Inventaire généré : {inventory_path} "
|
||||||
|
|||||||
@ -653,3 +653,15 @@ def test_urls_versioned_by_served_file_mtime(tmp_path):
|
|||||||
url_after = tiles05b[0]['viz']['hillshade_multi']['thumb']
|
url_after = tiles05b[0]['viz']['hillshade_multi']['thumb']
|
||||||
assert url_after.split('?')[0] == url_before.split('?')[0]
|
assert url_after.split('?')[0] == url_before.split('?')[0]
|
||||||
assert url_after != url_before
|
assert url_after != url_before
|
||||||
|
|
||||||
|
|
||||||
|
def test_inventory_contains_quality_table(tmp_path):
|
||||||
|
import json
|
||||||
|
from lidar_pipeline.index import build_index
|
||||||
|
from lidar_pipeline.quality import write_quality
|
||||||
|
from lidar_pipeline.tests.test_tiles import _make_dalle
|
||||||
|
base = _make_dalle(tmp_path, 1054, 6882, ["relief_oriente"])
|
||||||
|
write_quality(tmp_path, base, {"version": 1, "ground_density": 7.5})
|
||||||
|
build_index(tmp_path, "webp")
|
||||||
|
inv = json.loads((tmp_path / "index_tiles.json").read_text(encoding="utf-8"))
|
||||||
|
assert inv["quality"] == {base: {"version": 1, "ground_density": 7.5}}
|
||||||
|
|||||||
@ -712,3 +712,20 @@ def test_webp_subtiles_indexed_and_rendered_nearest(tmp_path):
|
|||||||
assert img is not None
|
assert img is not None
|
||||||
grays = {p[0] for p in img.getdata() if p[3] == 255}
|
grays = {p[0] for p in img.getdata() if p[3] == 255}
|
||||||
assert grays <= {0, 255} # aucun gris intermédiaire inventé
|
assert grays <= {0, 255} # aucun gris intermédiaire inventé
|
||||||
|
|
||||||
|
|
||||||
|
def test_remote_quality_persisted_locally(tmp_path, monkeypatch):
|
||||||
|
"""La table qualité amont est recopiée en sidecars locaux (Pi autonome)."""
|
||||||
|
from lidar_pipeline import tiles
|
||||||
|
from lidar_pipeline.quality import read_quality
|
||||||
|
payload = _remote_payload()
|
||||||
|
base = "LHD_FXX_1054_6882_PTS_LAMB93_IGN69"
|
||||||
|
payload["quality"] = {base: {"version": 1, "ground_density": 3.0},
|
||||||
|
"../evasion": {"version": 1}}
|
||||||
|
monkeypatch.setattr(tiles, "REMOTE_SOURCE_URL", "http://amont")
|
||||||
|
monkeypatch.setattr(tiles, "_remote_payload", lambda force=False: payload)
|
||||||
|
monkeypatch.setattr(tiles, "_remote_cache", dict(tiles._remote_cache, index=None, built=None))
|
||||||
|
tiles._remote_index(tmp_path, force=True)
|
||||||
|
assert read_quality(tmp_path, base) == {"version": 1, "ground_density": 3.0}
|
||||||
|
assert not (tmp_path / "evasion.json").exists()
|
||||||
|
assert list((tmp_path / "quality").iterdir()) == [tmp_path / "quality" / f"{base}.json"]
|
||||||
|
|||||||
@ -305,6 +305,30 @@ def _remote_payload(force=False):
|
|||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
|
||||||
|
_SAFE_BASENAME = re.compile(r"^[A-Za-z0-9_.-]+$")
|
||||||
|
|
||||||
|
|
||||||
|
def _persist_remote_quality(output_dir, payload):
|
||||||
|
"""Recopie la table qualité amont en sidecars locaux.
|
||||||
|
|
||||||
|
L'encart qualité de l'export PDF lit toujours le disque local : la
|
||||||
|
carte autonome (amont éteint) garde ainsi les dernières valeurs connues.
|
||||||
|
Returns: nombre de sidecars (ré)écrits.
|
||||||
|
"""
|
||||||
|
from .quality import QUALITY_VERSION, write_quality
|
||||||
|
written = 0
|
||||||
|
for base, data in (payload.get("quality") or {}).items():
|
||||||
|
if (not isinstance(base, str) or not _SAFE_BASENAME.match(base)
|
||||||
|
or base.startswith(".") or not isinstance(data, dict)
|
||||||
|
or data.get("version") != QUALITY_VERSION):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
written += bool(write_quality(output_dir, base, data))
|
||||||
|
except OSError as e:
|
||||||
|
logger.debug(f"Sidecar qualité non recopié ({base}) : {e}")
|
||||||
|
return written
|
||||||
|
|
||||||
|
|
||||||
def _remote_index(output_dir, force=False):
|
def _remote_index(output_dir, force=False):
|
||||||
"""Inventaire construit depuis l'amont : sources non encore rapatriées.
|
"""Inventaire construit depuis l'amont : sources non encore rapatriées.
|
||||||
|
|
||||||
@ -320,6 +344,7 @@ def _remote_index(output_dir, force=False):
|
|||||||
and _remote_cache.get("built") is payload):
|
and _remote_cache.get("built") is payload):
|
||||||
return _remote_cache["index"]
|
return _remote_cache["index"]
|
||||||
output_dir = Path(output_dir)
|
output_dir = Path(output_dir)
|
||||||
|
_persist_remote_quality(output_dir, payload)
|
||||||
layers = {}
|
layers = {}
|
||||||
for entry in payload["tiles"]:
|
for entry in payload["tiles"]:
|
||||||
col, row = entry.get("col"), entry.get("row")
|
col, row = entry.get("col"), entry.get("row")
|
||||||
|
|||||||
Reference in New Issue
Block a user