Routing and accessibility analyses
often combine several GTFS feeds, such as those of the different transit
agencies of a metropolitan region. Routing engines such as
{r5r} and OpenTripPlanner work on a given departure date,
which must have service in every feed: otherwise, one of the networks
silently drops out of the analysis. Feeds are published at different
times and cover different periods, so such a date is not always easy to
find.
get_calendar_overlap() compares the calendars of several
feeds. Besides the feeds, it has only two arguments. Its
resolution argument controls what the result captures:
resolution = "daily" (the default) captures the
intensity of transit services: the number of trips each
feed runs on each of its service days, and whether all feeds have
service on that day.resolution = "periods" captures whether there is
any service: the periods of consecutive days on which
every feed runs at least one trip, however few.A day may thus belong to an overlap period even if one of the feeds
runs only a handful of trips on it, which the daily trips reveal. With
plot = TRUE, either result is plotted with
{ggplot2}.
Feeds can be given as paths to their .zip files. In this
case, the large shapes and stop_times tables
are not read. By default, the function returns one row per feed and
service day:
spo_path <- system.file("extdata/spo_gtfs.zip", package = "gtfstools")
poa_path <- system.file("extdata/poa_gtfs.zip", package = "gtfstools")
daily_trips <- get_calendar_overlap(c(spo_path, poa_path))
daily_trips |>
dplyr::filter( overlap == TRUE) |>
head()
#> feed date n_trips overlap
#> <char> <Date> <int> <lgcl>
#> 1: spo_gtfs 2019-01-18 7948 TRUE
#> 2: spo_gtfs 2019-01-19 7945 TRUE
#> 3: spo_gtfs 2019-01-20 7945 TRUE
#> 4: spo_gtfs 2019-01-21 7948 TRUE
#> 5: spo_gtfs 2019-01-22 7948 TRUE
#> 6: spo_gtfs 2019-01-23 7948 TRUEn_trips is the number of trips that run on the day, and
overlap tells whether all feeds have service on it. Trips
listed in frequencies count once per departure, so feeds
that describe their service with frequencies are comparable with feeds
that list every trip.
A feed has service on a day when at least one of its services runs on
it, according to the weekdays and dates in calendar and the
exceptions in calendar_dates. Services not used by any trip
are ignored. Feeds already read with read_gtfs() (and
possibly edited afterwards) can also be given as a list, whose names are
used as feed names,
e.g. get_calendar_overlap(list(spo = spo_gtfs, poa = poa_gtfs)).
With plot = TRUE, the density of each feed’s trips over
time is plotted in a panel of its own, weighting each day by its number
of trips. The days on which all feeds have service are shaded:
get_calendar_overlap(c(spo_path, poa_path), plot = TRUE) +
coord_cartesian(xlim = as.Date(c("2019-01-01", "2019-05-01")))The result is a regular ggplot object, so it can be
customised with {ggplot2} functions, e.g. to zoom in on a
period, as above.
The daily trips help pick a departure date on which every feed runs
its usual level of service, such as a regular weekday away from
holidays. For example, the trips each feed runs on the first Wednesday
on which both have service (format(x, "%u") numbers
weekdays from 1, Monday, regardless of the locale):
overlap_days <- unique(daily_trips$date[daily_trips$overlap])
departure_date <- overlap_days[format(overlap_days, "%u") == "3"][1]
daily_trips |>
filter(date == departure_date)
#> feed date n_trips overlap
#> <char> <Date> <int> <lgcl>
#> 1: spo_gtfs 2019-01-23 7948 TRUE
#> 2: poa_gtfs 2019-01-23 194 TRUEWhen only the dates matter, resolution = "periods"
summarises the days on which all feeds have service into periods of
consecutive days, with their first and last days (both included) and
their length:
get_calendar_overlap(c(spo_path, poa_path), resolution = "periods")
#> start_date end_date n_days
#> <Date> <Date> <int>
#> 1: 2019-01-18 2019-04-18 91The São Paulo and Porto Alegre feeds overlap for 91 days, from January 18 to April 18, 2019. When the feeds don’t overlap, the result has no rows:
ber_path <- system.file("extdata/ber_gtfs.zip", package = "gtfstools")
feeds <- c(spo_path, poa_path, ber_path)
get_calendar_overlap(feeds, resolution = "periods")
#> Empty data.table (0 rows and 3 cols): start_date,end_date,n_daysWith plot = TRUE, a timeline shows why. Each feed is a
bar spanning its service days, and the periods in which all feeds have
service are shaded in the background. The São Paulo feed spans more than
12 years, so the plot is zoomed in:
get_calendar_overlap(feeds, resolution = "periods", plot = TRUE) +
coord_cartesian(xlim = as.Date(c("2018-12-01", "2021-07-01")))The Berlin feed starts in November 2020, after the São Paulo feed ends, so no date has service in all three feeds.
Once a date is chosen, the feeds can be combined into a single one
with merge_gtfs(), or reduced to the services of that date
with the filtering functions (see the filtering
vignette).