Note
Go to the end to download the full example code.
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_model import TempModel
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 sphinx_gallery_thumbnail_path = ‘../../examples/editing_models/bus_map.png’
project_dir = TempModel("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)
/home/gitlab-runner/builds/polaris/code/polarislib/polaris/network/transit/edit_supply.py:459: 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)
/home/gitlab-runner/builds/polaris/code/polarislib/polaris/network/transit/edit_supply.py:459: UserWarning: Caching data
warnings.warn("Caching data")
Added trips: [100010023, 100010024, 100010025]
Deleted trips: [100010016, 100010017, 100010018, 100010019, 100010020, 100010021, 100010022]
We can also flip the route we just added to create the “return” direction In this case we could simply revert the order of the stops we already had, but this would be helpful in duplicating patterns created manually or on pre-existing models
return_pattern_id = transit.edit.flip_pattern(pattern_id, keep_original_pattern=True)
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.utils.database.data_table_access import DataTableAccess
import folium
import geopandas as gpd
import shapely.wkb
with read_and_close(network.path_to_file, spatial=True) as conn:
transit_stops = DataTableAccess(network.path_to_file).get("Transit_Stops", conn).to_crs(4326)
transit_patterns = DataTableAccess(network.path_to_file).get("Transit_Patterns", conn).to_crs(4326)
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])]
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
Total running time of the script: (0 minutes 16.547 seconds)