Initial Vibe coded project

This commit is contained in:
Olaf committed 2026-10-20 14:18:35 +02:00
commit 0891cf12a5
21 files changed
+2830

No files matched your search

+127
View File
@@ -0,0 +1,127 @@
import hashlib
import json
import logging
import math
import os
import tempfile
from pathlib import Path
from typing import Any
from urllib.parse import urlencode
from shapely.geometry import LineString
from flight_map.geo import Bounds, Projection
from flight_map.network import request_json
OVERPASS_URL = "https://overpass-api.de/api/interpreter"
LOGGER = logging.getLogger(__name__)
def validate_map_data(data: Any) -> dict[str, Any]:
if not isinstance(data, dict) or not isinstance(data.get("elements"), list):
raise ValueError("Map data must be an Overpass JSON object containing elements.")
if data.get("remark"):
raise ValueError(f"Overpass could not complete the map query: {data['remark']}")
return data
def map_data(
bounds: Bounds, cache: Path, local_file: Path | None = None, *, refresh: bool = False
) -> dict[str, Any]:
if local_file and refresh:
raise ValueError("--refresh-map cannot be combined with --map-data.")
bbox = f"{bounds.south},{bounds.west},{bounds.north},{bounds.east}"
query = (
"[out:json][timeout:60];("
f'way["highway"]({bbox});way["waterway"]({bbox});'
f'way["building"]({bbox});way["natural"="water"]({bbox});'
");out geom;"
)
key = hashlib.sha256((OVERPASS_URL + "\n" + query).encode()).hexdigest()
path = local_file or cache / f"osm-{key}.json"
if path.exists() and (local_file or not refresh):
LOGGER.info("Reading %s map data: %s", "local" if local_file else "cached", path)
with path.open() as stream:
try:
data = json.load(stream)
except (ValueError, UnicodeError) as exc:
raise ValueError(
f"Invalid map JSON in {path}; correct the file or use --refresh-map "
"to replace a damaged cache entry."
) from exc
elif local_file:
raise ValueError(f"Local map data file does not exist: {local_file}")
else:
LOGGER.info("Downloading vector map data from Overpass for %s", bounds.values)
data = request_json(
OVERPASS_URL,
data=urlencode({"data": query}).encode(),
headers={"Content-Type": "application/x-www-form-urlencoded"},
)
validate_map_data(data)
cache.mkdir(parents=True, exist_ok=True)
temporary: Path | None = None
try:
with tempfile.NamedTemporaryFile(
mode="w", encoding="utf-8", dir=cache, suffix=".tmp", delete=False
) as stream:
temporary = Path(stream.name)
json.dump(data, stream)
stream.flush()
os.fsync(stream.fileno())
os.replace(temporary, path)
finally:
if temporary and temporary.exists():
temporary.unlink()
LOGGER.info("Cached vector map data: %s", path)
return validate_map_data(data)
def map_lines(
data: dict[str, Any], projection: Projection, *, min_building_size: float = 0
) -> list[LineString]:
result = []
for element in data["elements"]:
if not isinstance(element, dict):
raise ValueError("Map data contains a malformed feature.")
if element.get("type") != "way":
continue
tags = element.get("tags", {})
if not isinstance(tags, dict):
raise ValueError("Map feature tags must be an object.")
if tags.get("highway") in {
"footway",
"path",
"steps",
"cycleway",
"bridleway",
"corridor",
"construction",
"proposed",
}:
continue
geometry = element.get("geometry")
if not isinstance(geometry, list) or len(geometry) < 2:
raise ValueError("Map way has no usable geometry; export using 'out geom'.")
try:
positions = [(float(p["lon"]), float(p["lat"])) for p in geometry]
except (KeyError, ValueError, TypeError) as exc:
raise ValueError("Map way contains invalid geographic coordinates.") from exc
if any(
not math.isfinite(lon)
or not math.isfinite(lat)
or not -180 <= lon <= 180
or not -90 <= lat <= 90
for lon, lat in positions
):
raise ValueError("Map way coordinates must be finite and within geographic limits.")
points = [projection.point(lon, lat) for lon, lat in positions]
line = LineString(points)
if "building" in tags:
west, south, east, north = line.bounds
if min(east - west, north - south) < min_building_size:
continue
result.append(line)
if not result:
raise ValueError("No drawable map features were found for this area.")
return result