Editing Public Transport Route System

Editing Public Transport Route System#

In this example we show how to add a new public transport route, beginning by adding new transit stops all the way to adding trips for an entire day.

Imports#

from pathlib import Path

from polaris.utils.testing.temp_network import TempNetwork
from polaris.utils.database.db_utils import read_and_close

Open the network and open the network database

project_dir = "/tmp/Grid"
supply_db = f"{project_dir}/Grid-Supply.sqlite"

# use Network instead of TempNetwork for real workflows
network = TempNetwork(Path(supply_db))
# network = Network.from_file(Path(supply_db))

network.upgrade()

transit = network.transit

We create a new route and add 3 new stops

route_id = transit.edit.add_route(agency_id=6, mode_id=3, route_name="my long distance route")

# We will add 3 new stops
pt1 = (4477925.19807411, 336098.12080047)
pt2 = (4480051.23290968, 333118.45076576)
pt3 = (4481433.69243029, 332522.51675882)

stop1 = transit.edit.add_stop(agency_id=6, route_type=3, stop_code="stp1", latitude=pt1[1], longitude=pt1[0])
stop2 = transit.edit.add_stop(agency_id=6, route_type=3, stop_code="stp2", latitude=pt2[1], longitude=pt2[0])

# For the last stop we can add more information with extra keys
# Any field existing on the database is accepted
stop3 = transit.edit.add_stop(
    agency_id=6,
    route_type=3,
    stop_code="stp3",
    latitude=pt3[1],
    longitude=pt3[0],
    parent_station="main_station_123",
    has_parking=1,
)

Our pattern will include the three stops we added, plus one pre-existing stop

stop_sequence = [stop1, stop2, stop3]
pattern_id = transit.edit.add_route_pattern(route_id=route_id, stop_sequence=stop_sequence)

We Compute the departure time for each trip in our period of interest

# Let's say we want to add trips for the first three hours of the day, each hour with a different headway
# We have a neat little API for coalescing the departure times for all of the intervals
intervals = [
    {"start": 3600, "end": 7200, "headway": 1200},
    {"start": 7200, "end": 10800, "headway": 1000},
]

trip_starts = transit.edit.suggest_departures(intervals)
print(trip_starts)
[4200.0, 5400.0, 6600.0, 7700.0, 8700.0, 9700.0, 10700.0]

We add a new trip for each trip departure time in our period of interest

added_trips = []
for instant in trip_starts:
    # This is the most vanilla alternative, where we add a new trip with a default speed
    # that will only be used if we don't find a speed value from the database (from route or gtfs mode, in that order)
    trip_id = transit.edit.add_pt_trip(pattern_id=pattern_id, departure_time=instant, default_speed=15)

    # We can also request that only trips that begin within a certain interval can be used for deriving the new
    # trip's speed
    # trip_id = transit.edit.add_pt_trip(pattern_id=pattern_id, departure_time=instant, default_speed=15,
    #                                            horizon_begin=28800, horizon_end=36000)

    # We can also enforce the use of the default speed we are providing
    # trip_id = transit.edit.add_pt_trip(pattern_id=pattern_id, departure_time=instant, default_speed=15,
    #                                            force_default_speed=True)

    # We can also tell polaris to apply a speedup factor to the speed found in the database, in case these trips are
    # expected to be faster (factor>1) or slower (factor<1) than what is found on average
    # This factor does NOT apply to the *default_speed*
    # trip_id = transit.edit.add_pt_trip(pattern_id=pattern_id, departure_time=instant, default_speed=15,
    #                                            speed_factor=1.15)

    added_trips.append(trip_id)
/builds/polaris/code/polarislib/polaris/network/transit/edit_supply.py:364: UserWarning: Caching data
  warnings.warn("Caching data")
# Let's say we want to ensure a certain frequency for a pattern within an interval
# In that case, we can use a dedicated API call that adds all trips necessary to keep that headway
# and deletes the trips that previously existed there

# First we clear the data cache so we can be sure to incorporate all recently added trips to our computations
transit.edit.clear_data_cache()

trip_starts = transit.edit.suggest_departures([{"start": 0, "end": 86400, "headway": 30000}])

added, deleted = transit.edit.ensure_pattern_departures(
    pattern_id=pattern_id, departure_times=trip_starts, default_speed=30
)

print("Added trips: ", added)
print("Deleted trips: ", deleted)
/builds/polaris/code/polarislib/polaris/network/transit/edit_supply.py:364: UserWarning: Caching data
  warnings.warn("Caching data")
Added trips:  [100010023, 100010024, 100010025]
Deleted trips:  [100010016, 100010017, 100010018, 100010019, 100010020, 100010021, 100010022]

Making sure all

We build the active networks

from polaris.network.traffic.road_connectors import RoadConnectors

active = network.active
active.build()

# And the ferry connectors (if they exist)
rc = RoadConnectors(supply_path=network.path_to_file)
# And we add connectors that are no longer than 50 meters
rc.connect_ferries(max_distance=50)

Visualizing the results#

from polaris.network.utils.srid import get_srid
from polaris.network.data.data_table_cache import DataTableCache
import folium
import geopandas as gpd
import shapely.wkb

with read_and_close(network.path_to_file, spatial=True) as conn:
    transit_stops = DataTableCache(network.path_to_file).get_table("Transit_Stops", conn).reset_index()
    transit_patterns = DataTableCache(network.path_to_file).get_table("Transit_Patterns", conn).reset_index()
    srid = get_srid(conn=conn)

ts = transit_stops[["stop_id", "geo"]]
ts = ts[ts.stop_id.isin(stop_sequence)]
tp = transit_patterns[["pattern_id", "geo"]]
tp = tp[tp.pattern_id.isin([pattern_id])]
ts.geo = ts.geo.apply(shapely.wkb.loads)
tp.geo = tp.geo.apply(shapely.wkb.loads)
ts = gpd.GeoDataFrame(ts, geometry="geo", crs=srid).to_crs(4326)
tp = gpd.GeoDataFrame(tp, geometry="geo", crs=srid).to_crs(4326)
map_osm = folium.Map(location=[ts.geo.y.mean(), ts.geo.x.mean()], zoom_start=14)

m1 = folium.Marker(icon=folium.Icon(color="red", prefix="fa", icon="bus"))
folium.GeoJson(ts.to_json(), name="Stops", show=True, marker=m1, tooltip="Stop").add_to(map_osm)

folium.GeoJson(tp.to_json(), name="Patterns", show=True, tooltip="Pattern").add_to(map_osm)

folium.LayerControl().add_to(map_osm)
map_osm
Make this Notebook Trusted to load map: File -> Trust Notebook


Total running time of the script: (0 minutes 2.319 seconds)

Gallery generated by Sphinx-Gallery