Mettre à jour la pyramide pendant le rendu et réduire son stockage
Le pipeline régénère l'inventaire après chaque dalle par défaut (tous lanceurs, sauf --no-index), avec une passe différée quand l'anti-rebond ignore une dalle. La carte surveille l'inventaire toutes les 10 s et place les tuiles des dalles modifiées en tête de file de maintenance : la pyramide suit le rendu au lieu d'attendre la fin du lot. Stockage réduit pour le Raspberry Pi : seuls les niveaux standard pairs jusqu'à z16 sont écrits sur disque, en AVIF ; les autres, dont le niveau le plus fin (~75 % de la pyramide), sont rendus à la volée avec un cache mémoire, y compris en mode cache seule. L'interface ne demande que les niveaux pairs et réduit ceux du niveau supérieur aux zooms impairs. Mesuré sur 8 dalles : ~24 Mo de pyramide avant, 1,2 Mo après. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@ -158,8 +158,8 @@ def _bg_conf():
|
||||
"LIDAR_TILE_BACKGROUND_MAX_Z", "16") or 16)),
|
||||
"scale": 1 if (os.environ.get(
|
||||
"LIDAR_TILE_BACKGROUND_SCALE", "2").strip() == "1") else 2,
|
||||
"fmt": (os.environ.get("LIDAR_TILE_BACKGROUND_FMT", "webp")
|
||||
or "webp").strip(),
|
||||
"fmt": (os.environ.get("LIDAR_TILE_BACKGROUND_FMT", "avif")
|
||||
or "avif").strip(),
|
||||
"pause": max(0.0, float(os.environ.get(
|
||||
"LIDAR_TILE_BACKGROUND_PAUSE", "1.0") or 1.0)),
|
||||
"queue_max": max(1024, int(os.environ.get(
|
||||
@ -174,10 +174,24 @@ _bg = {"threads": [], "stop": threading.Event(), "queue": deque(),
|
||||
"dernier_scan": None, "ajouts_dernier_scan": 0}}
|
||||
|
||||
|
||||
def _bg_enqueue(items):
|
||||
"""Ajoute des tuiles (layer, z, x, y) à la file : dédupliquée, bornée."""
|
||||
def _bg_enqueue(items, front=False):
|
||||
"""Ajoute des tuiles (layer, z, x, y) à la file : dédupliquée, bornée.
|
||||
|
||||
front=True : en tête de file, dans l'ordre donné (dalles tout juste
|
||||
rendues) ; une tuile déjà en file y est remontée.
|
||||
"""
|
||||
added = 0
|
||||
with _bg["guard"]:
|
||||
if front:
|
||||
fresh = [it for it in items]
|
||||
keep = set(fresh)
|
||||
rest = [it for it in _bg["queue"] if it not in keep]
|
||||
_bg["queue"].clear()
|
||||
_bg["queue"].extend(fresh[:_bg_conf()["queue_max"]])
|
||||
_bg["queue"].extend(rest[:max(0, _bg_conf()["queue_max"] - len(_bg["queue"]))])
|
||||
added = sum(1 for it in fresh if it not in _bg["queued"])
|
||||
_bg["queued"] = set(_bg["queue"])
|
||||
return added
|
||||
for item in items:
|
||||
if item in _bg["queued"] or len(_bg["queue"]) >= _bg_conf()["queue_max"]:
|
||||
continue
|
||||
@ -205,6 +219,7 @@ def background_scan(output_dir=None):
|
||||
"""
|
||||
output_dir = Path(output_dir or OUTPUT_DIR)
|
||||
conf = _bg_conf()
|
||||
first_scan = _bg["snapshot"] is None
|
||||
snapshot = {}
|
||||
changed = {}
|
||||
for layer, per_cell in tiles_mod.source_index(output_dir, force=True).items():
|
||||
@ -226,12 +241,15 @@ def background_scan(output_dir=None):
|
||||
for col, row in cells:
|
||||
bbox = tiles_mod.cell_bounds_wgs84(col, row)
|
||||
for z in range(tiles_mod.TILE_MIN_Z, conf["max_z"] + 1):
|
||||
if not tiles_mod.zoom_supported(z, conf["scale"]):
|
||||
if not (tiles_mod.zoom_supported(z, conf["scale"])
|
||||
and tiles_mod.zoom_cached(z, conf["scale"])):
|
||||
continue
|
||||
for x, y in tiles_mod.tiles_in_bounds(bbox, z):
|
||||
items.append((layer, z, x, y))
|
||||
items.sort(key=lambda t: (t[1], t[0], t[2], t[3])) # bas niveaux d'abord
|
||||
added = _bg_enqueue(items)
|
||||
# Dalles qui viennent de changer : en tête de file, devant l'arriéré —
|
||||
# la pyramide suit le rendu en cours (hors premier scan, qui construit tout).
|
||||
added = _bg_enqueue(items, front=first_scan is False)
|
||||
_bg["stats"]["scans"] += 1
|
||||
_bg["stats"]["dernier_scan"] = time.time()
|
||||
_bg["stats"]["ajouts_dernier_scan"] = added
|
||||
@ -276,13 +294,37 @@ def _bg_worker():
|
||||
_bg["stop"].wait(0.5)
|
||||
|
||||
|
||||
def _inventory_mark():
|
||||
"""Signature bon marché de l'inventaire des dalles (fichier local ou
|
||||
charge utile amont), pour déclencher un scan dès qu'une dalle arrive."""
|
||||
try:
|
||||
return (OUTPUT_DIR / "index_tiles.json").stat().st_mtime
|
||||
except OSError:
|
||||
pass
|
||||
if not tiles_mod.REMOTE_SOURCE_URL:
|
||||
return None
|
||||
try:
|
||||
payload = tiles_mod._remote_payload()
|
||||
return payload.get("stamp") if payload else None
|
||||
except Exception: # noqa: BLE001
|
||||
return None
|
||||
|
||||
|
||||
BG_WATCH_S = 10.0 # surveillance de l'inventaire pendant les runs
|
||||
|
||||
|
||||
def _bg_poller():
|
||||
last_scan, last_mark = 0.0, _inventory_mark()
|
||||
while not _bg["stop"].is_set():
|
||||
try:
|
||||
background_scan()
|
||||
except Exception as e: # noqa: BLE001 — prochain scan dans INTERVAL
|
||||
logger.warning(f"Maintenance des tuiles : scan impossible ({e})")
|
||||
_bg["stop"].wait(_bg_conf()["interval"])
|
||||
mark = _inventory_mark()
|
||||
due = time.time() - last_scan >= _bg_conf()["interval"]
|
||||
if due or mark != last_mark:
|
||||
try:
|
||||
background_scan()
|
||||
except Exception as e: # noqa: BLE001 — prochain scan au prochain tour
|
||||
logger.warning(f"Maintenance des tuiles : scan impossible ({e})")
|
||||
last_scan, last_mark = time.time(), mark
|
||||
_bg["stop"].wait(min(BG_WATCH_S, _bg_conf()["interval"]))
|
||||
|
||||
|
||||
def start_background():
|
||||
@ -570,7 +612,7 @@ async def tile(layer: str, z: int, x: int, name: str, v: Optional[str] = None):
|
||||
if not (0 <= x < n and 0 <= y < n):
|
||||
raise HTTPException(404, "tuile hors grille")
|
||||
|
||||
if _env_flag("LIDAR_TILE_CACHE_ONLY"):
|
||||
if _env_flag("LIDAR_TILE_CACHE_ONLY") and tiles_mod.zoom_cached(z, scale):
|
||||
# Mode cache seule : aucune navigation ne déclenche de RENDU local.
|
||||
# Une tuile absente (ex. zoom > LIDAR_TILE_BACKGROUND_MAX_Z, jamais
|
||||
# couverte par la maintenance) est rapatriée du serveur amont
|
||||
@ -847,7 +889,10 @@ def map_meta():
|
||||
"defaults_saved": bool(saved),
|
||||
# L'interface consomme des tuiles 512 px (2× moins de requêtes qu'en
|
||||
# 256 : décisif en HTTP/1.1) ; les clients OSM gardent le 256 canonique.
|
||||
"tile_url": "tiles/{layer}/{z}/{x}/{y}@2x.webp",
|
||||
"tile_url": "tiles/{layer}/{z}/{x}/{y}@2x.avif",
|
||||
# Niveaux standard pairs seulement (stockage réduit) : l'interface
|
||||
# réduit ceux du niveau supérieur aux zooms impairs.
|
||||
"even_levels": tiles_mod.TILE_EVEN_LEVELS,
|
||||
"tile_size": tiles_mod.TILE_SIZE * 2,
|
||||
"zoom_offset": -1,
|
||||
"min_zoom": tiles_mod.TILE_MIN_Z,
|
||||
|
||||
Reference in New Issue
Block a user