Skip to contents

Introduction

GTFS-RT extends the GTFS static data model by providing real time operational information. From service alerts, to trip updates, but also vehicle positions. The collection of this data can provide valuable insights about how planned operation performs in practice. GTFShift provides several methods to enable this data collection and analysis.

Collect GTFS-RT data

To collect GTFS-RT data, use GTFShift::rt_collect_json() or GTFShift::rt_collect_protobuf(), depending on the encoding the feed uses (JSON or Protocol Buffers, respectively). These fetch GTFS-RT data from the endpoint and save it to a CSV file for further analysis. The method runs in a loop, fetching data at regular intervals (default is every 60 seconds) until manually stopped (CTRL+C).

# Get GTFS from library GTFS database for Portugal
data = read.csv(system.file("extdata", "gtfs_sources_pt.csv", package = "GTFShift"))
gtfs_id = "lisboa"

rt_collect_file <- "gtfs_rt_data.csv"
GTFShift::rt_collect_protobuf(data$URL.RT[data$ID == gtfs_id], rt_collect_file) # Run until manually stopped (CTRL+C)

Extend prioritization with GTFS-RT data

Once GTFS-RT data is collected, it can be used to extend lane prioritization analysis. GTFShift::rt_extend_prioritization() takes a lane prioritization data frame and a GTFS-RT collection (as an sf object) and enriches the prioritization with real-time metrics. Refer to the method documentation for the full details.

# Prioritization based on static GTFS data and infrastructure characteristics
gtfs = GTFShift::load_feed(data$URL[data$ID == gtfs_id], create_transfers=FALSE)
osm_q = opq(bbox=sf::st_bbox(tidytransit::shapes_as_sf(gtfs$shapes)))  |>
  add_osm_feature(key = "route", value = c("bus", "tram")) |>
  add_osm_feature(key = "network", value = "Carris", key_exact = TRUE)

lane_prioritization <- GTFShift::prioritize_lanes(gtfs, osm_query)

# GTFS-RT data preparation
rt_collection = read.csv("rt_collect_file.csv") |> 
  sf::st_as_sf(coords = c("vehicle.position.longitude", "vehicle.position.latitude"), crs = 4326)

# (Optional) Filter updates, to remove those close to bus stops
gtfs_stops = tidytransit::stops_as_sf(gtfs$stops, crs=4326)
within_distance = st_is_within_distance(
  rt_collection |> st_transform(crs=3857), 
  gtfs_stops |> st_transform(crs=3857), 
  dist = stop_buffer_size
)
rt_collection_filtered = rt_collection[lengths(within_distance) == 0, ]

# Prioritization extended with real-time data to add traffic conditions
lane_prioritization_extended <- GTFShift::rt_extend_prioritization(
  lane_prioritization = lane_prioritization,
  rt_collection = rt_collection_filtered
)

The resulting lane_prioritization_extended data frame includes additional columns with speed metrics, such as average speed, median speed, and speed percentiles, providing a more comprehensive view of lane performance based on real-time data.

lane_prioritization_0800 = lane_prioritization_extended |> filter(hour==8)

mapview::mapview(
  lane_prioritization_0800,
  zcol = "speed_avg",
  layer.name = "Average speed per lane"
)

p50_frequency = quantile(lane_prioritization_0800$frequency, 0.5, na.rm=TRUE)
p50_speed = quantile(lane_prioritization_0800$speed_avg, 0.5, na.rm=TRUE)
mapview::mapview(
  lane_prioritization_0800 |> filter(is_bus_lane & (frequency<p50_frequency | (is.na(n_lanes) | n_lanes_direction<=1) | speed_avg<=p50_speed)),
  layer.name=sprintf("Bus lane with -%d bus/h OR -2 lane/dir OR %.2f km/h or - avg. speed", p50_frequency, p50_speed),
  color="#DAD887",
  homebutton=FALSE,
  lwd=3

) + mapview::mapview(
  lane_prioritization_0800 |> filter(is_bus_lane & frequency>=p50_frequency & !is.na(n_lanes) & n_lanes_direction>1 & speed_avg>p50_speed),
  layer.name=sprintf("Bus lane with +%d bus/h AND +1 lane/dir AND +%.2f km/h avg.speed", p50_frequency-1, p50_speed),
  color="#3BC1A8",
  homebutton=FALSE,
  lwd=3
) + mapview::mapview(
  lane_prioritization_0800 |> filter(!is_bus_lane & frequency>=p50_frequency & !is.na(n_lanes) & n_lanes_direction>1 & speed_avg<=p50_speed),
  layer.name=sprintf("NO bus lane with +%d bus/h AND +1 lane/dir AND %.2f km/h or - avg.speed", p50_frequency-1, p50_speed),
  color="#F63049",
  homebutton=FALSE,
  lwd=3
)