gateway/modules/connectors/connectorZhWfsParcels.py

89 lines
3.1 KiB
Python

"""
Swiss Parcel (Liegenschaften) Connector
Fetches parcel data from geodienste.ch OGC API Features (Amtliche Vermessung).
Covers all of Switzerland. Returns GeoJSON in WGS84.
Uses: geodienste.ch OGC API - RESF collection (Liegenschaften)
No config override needed - this is the single working solution.
"""
import logging
from typing import Dict, Any
import requests
logger = logging.getLogger(__name__)
# geodienste.ch OGC API - RESF = Liegenschaften (parcels), all Switzerland
# API returns WGS84 directly when bbox-crs=EPSG:2056 is used
_OGC_API_BASE = "https://www.geodienste.ch/db/av_0/deu/ogcapi/collections/RESF/items"
_MAX_ITEMS = 2000
_TIMEOUT = 30
class ZhWfsParcelsConnector:
"""
Connector for Swiss parcel (Liegenschaften) data via geodienste.ch OGC API.
Returns GeoJSON FeatureCollection in WGS84.
"""
def __init__(self, timeout: int = _TIMEOUT):
self.timeout = timeout
logger.info("ZhWfsParcelsConnector initialized (geodienste.ch OGC API)")
def get_parcels_by_bbox(self, bbox: str) -> Dict[str, Any]:
"""
Fetch parcels within bounding box.
Returns GeoJSON FeatureCollection in WGS84 (EPSG:4326).
Args:
bbox: Bounding box as "minx,miny,maxx,maxy" in LV95 (EPSG:2056)
Returns:
GeoJSON FeatureCollection with geometries in WGS84
"""
try:
parts = [p.strip() for p in bbox.split(",")]
if len(parts) != 4:
raise ValueError(f"Invalid bbox: expected minx,miny,maxx,maxy, got {bbox}")
minx, miny, maxx, maxy = (float(p) for p in parts)
params = {
"f": "json",
"limit": _MAX_ITEMS,
"bbox": f"{minx},{miny},{maxx},{maxy}",
"bbox-crs": "http://www.opengis.net/def/crs/EPSG/0/2056",
}
logger.debug(f"Requesting parcels: bbox={bbox}")
resp = requests.get(_OGC_API_BASE, params=params, timeout=self.timeout)
if resp.status_code != 200:
logger.error(f"Parcel API failed: status={resp.status_code}, body={resp.text[:500]}")
return {"type": "FeatureCollection", "features": []}
data = resp.json()
# OGC API returns FeatureCollection in WGS84 directly
features = data.get("features", [])
if not features:
return {"type": "FeatureCollection", "features": []}
# Pass through - geodienste returns WGS84 GeoJSON
result = {
"type": "FeatureCollection",
"features": features,
}
logger.info(f"Returned {len(features)} parcels in WGS84")
return result
except ValueError as e:
logger.warning(f"Invalid bbox: {e}")
raise
except requests.RequestException as e:
logger.error(f"Parcel API request error: {e}")
return {"type": "FeatureCollection", "features": []}
except Exception as e:
logger.error(f"Error fetching parcels: {e}", exc_info=True)
return {"type": "FeatureCollection", "features": []}