import time import urllib.error import urllib.request from pathlib import Path import numpy as np from PIL import Image from .coords import SLICE, TILE_PX, mosaic_origin_px, slice_tiles COMMIT = "4093e8c874e480d47c38b37fe7a404f8b0c5275e" BASE = f"https://raw.githubusercontent.com/rolux/gtadb.org/{COMMIT}/maps/tiles/6/yanis%2C15/5" UA = {"User-Agent": "leonida-extract/0.1 (local hobby project)"} def download_tiles(cache: Path) -> None: """Lädt alle Kacheln des Ausschnitts; vorhandene werden übersprungen.""" cache.mkdir(parents=True, exist_ok=True) missing = [] for row, col in slice_tiles(): name = f"5,{row},{col}.jpg" dst = cache / name if dst.exists(): continue for attempt in range(3): try: req = urllib.request.Request(f"{BASE}/{name}", headers=UA) dst.write_bytes(urllib.request.urlopen(req, timeout=30).read()) break except (urllib.error.URLError, TimeoutError): time.sleep(2 ** attempt) else: missing.append(name) if missing: raise RuntimeError(f"{len(missing)} Kacheln fehlen: {missing[:5]} ...") def assemble(load_tile) -> np.ndarray: """Setzt die Kacheln zusammen und schneidet auf den Ausschnitt zu.""" tiles = slice_tiles() r0 = min(r for r, _ in tiles) c0 = min(c for _, c in tiles) rows = max(r for r, _ in tiles) - r0 + 1 cols = max(c for _, c in tiles) - c0 + 1 full = np.zeros((rows * TILE_PX, cols * TILE_PX, 3), dtype=np.uint8) for row, col in tiles: y, x = (row - r0) * TILE_PX, (col - c0) * TILE_PX full[y:y + TILE_PX, x:x + TILE_PX] = load_tile(row, col) ox, oy = mosaic_origin_px() w = SLICE["x1"] - SLICE["x0"] h = SLICE["y1"] - SLICE["y0"] return full[oy:oy + h, ox:ox + w] def load_mosaic(cache: Path) -> np.ndarray: def load_tile(row: int, col: int) -> np.ndarray: with Image.open(cache / f"5,{row},{col}.jpg") as img: return np.asarray(img.convert("RGB")) return assemble(load_tile)