Files
rail-game/backend/app/core/osm_config.py
zwitschi c2927f2f60 feat: Enhance track model and import functionality
- Added new fields to TrackModel: status, is_bidirectional, and coordinates.
- Updated network service to handle new track attributes and geometry extraction.
- Introduced CLI scripts for importing and loading tracks from OpenStreetMap.
- Implemented normalization of track elements to ensure valid geometries.
- Enhanced tests for track model, network service, and import/load scripts.
- Updated frontend to accommodate new track attributes and improve route computation.
- Documented OSM ingestion process in architecture and runtime views.
2025-10-11 19:54:10 +02:00

107 lines
2.9 KiB
Python

from __future__ import annotations
"""Geographic presets and tagging rules for OpenStreetMap imports."""
from dataclasses import dataclass
from typing import Iterable, Mapping, Tuple
@dataclass(frozen=True)
class BoundingBox:
"""Geographic bounding box expressed as WGS84 coordinates."""
name: str
north: float
south: float
east: float
west: float
description: str | None = None
def __post_init__(self) -> None:
if self.north <= self.south:
msg = f"north ({self.north}) must be greater than south ({self.south})"
raise ValueError(msg)
if self.east <= self.west:
msg = f"east ({self.east}) must be greater than west ({self.west})"
raise ValueError(msg)
def contains(self, latitude: float, longitude: float) -> bool:
"""Return True when the given coordinate lies inside the bounding box."""
return (
self.south <= latitude <= self.north and self.west <= longitude <= self.east
)
def to_overpass_arg(self) -> str:
"""Return the bbox string used for Overpass API queries."""
return f"{self.south},{self.west},{self.north},{self.east}"
# Primary metropolitan areas we plan to support.
DEFAULT_REGIONS: Tuple[BoundingBox, ...] = (
BoundingBox(
name="berlin_metropolitan",
north=52.6755,
south=52.3381,
east=13.7611,
west=13.0884,
description="Berlin and surrounding rapid transit network",
),
BoundingBox(
name="hamburg_metropolitan",
north=53.7447,
south=53.3950,
east=10.3253,
west=9.7270,
description="Hamburg S-Bahn and harbor region",
),
BoundingBox(
name="munich_metropolitan",
north=48.2485,
south=47.9960,
east=11.7229,
west=11.3600,
description="Munich S-Bahn core and airport corridor",
),
)
# Tags that identify passenger stations and stops.
STATION_TAG_FILTERS: Mapping[str, Tuple[str, ...]] = {
"railway": ("station", "halt", "stop"),
"public_transport": ("station", "stop_position", "platform"),
"train": ("yes", "regional", "suburban"),
}
# Tags that describe rail infrastructure usable for train routing.
TRACK_TAG_FILTERS: Mapping[str, Tuple[str, ...]] = {
"railway": (
"rail",
"light_rail",
"subway",
"tram",
"narrow_gauge",
),
}
def compile_overpass_filters(filters: Mapping[str, Iterable[str]]) -> str:
"""Build an Overpass boolean expression that matches the provided filters."""
parts: list[str] = []
for key, values in filters.items():
options = "|".join(sorted(set(values)))
parts.append(f' ["{key}"~"^({options})$"]')
return "\n".join(parts)
__all__ = [
"BoundingBox",
"DEFAULT_REGIONS",
"STATION_TAG_FILTERS",
"TRACK_TAG_FILTERS",
"compile_overpass_filters",
]