Speed up Wereldexport map loading dramatically.

Limit world overview payload, optimize SQL joins, cache bundle and tiles, reuse map instance, and show loading state.
This commit is contained in:
Aissa
2026-07-19 18:42:47 +00:00
parent 7289b770e5
commit 6856d09cd1
6 changed files with 198 additions and 45 deletions
+40 -7
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
import os
import time
from typing import Any, Optional
import httpx
@@ -15,6 +16,14 @@ BASE = Path(__file__).resolve().parent.parent.parent
templates = Jinja2Templates(directory=str(BASE / "templates"))
TOOLS = os.getenv("TOOLS_API_URL", "http://tools-api:8700").rstrip("/")
_TILE_CACHE: dict[str, tuple[float, bytes]] = {}
_TILE_CACHE_TTL = 86400.0
_TILE_CACHE_MAX = 900
_BUNDLE_CACHE: dict[str, tuple[float, bytes]] = {}
_BUNDLE_CACHE_TTL = 120.0
async def _proxy(method: str, path: str, **kwargs) -> Any:
url = f"{TOOLS}/export-intel{path}"
@@ -161,7 +170,12 @@ async def api_gov_sources(country: Optional[str] = None):
@router.get("/api/map/tiles/{z}/{x}/{y}.png")
async def map_tile_proxy(z: int, x: int, y: int) -> Response:
"""Proxy Carto dark basemap tiles via cockpit (no client internet needed)."""
"""Proxy Carto dark basemap tiles via cockpit (cached, no client internet needed)."""
key = f"{z}/{x}/{y}"
hit = _TILE_CACHE.get(key)
if hit and time.time() - hit[0] < _TILE_CACHE_TTL:
return Response(content=hit[1], media_type="image/png", headers={"Cache-Control": "public, max-age=604800"})
host = ("a", "b", "c", "d")[(x + y) % 4]
url = f"https://{host}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png"
try:
@@ -169,11 +183,12 @@ async def map_tile_proxy(z: int, x: int, y: int) -> Response:
r = await client.get(url)
if r.status_code != 200:
return Response(status_code=502, content=b"")
return Response(
content=r.content,
media_type="image/png",
headers={"Cache-Control": "public, max-age=604800"},
)
body = r.content
if len(_TILE_CACHE) >= _TILE_CACHE_MAX:
oldest = min(_TILE_CACHE.items(), key=lambda item: item[1][0])[0]
_TILE_CACHE.pop(oldest, None)
_TILE_CACHE[key] = (time.time(), body)
return Response(content=body, media_type="image/png", headers={"Cache-Control": "public, max-age=604800"})
except Exception:
return Response(status_code=502, content=b"")
@@ -193,7 +208,25 @@ async def api_map_bundle(
"entity_types": entity_types, "q": q, "halal_min": halal_min,
"favorite_only": favorite_only, "crm_linked": crm_linked,
}.items() if v is not None and v is not False}
return await _proxy("GET", "/map/bundle", params=params)
import hashlib
import json
cache_key = hashlib.md5(json.dumps(params, sort_keys=True, default=str).encode()).hexdigest()
hit = _BUNDLE_CACHE.get(cache_key)
if hit and time.time() - hit[0] < _BUNDLE_CACHE_TTL:
return Response(content=hit[1], media_type="application/json")
url = f"{TOOLS}/export-intel/map/bundle"
async with httpx.AsyncClient(timeout=60.0) as client:
r = await client.get(url, params=params)
if r.status_code >= 400:
return {"error": r.text, "status": r.status_code}
body = r.content
if len(_BUNDLE_CACHE) > 32:
oldest = min(_BUNDLE_CACHE.items(), key=lambda item: item[1][0])[0]
_BUNDLE_CACHE.pop(oldest, None)
_BUNDLE_CACHE[cache_key] = (time.time(), body)
return Response(content=body, media_type="application/json")
@router.post("/api/export-intel/entities/favorites")