Add web frontend and refactor core to use OsmError
- Refactor core.py: replace sys.exit() calls with OsmError exceptions
so the library is safe to use from Flask and other callers
- Add fetch_sibling_routes and fetch_route_master_routes to core.py
- Add Flask web frontend (web/app.py, templates, static assets):
- Map view with Leaflet; full route drawn in grey on load
- Sidebar stop list; active-slot UX for From/To selection
- Segment preview and download (full route or selected segment)
- Include-stops toggle applied client-side
- Bookmarkable URLs: GET /<relation_id>
- Clear selection button
- Other directions panel (sibling routes from same route_master)
- route_master handling: draws all member routes in colour on map
with links to each individual direction
- Add SVG favicon
- Add py.typed marker; add .gitignore
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d3e6d7ac42
commit
e0ade9e5ab
13 changed files with 1049 additions and 20 deletions
|
|
@ -6,12 +6,13 @@ import click
|
|||
|
||||
from osm_geojson.pt.core import (
|
||||
GeoJson,
|
||||
OsmError,
|
||||
build_route_coords,
|
||||
fetch_relation_full,
|
||||
make_geojson,
|
||||
nearest_coord_index,
|
||||
node_name,
|
||||
parse_elements,
|
||||
nearest_coord_index,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -35,8 +36,12 @@ def cli() -> None:
|
|||
@click.argument("relation_id", type=int)
|
||||
def list_stations(relation_id: int) -> None:
|
||||
"""List all stations in an OSM public transport route relation."""
|
||||
data = fetch_relation_full(relation_id)
|
||||
nodes, ways, stop_ids, way_ids, tags = parse_elements(data, relation_id)
|
||||
try:
|
||||
data = fetch_relation_full(relation_id)
|
||||
nodes, ways, stop_ids, way_ids, tags = parse_elements(data, relation_id)
|
||||
except OsmError as e:
|
||||
click.echo(f"Error: {e}", err=True)
|
||||
sys.exit(1)
|
||||
click.echo(f"Route: {tags.get('name', relation_id)}")
|
||||
click.echo(f"Stops ({len(stop_ids)}):")
|
||||
for i, sid in enumerate(stop_ids, 1):
|
||||
|
|
@ -60,8 +65,13 @@ def route_between(
|
|||
no_stops: bool,
|
||||
) -> None:
|
||||
"""Output GeoJSON for the route segment between two named stations."""
|
||||
data = fetch_relation_full(relation_id)
|
||||
nodes, ways, stop_ids, way_ids, tags = parse_elements(data, relation_id)
|
||||
try:
|
||||
data = fetch_relation_full(relation_id)
|
||||
nodes, ways, stop_ids, way_ids, tags = parse_elements(data, relation_id)
|
||||
except OsmError as e:
|
||||
click.echo(f"Error: {e}", err=True)
|
||||
sys.exit(1)
|
||||
|
||||
route_coords = build_route_coords(way_ids, ways, nodes)
|
||||
|
||||
def find_stop(name: str) -> int | None:
|
||||
|
|
@ -80,8 +90,8 @@ def route_between(
|
|||
if sid_to is None:
|
||||
errors.append(f"Station not found: {to_station!r}")
|
||||
if errors:
|
||||
for e in errors:
|
||||
click.echo(f"Error: {e}", err=True)
|
||||
for msg in errors:
|
||||
click.echo(f"Error: {msg}", err=True)
|
||||
click.echo("Available stations:", err=True)
|
||||
for sid in stop_ids:
|
||||
if sid in nodes:
|
||||
|
|
@ -104,8 +114,12 @@ def route_between(
|
|||
@click.option("--no-stops", is_flag=True, default=False, help="Omit stop points from output.")
|
||||
def full_route(relation_id: int, output: str | None, no_stops: bool) -> None:
|
||||
"""Output GeoJSON for the entire route, end to end."""
|
||||
data = fetch_relation_full(relation_id)
|
||||
nodes, ways, stop_ids, way_ids, tags = parse_elements(data, relation_id)
|
||||
try:
|
||||
data = fetch_relation_full(relation_id)
|
||||
nodes, ways, stop_ids, way_ids, tags = parse_elements(data, relation_id)
|
||||
except OsmError as e:
|
||||
click.echo(f"Error: {e}", err=True)
|
||||
sys.exit(1)
|
||||
route_coords = build_route_coords(way_ids, ways, nodes)
|
||||
geojson = make_geojson(route_coords, stop_ids, nodes, tags, no_stops=no_stops)
|
||||
output_geojson(geojson, output)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue