height correction, new defalt

This commit is contained in:
Olaf committed 2026-10-21 09:24:12 +02:00
1 parent 0891cf12a5
commit b692f94122
12 files changed
+792 -205

No files matched your search

+108
View File
@@ -0,0 +1,108 @@
"""PDOK AHN DTM coverage in RD New, elevations in metres relative to NAP."""
import hashlib
import logging
import math
import os
import tempfile
from pathlib import Path
from urllib.parse import urlencode
import rasterio
from pyproj import Transformer
from rasterio.errors import RasterioError
from flight_map.geo import Bounds
from flight_map.network import request_bytes
LOGGER = logging.getLogger(__name__)
WCS_URL = "https://service.pdok.nl/rws/ahn/wcs/v1_0"
COVERAGE = "dtm_05m"
AHN_EXTENT = Bounds(3.20030320333955, 50.728765211722, 7.273444085294, 53.555958411015)
SIZE = 512
def coverage_url(bounds: Bounds) -> str:
transform = Transformer.from_crs("EPSG:4326", "EPSG:28992", always_xy=True)
west, south, east, north = transform.transform_bounds(*bounds.values, densify_pts=21)
bbox = ",".join(
str(v) for v in (math.floor(west), math.floor(south), math.ceil(east), math.ceil(north))
)
return (
WCS_URL
+ "?"
+ urlencode(
{
"service": "WCS",
"version": "1.0.0",
"request": "GetCoverage",
"coverage": COVERAGE,
"crs": "EPSG:28992",
"response_crs": "EPSG:28992",
"bbox": bbox,
"width": SIZE,
"height": SIZE,
"format": "GEOTIFF",
"interpolation": "nearest neighbor",
}
)
)
def validate_raster(path: Path) -> None:
with rasterio.open(path) as dataset:
if dataset.driver != "GTiff" or dataset.crs != rasterio.crs.CRS.from_epsg(28992):
raise ValueError("AHN response is not an RD New GeoTIFF.")
if dataset.count != 1 or dataset.width != SIZE or dataset.height != SIZE:
raise ValueError("AHN response has unexpected raster dimensions.")
dataset.read(1, masked=True)
def terrain_data(bounds: Bounds, cache: Path, *, refresh: bool = False) -> Path | None:
if not (
AHN_EXTENT.contains(bounds.west, bounds.south)
and AHN_EXTENT.contains(bounds.east, bounds.north)
):
LOGGER.warning("AHN height map unavailable: map bounds extend outside Dutch AHN coverage.")
return None
url = coverage_url(bounds)
key = hashlib.sha256(url.encode()).hexdigest()
path = cache / f"ahn-{key}.tif"
temporary: Path | None = None
try:
if path.exists() and not refresh:
validate_raster(path)
LOGGER.info("Reading cached AHN DTM (NAP): %s", path)
return path
LOGGER.info("Downloading AHN DTM height map from PDOK for %s", bounds.values)
content = request_bytes(url)
cache.mkdir(parents=True, exist_ok=True)
with tempfile.NamedTemporaryFile(dir=cache, suffix=".tif", delete=False) as stream:
temporary = Path(stream.name)
stream.write(content)
stream.flush()
os.fsync(stream.fileno())
validate_raster(temporary)
with rasterio.open(temporary, "r+") as dataset:
dataset.update_tags(
source=WCS_URL,
coverage=COVERAGE,
vertical_reference="NAP",
units="metres",
license="CC0",
request_url=url,
)
os.replace(temporary, path)
LOGGER.info("Cached AHN DTM (NAP): %s", path)
return path
except (RuntimeError, OSError, ValueError, RasterioError) as exc:
LOGGER.warning(
"AHN height map unavailable: %s. Land elevation will be N/A; "
"reported flight altitudes are unaffected. "
"Use --refresh-terrain to retry a damaged cache entry.",
exc,
)
return None
finally:
if temporary and temporary.exists():
temporary.unlink()