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.
This commit is contained in:
@@ -75,6 +75,18 @@ STATION_TAG_FILTERS: Mapping[str, Tuple[str, ...]] = {
|
||||
}
|
||||
|
||||
|
||||
# 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."""
|
||||
|
||||
@@ -89,5 +101,6 @@ __all__ = [
|
||||
"BoundingBox",
|
||||
"DEFAULT_REGIONS",
|
||||
"STATION_TAG_FILTERS",
|
||||
"TRACK_TAG_FILTERS",
|
||||
"compile_overpass_filters",
|
||||
]
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from typing import Generic, Sequence, TypeVar
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
def to_camel(string: str) -> str:
|
||||
@@ -53,6 +53,9 @@ class TrackModel(IdentifiedModel[str]):
|
||||
end_station_id: str
|
||||
length_meters: float
|
||||
max_speed_kph: float
|
||||
status: str | None = None
|
||||
is_bidirectional: bool = True
|
||||
coordinates: list[tuple[float, float]] = Field(default_factory=list)
|
||||
|
||||
|
||||
class TrainModel(IdentifiedModel[str]):
|
||||
|
||||
@@ -8,9 +8,10 @@ from geoalchemy2.elements import WKBElement, WKTElement
|
||||
from geoalchemy2.shape import to_shape
|
||||
|
||||
try: # pragma: no cover - optional dependency guard
|
||||
from shapely.geometry import Point # type: ignore
|
||||
from shapely.geometry import LineString, Point # type: ignore
|
||||
except ImportError: # pragma: no cover - allow running without shapely at import time
|
||||
Point = None # type: ignore[assignment]
|
||||
LineString = None # type: ignore[assignment]
|
||||
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -51,6 +52,12 @@ def _fallback_snapshot() -> dict[str, list[dict[str, object]]]:
|
||||
end_station_id="station-2",
|
||||
length_meters=289000.0,
|
||||
max_speed_kph=230.0,
|
||||
status="operational",
|
||||
is_bidirectional=True,
|
||||
coordinates=[
|
||||
(stations[0].latitude, stations[0].longitude),
|
||||
(stations[1].latitude, stations[1].longitude),
|
||||
],
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
)
|
||||
@@ -134,6 +141,20 @@ def get_network_snapshot(session: Session) -> dict[str, list[dict[str, object]]]
|
||||
|
||||
track_models: list[TrackModel] = []
|
||||
for track in tracks_entities:
|
||||
coordinates: list[tuple[float, float]] = []
|
||||
geometry = track.track_geometry
|
||||
shape = (
|
||||
to_shape(cast(WKBElement | WKTElement, geometry))
|
||||
if geometry is not None and LineString is not None
|
||||
else None
|
||||
)
|
||||
if LineString is not None and shape is not None and isinstance(shape, LineString):
|
||||
coords_list: list[tuple[float, float]] = []
|
||||
for coord in shape.coords:
|
||||
lon = float(coord[0])
|
||||
lat = float(coord[1])
|
||||
coords_list.append((lat, lon))
|
||||
coordinates = coords_list
|
||||
track_models.append(
|
||||
TrackModel(
|
||||
id=str(track.id),
|
||||
@@ -141,6 +162,9 @@ def get_network_snapshot(session: Session) -> dict[str, list[dict[str, object]]]
|
||||
end_station_id=str(track.end_station_id),
|
||||
length_meters=_to_float(track.length_meters),
|
||||
max_speed_kph=_to_float(track.max_speed_kph),
|
||||
status=track.status,
|
||||
is_bidirectional=track.is_bidirectional,
|
||||
coordinates=coordinates,
|
||||
created_at=cast(datetime, track.created_at),
|
||||
updated_at=cast(datetime, track.updated_at),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user