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_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
Total running time of the script: (0 minutes 4.626 seconds)