Projeter les coins du bbox de zone (PROJ) au lieu d exiger un rectangle plein
This commit is contained in:
@ -917,19 +917,44 @@ def _build_subtiles(tile, offered_viz_keys, output_dir, sub_dir_name):
|
|||||||
return usable
|
return usable
|
||||||
|
|
||||||
|
|
||||||
|
def _l93_points_to_wgs84_corners(xs, ys):
|
||||||
|
"""Transforme des points L93 en [[lat, lon], …] (ordre conservé).
|
||||||
|
|
||||||
|
rasterio.warp (PROJ exact) si disponible, sinon pyproj, sinon
|
||||||
|
l'approximation affine (webapp légère sans GDAL — précision ~km).
|
||||||
|
Même transformation que attach_gps_bounds : les coins produits
|
||||||
|
coïncident exactement avec ceux des dalles.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from rasterio.warp import transform as warp_transform
|
||||||
|
lons, lats = warp_transform('EPSG:2154', 'EPSG:4326', list(xs), list(ys))
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
from pyproj import Transformer
|
||||||
|
tr = Transformer.from_crs('EPSG:2154', 'EPSG:4326', always_xy=True)
|
||||||
|
lons, lats = tr.transform(list(xs), list(ys))
|
||||||
|
except ImportError:
|
||||||
|
pairs = [_approx_l93_to_wgs84(x, y) for x, y in zip(xs, ys)]
|
||||||
|
lons = [p[0] for p in pairs]
|
||||||
|
lats = [p[1] for p in pairs]
|
||||||
|
return [[la, lo] for la, lo in zip(lats, lons)]
|
||||||
|
|
||||||
|
|
||||||
def _build_overview(zone_records, output_dir):
|
def _build_overview(zone_records, output_dir):
|
||||||
"""Assemble les mosaïques d'overview par couche pour la vue dézoomée.
|
"""Assemble les mosaïques d'overview par couche pour la vue dézoomée.
|
||||||
|
|
||||||
Une image WebP par (zone géographique × couche), couvrant la grille L93
|
Une image WebP par (zone géographique × couche), couvrant le bbox L93
|
||||||
des dalles de la zone (cases sans dalle transparentes), assemblée depuis
|
des dalles de la zone (cases sans dalle transparentes), assemblée depuis
|
||||||
les vignettes index_thumbs (256 px/km) : au dézoom l'interface l'affiche
|
les vignettes index_thumbs (256 px/km) : au dézoom l'interface l'affiche
|
||||||
à la place des vignettes individuelles — 1 requête et ~30 Mo décodés par
|
à la place des vignettes individuelles — 1 requête et ~30 Mo décodés par
|
||||||
couche et par zone au lieu de centaines. Une mosaïque par zone : les
|
couche et par zone au lieu de centaines. Une mosaïque par zone : les
|
||||||
grilles distantes (plusieurs secteurs, parfois à des centaines de km)
|
grilles distantes (plusieurs secteurs, parfois à des centaines de km)
|
||||||
n'ont pas de coins partagés et surtout pas la même échelle utile.
|
n'ont pas de coins partagés et surtout pas la même échelle utile. Les
|
||||||
Résolution plafonnée à 256 px/km et 3072 px de côté : l'overview n'est
|
coins du bbox sont projetés par la même transformation que les dalles —
|
||||||
affiché que tant qu'il reste au moins 1:1 avec l'écran, jamais étiré
|
la mosaïque s'emboîte exactement sur les vignettes, même si la zone
|
||||||
donc jamais flou.
|
n'est pas un rectangle plein. Résolution plafonnée à 256 px/km et
|
||||||
|
3072 px de côté : l'overview n'est affiché que tant qu'il reste au
|
||||||
|
moins 1:1 avec l'écran, jamais étiré donc jamais flou.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
zone_records: enregistrements par zone ({'tiles': [dalle...]}) avec
|
zone_records: enregistrements par zone ({'tiles': [dalle...]}) avec
|
||||||
@ -960,28 +985,18 @@ def _build_overview(zone_records, output_dir):
|
|||||||
max_col = max(t['col'] for t in have)
|
max_col = max(t['col'] for t in have)
|
||||||
min_row = min(t['row'] for t in have)
|
min_row = min(t['row'] for t in have)
|
||||||
max_row = max(t['row'] for t in have)
|
max_row = max(t['row'] for t in have)
|
||||||
# Lattice exact des coins (km L93 → WGS84) : les coins partagés de
|
|
||||||
# dalles voisines proviennent de la même transformation pyproj →
|
|
||||||
# cohérents, la mosaïque s'emboîte exactement comme les vignettes.
|
|
||||||
lattice = {}
|
|
||||||
for t in have:
|
|
||||||
c = t['corners']
|
|
||||||
lattice[(t['col'], t['row'])] = c[0] # SW
|
|
||||||
lattice[(t['col'] + 1, t['row'])] = c[1] # SE
|
|
||||||
lattice[(t['col'] + 1, t['row'] + 1)] = c[2] # NE
|
|
||||||
lattice[(t['col'], t['row'] + 1)] = c[3] # NW
|
|
||||||
quad = [(min_col, min_row), (max_col + 1, min_row),
|
|
||||||
(max_col + 1, max_row + 1), (min_col, max_row + 1)]
|
|
||||||
if any(k not in lattice for k in quad):
|
|
||||||
logger.debug(f"Overview : coins de grille absents pour "
|
|
||||||
f"{zone.get('label', '?')} — zone ignorée")
|
|
||||||
continue
|
|
||||||
km_w = max_col + 1 - min_col
|
km_w = max_col + 1 - min_col
|
||||||
km_h = max_row + 1 - min_row
|
km_h = max_row + 1 - min_row
|
||||||
px_per_km = max(4, min(_OVERVIEW_PX_PER_KM,
|
px_per_km = max(4, min(_OVERVIEW_PX_PER_KM,
|
||||||
_OVERVIEW_MAX_PX // max(km_w, km_h)))
|
_OVERVIEW_MAX_PX // max(km_w, km_h)))
|
||||||
w, h = km_w * px_per_km, km_h * px_per_km
|
w, h = km_w * px_per_km, km_h * px_per_km
|
||||||
corners = [lattice[k] for k in quad]
|
# Coins SW, SE, NE, NW du bbox L93 (dalle (col,row) : Y ∈ [(row−1),
|
||||||
|
# row] km — cf. attach_gps_bounds), projetés comme les coins de dalles.
|
||||||
|
xs = [min_col * 1000, (max_col + 1) * 1000,
|
||||||
|
(max_col + 1) * 1000, min_col * 1000]
|
||||||
|
ys = [(min_row - 1) * 1000, (min_row - 1) * 1000,
|
||||||
|
max_row * 1000, max_row * 1000]
|
||||||
|
corners = _l93_points_to_wgs84_corners(xs, ys)
|
||||||
viz_keys = []
|
viz_keys = []
|
||||||
for t in have:
|
for t in have:
|
||||||
for k in t['viz']:
|
for k in t['viz']:
|
||||||
|
|||||||
Reference in New Issue
Block a user