Ajouter la fiche de dalle IGN et la rose des vents, imposer la génération

Un clic sélectionne toujours la dalle sous le curseur (cadre sur la carte),
générée ou non : emprise, rendu et recalage des passes s'affichent tout de
suite ; la fiche IGN (date et heure du scan LiDAR, capteurs, mission,
édition, nombre de points, lien de téléchargement du .copc.laz) arrive à
part via /api/map/ign, mise en cache, sans jamais bloquer la sélection.

Une rose des vents donne la couleur de chaque orientation de pente du
relief orienté, avec la même formule CIELAB que le rendu.

La génération impose la classification IGN (sol) et le raccord de 100 m
avec les dalles voisines : plus de réglage dans la carte, défauts CLI
alignés.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Antoine Jacquin
2026-09-27 01:57:04 +02:00
parent d32502b74e
commit f836408d6f
7 changed files with 427 additions and 109 deletions

View File

@ -70,6 +70,17 @@ def _bbox_wgs84(col, row):
ys = [(row - 1) * 1000] * 2 + [row * 1000] * 2
lons, lats = warp_transform('EPSG:2154', 'EPSG:4326', xs, ys)
except Exception:
lons = None
if lons is None:
try: # image légère (carte) : pyproj sans rasterio
from pyproj import Transformer
tr = Transformer.from_crs("EPSG:2154", "EPSG:4326", always_xy=True)
xs = [col * 1000, (col + 1) * 1000, col * 1000, (col + 1) * 1000]
ys = [(row - 1) * 1000] * 2 + [row * 1000] * 2
lons, lats = tr.transform(xs, ys)
except Exception:
lons = None
if lons is None:
from .index import _approx_l93_to_wgs84
pts = [_approx_l93_to_wgs84(x, y)
for x in (col * 1000, (col + 1) * 1000)
@ -93,12 +104,48 @@ def match_feature(features, col, row):
def find_tile_url(col, row, timeout=20, max_pages=5):
"""Cherche l'URL de téléchargement de la dalle (col,row) dans le catalogue STAC.
Suit la pagination du catalogue (lien rel=next) : sans elle, une dalle
au-delà de la première page de résultats semblait « introuvable ».
Returns:
URL (str) ou None si la dalle n'est pas (encore) publiée par l'IGN.
"""
feature = find_tile_feature(col, row, timeout=timeout, max_pages=max_pages)
return feature.get("assets", {}).get("data", {}).get("href") if feature else None
def tile_ign_metadata(col, row, timeout=10):
"""Fiche IGN d'une dalle : acquisition, capteurs, édition, téléchargement.
Returns:
dict normalisé (found=False si la dalle n'est pas publiée).
"""
feature = find_tile_feature(col, row, timeout=timeout)
if not feature:
return {"found": False}
p = feature.get("properties", {})
self_link = next((link.get("href") for link in feature.get("links", [])
if link.get("rel") == "self"), None)
return {
"found": True,
"download_url": feature.get("assets", {}).get("data", {}).get("href"),
"acquisition_start": p.get("start_datetime") or p.get("lidarhd:date_debut_acquisition"),
"acquisition_end": p.get("end_datetime") or p.get("lidarhd:date_fin_acquisition"),
"sensors": p.get("lidarhd:capteur") or [],
"mission": p.get("lidarhd:code_mission"),
"acquisition_operator": p.get("lidarhd:moe_acquisition"),
"acquisition_owner": p.get("lidarhd:moa_acquisition"),
"edition_date": p.get("lidarhd:date_edition"),
"classification_process": p.get("lidarhd:procede_classement"),
"points": p.get("lidarhd:nombre_points") or p.get("pc:count"),
"altimetry": p.get("lidarhd:systeme_altimetrique"),
"stac_url": self_link,
}
def find_tile_feature(col, row, timeout=20, max_pages=5):
"""Item STAC de la dalle (col,row), ou None si non publiée.
Suit la pagination du catalogue (lien rel=next) : sans elle, une dalle
au-delà de la première page de résultats semblait « introuvable ».
"""
w, s, e, n = _bbox_wgs84(col, row)
query = urllib.parse.urlencode({"bbox": f"{w:.6f},{s:.6f},{e:.6f},{n:.6f}", "limit": 50})
url = f"{_STAC_ITEMS_URL}?{query}"
@ -108,7 +155,7 @@ def find_tile_url(col, row, timeout=20, max_pages=5):
data = json.loads(response.read().decode("utf-8"))
feature = match_feature(data.get("features", []), col, row)
if feature:
return feature.get("assets", {}).get("data", {}).get("href")
return feature
nxt = next((link.get("href") for link in data.get("links", [])
if link.get("rel") == "next" and link.get("href")), None)
if not nxt: