Fix map loading and tab navigation v0.4.1

This commit is contained in:
dmo committed 2026-07-23 09:50:45 +02:00
1 parent e290bd1fff
commit 9b7d302064
9 files changed
+148 -47

No files matched your search

+63 -3
View File
@@ -11,6 +11,7 @@ import time
import urllib.error
import urllib.parse
import urllib.request
from collections import OrderedDict
from datetime import datetime
from pathlib import Path
from typing import Annotated, Any
@@ -21,12 +22,15 @@ from fastapi.staticfiles import StaticFiles
APP_NAME = os.getenv("APP_NAME", "Photo Date Editor")
APP_URL = os.getenv("APP_URL", "http://localhost:8080")
APP_VERSION = "0.4.0"
APP_VERSION = "0.4.1"
MAX_UPLOAD_MB = int(os.getenv("MAX_UPLOAD_MB", "150"))
MAX_UPLOAD_BYTES = MAX_UPLOAD_MB * 1024 * 1024
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO").upper()
MAP_TILE_URL = os.getenv("MAP_TILE_URL", "https://tile.openstreetmap.org/{z}/{x}/{y}.png")
MAP_TILE_SOURCE_URL = os.getenv(
"MAP_TILE_SOURCE_URL",
os.getenv("MAP_TILE_URL", "https://tile.openstreetmap.org/{z}/{x}/{y}.png"),
)
MAP_ATTRIBUTION = os.getenv(
"MAP_ATTRIBUTION",
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>',
@@ -43,6 +47,9 @@ GEOCODER_USER_AGENT = os.getenv(
_geocode_lock = asyncio.Lock()
_geocode_cache: dict[str, list[dict[str, Any]]] = {}
_last_geocode_request = 0.0
_tile_cache: OrderedDict[str, tuple[bytes, str]] = OrderedDict()
_tile_cache_limit = 512
_tile_lock = asyncio.Lock()
logging.basicConfig(
level=getattr(logging, LOG_LEVEL, logging.INFO),
@@ -68,7 +75,7 @@ def config() -> dict[str, str | int | float]:
"appUrl": APP_URL,
"version": APP_VERSION,
"maxUploadMb": MAX_UPLOAD_MB,
"mapTileUrl": MAP_TILE_URL,
"mapTileUrl": "/api/map/tiles/{z}/{x}/{y}.png",
"mapAttribution": MAP_ATTRIBUTION,
"defaultMapLat": DEFAULT_MAP_LAT,
"defaultMapLon": DEFAULT_MAP_LON,
@@ -77,6 +84,59 @@ def config() -> dict[str, str | int | float]:
def _fetch_map_tile(z: int, x: int, y: int) -> tuple[bytes, str]:
tile_url = (
MAP_TILE_SOURCE_URL
.replace("{z}", str(z))
.replace("{x}", str(x))
.replace("{y}", str(y))
)
request = urllib.request.Request(
tile_url,
headers={
"User-Agent": GEOCODER_USER_AGENT,
"Accept": "image/avif,image/webp,image/png,image/*,*/*;q=0.8",
},
)
with urllib.request.urlopen(request, timeout=20) as response:
content_type = response.headers.get_content_type() or "image/png"
return response.read(), content_type
@app.get("/api/map/tiles/{z}/{x}/{y}.png", include_in_schema=False)
async def map_tile(z: int, x: int, y: int) -> Response:
if not 0 <= z <= 19:
raise HTTPException(status_code=404, detail="Unsupported map zoom level.")
limit = 1 << z
if not 0 <= x < limit or not 0 <= y < limit:
raise HTTPException(status_code=404, detail="Invalid map tile coordinates.")
cache_key = f"{z}/{x}/{y}"
cached = _tile_cache.get(cache_key)
if cached is not None:
_tile_cache.move_to_end(cache_key)
payload, content_type = cached
return Response(payload, media_type=content_type, headers={"Cache-Control": "public, max-age=86400"})
async with _tile_lock:
cached = _tile_cache.get(cache_key)
if cached is None:
try:
cached = await asyncio.to_thread(_fetch_map_tile, z, x, y)
except (urllib.error.URLError, TimeoutError) as exc:
logger.warning("Map tile request failed for %s: %s", cache_key, exc)
raise HTTPException(status_code=502, detail="The map tile service could not be reached.") from exc
_tile_cache[cache_key] = cached
if len(_tile_cache) > _tile_cache_limit:
_tile_cache.popitem(last=False)
else:
_tile_cache.move_to_end(cache_key)
payload, content_type = cached
return Response(payload, media_type=content_type, headers={"Cache-Control": "public, max-age=86400"})
def _fetch_geocode_results(query: str) -> list[dict[str, Any]]:
parameters = urllib.parse.urlencode(
{