Introduction
GTFS feeds do not have a defined scope regarding its coverage of the transportation system. Some can be bounded to one agency, whereas others can aggregate several modes in the same city, or even national wise.
From the simpler to the most complex feeds, some analysis require to narrow the perspective. GTFShift provides some to help in this process.
Filters
Filter by agency
This example uses a national GTFS feed for long distance rail in Germany, retrieved from https://gtfs.de/en/feeds/de_fv/.
# Load GTFS
gtfs = load_feed("https://download.gtfs.de/germany/fv_free/latest.zip")
summary(gtfs)
#> tidygtfs object
#> files agency, routes, stop_times, trips, attributions, shapes, transfers, ., calendar, calendar_dates, feed_info, stops
#> agencies MAV, Ceske Drahy, PKP Intercity ... 10 more
#> service from 2026-01-03 to 2026-02-02
#> uses stop_times (no frequencies)
#> # routes 96
#> # trips 4221
#> # stop_ids 1266
#> # stop_names 767
#> # shapes 1472Multimodal feeds aggregate several agencies.
GTFShift::filter_by_agency(), allows to filter by the
agency id, name, or both. You can filter by the id and/or
name of the agency.
# Filter by agency id
gtfs_5 = GTFShift::filter_by_agency(gtfs, id = 5)
summary(gtfs_5)
#> tidygtfs object
#> files agency, routes, stop_times, trips, attributions, shapes, transfers, ., calendar, calendar_dates, feed_info, stops
#> agencies MAV, Ceske Drahy, PKP Intercity ... 10 more
#> service from 2026-01-03 to 2026-02-02
#> uses stop_times (no frequencies)
#> # routes 1
#> # trips 1
#> # stop_ids 12
#> # stop_names 12
#> # shapes 1
# Filter by agency name
gtfs_sncf = GTFShift::filter_by_agency(gtfs, name = "SNCF")
summary(gtfs_sncf)
#> tidygtfs object
#> files agency, routes, stop_times, trips, attributions, shapes, transfers, ., calendar, calendar_dates, feed_info, stops
#> agencies MAV, Ceske Drahy, PKP Intercity ... 10 more
#> service from 2026-01-03 to 2026-02-02
#> uses stop_times (no frequencies)
#> # routes 2
#> # trips 34
#> # stop_ids 6
#> # stop_names 5
#> # shapes 6Original GTFS
shape_agency = gtfs$trips |>
left_join(gtfs$routes, by = "route_id") |>
left_join(gtfs$agency, by = "agency_id") |>
select(shape_id, agency_id, agency_name) |>
distinct() |>
mutate(name = paste(agency_id, agency_name, sep = ": "))
shapes_sf = tidytransit::shapes_as_sf(gtfs$shapes) |>
left_join(shape_agency, by = "shape_id")
mapview::mapview(shapes_sf, zcol = "name", legend = TRUE, layer.name="Agency")GTFS filtered for agency id = 5
shape_agency = gtfs_5$trips |>
left_join(gtfs_5$routes, by = "route_id") |>
left_join(gtfs_5$agency, by = "agency_id") |>
select(shape_id, agency_id, agency_name) |>
distinct() |>
mutate(name = paste(agency_id, agency_name, sep = ": "))
shapes_sf = tidytransit::shapes_as_sf(gtfs_5$shapes) |>
left_join(shape_agency, by = "shape_id")
mapview::mapview(shapes_sf, zcol = "name", legend = TRUE, layer.name = "Agency")GTFS filtered for agency name = SNCF
shape_agency = gtfs_sncf$trips |>
left_join(gtfs_sncf$routes, by = "route_id") |>
left_join(gtfs_sncf$agency, by = "agency_id") |>
select(shape_id, agency_id, agency_name) |>
distinct()|>
mutate(name = paste(agency_id, agency_name, sep = ": "))
shapes_sf = tidytransit::shapes_as_sf(gtfs_sncf$shapes) |>
left_join(shape_agency, by = "shape_id")
mapview::mapview(shapes_sf, zcol = "name", legend = TRUE, layer.name="Agency")Filter by mode
This example uses a metropolitan wide GTFS feed for Los Angeles rail services, retrieved from https://developer.metro.net/gtfs-schedule-data/.
# Load GTFS
gtfs = GTFShift::load_feed("https://gitlab.com/LACMTA/gtfs_rail/raw/master/gtfs_rail.zip", create_transfers=FALSE)
summary(gtfs)
#> tidygtfs object
#> files agency, routes, stop_times, trips, fare_attributes, fare_rules, shapes, calendar, calendar_dates, feed_info, stops
#> agency Metro - Los Angeles
#> service from 2026-01-06 to 2026-01-20
#> uses stop_times (no frequencies)
#> # routes 6
#> # trips 5177
#> # stop_ids 448
#> # stop_names 345
#> # shapes 16Multimodal feeds aggregate several modes.
GTFShift::filter_by_mode() allows to restrict the feed to
some modes only. Refer to routes.txt ‘route_type’ parameter on GTFS
documentation for more details on the modes id that should be used
as parameters.
# Filter by mode tram
gtfs_tram = GTFShift::filter_by_modes(gtfs, modes = list(0))
summary(gtfs_tram)
#> tidygtfs object
#> files agency, routes, stop_times, trips, fare_attributes, fare_rules, shapes, calendar, calendar_dates, feed_info, stops
#> agency Metro - Los Angeles
#> service from 2026-01-06 to 2026-01-20
#> uses stop_times (no frequencies)
#> # routes 4
#> # trips 3465
#> # stop_ids 95
#> # stop_names 95
#> # shapes 8Original GTFS
shape_route = gtfs$trips |>
left_join(gtfs$routes, by = "route_id") |>
select(shape_id, route_id, route_type) |>
distinct() |>
mutate(route_type = as.character(route_type))
shapes_sf = tidytransit::shapes_as_sf(gtfs$shapes) |>
left_join(shape_route, by = "shape_id") |>
filter(!is.na(route_type))
mapview::mapview(shapes_sf, zcol = "route_type", legend = TRUE, layer.name="Route type")GTFS filtered for mode tram (0)
shape_route = gtfs_tram$trips |>
left_join(gtfs_tram$routes, by = "route_id") |>
select(shape_id, route_id, route_type) |>
distinct() |>
mutate(route_type = as.character(route_type))
shapes_sf = tidytransit::shapes_as_sf(gtfs_tram$shapes) |>
left_join(shape_route, by = "shape_id")
mapview::mapview(shapes_sf, zcol = "route_type", legend = TRUE, layer.name="Route type")Filter by route_name
This article uses a GTFS feed from the library GTFS database for Portugal as an example. Refer to the vignette(“download”) for more details.
# Get GTFS from library GTFS database for Portugal
data = read.csv(system.file("extdata", "gtfs_sources_pt.csv", package = "GTFShift"))
gtfs = GTFShift::load_feed(data[data$ID=="faro",]$URL, create_transfers=FALSE)
summary(gtfs)
#> tidygtfs object
#> files agency, routes, stop_times, trips, fare_attributes, fare_rules, shapes, calendar, calendar_dates, feed_info, stops
#> agency Próximo - Transportes Urbanos de Faro
#> service from 2024-01-01 to 2026-12-31
#> uses stop_times (no frequencies)
#> # routes 14
#> # trips 746
#> # stop_ids 187
#> # stop_names 126
#> # shapes 30GTFS feeds aggregate several routes.
GTFShift::filter_by_route_name(), allows to filter the feed
for specific routes, given a partial or total match with the short or
the long name.
# Filter by short_name with exact match
gtfs_1 = GTFShift::filter_by_route_name(
gtfs,
values = list("1"),
short_name = TRUE,
exact_match = TRUE
)
summary(gtfs_1)
#> tidygtfs object
#> files agency, routes, stop_times, trips, fare_attributes, fare_rules, shapes, calendar, calendar_dates, feed_info, stops
#> agency Próximo - Transportes Urbanos de Faro
#> service from 2024-01-02 to 2026-12-31
#> uses stop_times (no frequencies)
#> # routes 1
#> # trips 82
#> # stop_ids 23
#> # stop_names 19
#> # shapes 1
# Filter by long_name with partial match
gtfs_terminal = GTFShift::filter_by_route_name(
gtfs,
values = list("Terminal", "Rodoviário"),
short_name = FALSE,
exact_match = FALSE
)
summary(gtfs_terminal)
#> tidygtfs object
#> files agency, routes, stop_times, trips, fare_attributes, fare_rules, shapes, calendar, calendar_dates, feed_info, stops
#> agency Próximo - Transportes Urbanos de Faro
#> service from 2024-01-01 to 2026-12-31
#> uses stop_times (no frequencies)
#> # routes 6
#> # trips 406
#> # stop_ids 103
#> # stop_names 72
#> # shapes 16Original GTFS
route_name = gtfs$trips |>
left_join(gtfs$routes, by = "route_id") |>
select(shape_id, route_id, route_short_name, route_long_name) |>
distinct() |>
mutate(name = paste(route_short_name, route_long_name, sep = ": "))
shapes_sf = tidytransit::shapes_as_sf(gtfs$shapes) |>
left_join(route_name, by = "shape_id")
mapview::mapview(shapes_sf, zcol = "name", legend = TRUE, layer.name="Route name")GTFS filtered for route short name 1 (exact match)
route_name = gtfs_1$trips |>
left_join(gtfs_1$routes, by = "route_id") |>
select(shape_id, route_id, route_short_name, route_long_name) |>
distinct() |>
mutate(name = paste(route_short_name, route_long_name, sep = ": "))
shapes_sf = tidytransit::shapes_as_sf(gtfs_1$shapes) |>
left_join(route_name, by = "shape_id")
mapview::mapview(shapes_sf, zcol = "name", legend = TRUE, layer.name="Route name")GTFS filtered for route long name Terminal,
Rodoviário (partial match)
route_name = gtfs_terminal$trips |>
left_join(gtfs_terminal$routes, by = "route_id") |>
select(shape_id, route_id, route_short_name, route_long_name) |>
distinct() |>
mutate(name = paste(route_short_name, route_long_name, sep = ": "))
shapes_sf = tidytransit::shapes_as_sf(gtfs_terminal$shapes) |>
left_join(route_name, by = "shape_id")
mapview::mapview(shapes_sf, zcol = "name", legend = TRUE, layer.name="Route name")