initial commit
This commit is contained in:
93
api/app/services/warncells.py
Normal file
93
api/app/services/warncells.py
Normal file
@@ -0,0 +1,93 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
from app.cache import cache_get, cache_set
|
||||
from app.config import CACHE_TTL_GEOCODE, DWD_WMS_URL
|
||||
from app.http_client import get_client
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
WFS_URL = f"{DWD_WMS_URL.rsplit('/wms', 1)[0]}/ows"
|
||||
# Landkreise reicht fuer warnapp_landkreise/json/warnings.json
|
||||
WFS_LAYERS = ("dwd:Warngebiete_Kreise",)
|
||||
|
||||
|
||||
async def _wfs_intersects(layer: str, lat: float, lon: float) -> list[dict[str, Any]]:
|
||||
params = {
|
||||
"service": "WFS",
|
||||
"version": "2.0.0",
|
||||
"request": "GetFeature",
|
||||
"typeName": layer,
|
||||
"outputFormat": "application/json",
|
||||
"count": 5,
|
||||
"CQL_FILTER": f"INTERSECTS(SHAPE,POINT({lat} {lon}))",
|
||||
}
|
||||
client = get_client()
|
||||
response = await client.get(WFS_URL, params=params, timeout=8.0)
|
||||
response.raise_for_status()
|
||||
payload = response.json()
|
||||
results = []
|
||||
for feature in payload.get("features", []):
|
||||
props = feature.get("properties") or {}
|
||||
cell_id = props.get("WARNCELLID")
|
||||
if cell_id is None:
|
||||
continue
|
||||
results.append(
|
||||
{
|
||||
"warncell_id": str(cell_id),
|
||||
"name": props.get("NAME"),
|
||||
"short_name": props.get("KURZNAME"),
|
||||
"layer": layer.split(":")[-1],
|
||||
"state": props.get("BL"),
|
||||
}
|
||||
)
|
||||
return results
|
||||
|
||||
|
||||
def _related_cell_ids(cell_id: str) -> set[str]:
|
||||
ids = {cell_id}
|
||||
if len(cell_id) >= 9:
|
||||
core = cell_id[1:]
|
||||
ids.add(f"1{core}")
|
||||
ids.add(f"9{core}")
|
||||
if cell_id.startswith("9"):
|
||||
ids.add(f"1{cell_id[1:]}")
|
||||
elif cell_id.startswith("1"):
|
||||
ids.add(f"9{cell_id[1:]}")
|
||||
return ids
|
||||
|
||||
|
||||
async def resolve_warncells(lat: float, lon: float) -> dict[str, Any]:
|
||||
cache_key = f"warncell:{lat:.4f}:{lon:.4f}"
|
||||
cached = await cache_get(cache_key)
|
||||
if cached:
|
||||
return cached
|
||||
|
||||
layer_results = await asyncio.gather(
|
||||
*[_wfs_intersects(layer, lat, lon) for layer in WFS_LAYERS],
|
||||
return_exceptions=True,
|
||||
)
|
||||
areas: list[dict[str, Any]] = []
|
||||
for layer, result in zip(WFS_LAYERS, layer_results):
|
||||
if isinstance(result, Exception):
|
||||
logger.warning("WFS warncell lookup failed for %s: %s", layer, result)
|
||||
continue
|
||||
areas.extend(result)
|
||||
|
||||
cell_ids: set[str] = set()
|
||||
for area in areas:
|
||||
cell_ids.update(_related_cell_ids(area["warncell_id"]))
|
||||
|
||||
payload = {
|
||||
"lat": lat,
|
||||
"lon": lon,
|
||||
"areas": areas,
|
||||
"cell_ids": sorted(cell_ids),
|
||||
}
|
||||
await cache_set(cache_key, payload, CACHE_TTL_GEOCODE)
|
||||
return payload
|
||||
Reference in New Issue
Block a user