Converts a National Rail fares feed (see atoc_fares_read()) to GTFS
fare tables and attaches them to a GTFS object produced by atoc2gtfs().
Both the original GTFS fares specification (fares_version = 1) and
GTFS Fares v2 (fares_version = 2) are supported.
Usage
gtfs_add_railfares(
gtfs,
fares,
fares_version = 1,
ticket_codes = NULL,
ticket_class = "standard",
ticket_type = NULL,
walkup_only = TRUE,
rider_categories = c("adult", "child"),
railcards = NULL,
ndf = TRUE,
travel_date = NULL,
travel_time = NULL,
booking_date = NULL,
silent = TRUE
)Arguments
- gtfs
a GTFS object (named list of data frames) from
atoc2gtfs().- fares
a fares list from
atoc_fares_read(), or a path to a fares zip/folder which will be read for you.- fares_version
numeric,
1for the original GTFS fares tables (fare_attributes/fare_rules) or2for GTFS Fares v2. Default1.- ticket_codes
optional character vector of ticket codes (e.g.
c("SDS","SDR")) to convert, overridingticket_class/ticket_type. See theticket_typetable of the fares list for available codes.- ticket_class
character,
"standard"and/or"first". Default"standard".- ticket_type
character, any of
"single","return","season". Default"single"forfares_version = 1andc("single","return")forfares_version = 2. Note GTFS has no native concept of a return or season ticket: returns are emitted as a product priced for the round trip, seasons (weekly price) are excluded by default.- walkup_only
logical, keep only walk-up (turn-up-and-go) tickets: the Anytime, Off-Peak and Super Off-Peak families (default TRUE). When FALSE every ticket type passing the class/type filters is converted, which includes trade, carnet and advance-purchase tier tickets whose headline prices are misleading (some are as low as GBP 0.05). Ignored when
ticket_codesis given.- rider_categories
character, any of
"adult","child". Which passenger types to emit (GTFS Fares v2 only). Defaultc("adult","child").- railcards
optional character vector of railcard codes (e.g.
"YNG"= 16-25,"SRN"= Senior,"DIS"= Disabled; see therailcardtable of the fares list). Each becomes an additional GTFS Fares v2 rider category with discounted prices. DefaultNULL.- ndf
logical, include the non-derivable fare overrides file (default TRUE).
- travel_date
optional Date: convert fares for a journey on this date. The date/time restriction data (RST file) is evaluated and fares not valid on that date are dropped (e.g. tickets barred on certain dates). If
faresis a path, the feed is also read as of this date.- travel_time
optional departure time on
travel_date, as"HH:MM"(or minutes after midnight). Fares whose time restriction makes them invalid at this departure time - e.g. Off-Peak tickets during the morning peak - are dropped. Requirestravel_date.- booking_date
optional Date: the day the ticket is bought. When given (with
travel_date), Advance tickets whose booking horizon (TAP file) is satisfied are included in addition to the walk-up tickets, at their tier prices. Booking is assumed to happen by the end of this day. See Details for the quota caveat.- silent
logical, suppress progress messages (default TRUE).
Details
How rail fares map to GTFS
Rail fares are set per flow: an origin/destination pair which may be a single station, a station cluster or a group station (e.g. "LONDON TERMINALS"), per fare route and per ticket type.
With fares_version = 1 every station is a fare zone (zone_id =
CRS code) and each flow is expanded to all its member station pairs in
fare_rules. Because GTFS v1 has no concept of passenger types or ticket
choice, the cheapest fare among the selected ticket types is emitted
for each station pair, and rider_categories/railcards are ignored.
Choose e.g. ticket_type = "single" to control what "the fare" means.
With fares_version = 2 each flow endpoint becomes an area in
areas/stop_areas (clusters and group stations map directly, without
expansion), each ticket type/price becomes a fare_products row, and
each flow becomes rows in fare_leg_rules. Passenger types are
distinguished via rider_categories: adult fares always, child fares
and railcard-discounted fares on request. Where several fare routes link
the same pair of areas, multiple products apply and a journey planner may
choose among them.
Prices
Adult prices come directly from the flow file (plus the non-derivable
overrides file, which contains fares that cannot be derived, e.g. most
London fares - disable with ndf = FALSE). Child and railcard prices are
calculated from the status discount file: percentage discounts with
flat-fare/minimum-fare caps, as specified in RSPS5045. The industry
rounding rules file (FRR) is not applied, so calculated discount prices
can differ from retail prices by a few pence.
Restrictions and scenario conversion
GTFS has no general way to say when a ticket is valid, so by default time restrictions are ignored: an Off-Peak product appears alongside the Anytime product with no indication that it cannot be used at 08:00, and Advance tickets are excluded entirely (their tier prices carry no availability information).
The travel_date, travel_time and booking_date arguments offer an
alternative: a scenario snapshot. Give a date (and optionally a
departure time and a booking date) and the restriction data is evaluated
at conversion time, so the output contains exactly the fares available
for that scenario - e.g. "travelling at 08:00 on Monday 3 August, booked
on 1 July" drops the Off-Peak products (invalid before 09:30 on most
flows) and includes the Advance tiers bookable a month ahead. The output
is still plain GTFS; the selection has simply been made for you.
Caveats: Advance prices are the tier prices from the flow file - which tier is actually on sale for a particular train is quota-controlled by the reservation system and is not in any public feed, so treat Advance fares as "best case". Restriction evaluation covers date bands and departure-time bands (network-wide or origin-specific); arrival-based, via-based and train-specific restrictions, easements and minimum-fare windows are treated as "fare remains valid", so the filter errs towards keeping a fare.
Scenarios are validated against the coverage of the data: a
travel_date outside the GTFS calendar is an error (the timetable
contains no trips then), a date outside the feed's fares rounds warns
(see atoc_fares_read()), booking more than a year before travel is an
error and more than ~12 weeks (when bookings usually open) warns, and a
fares list read for a different date than travel_date warns.
See also
Other rail fares:
atoc_fares_read()
Examples
if (FALSE) { # \dontrun{
gtfs <- atoc2gtfs("ttis123.zip", ncores = 4)
fares <- atoc_fares_read("RJFAF756.zip")
# GTFS v1: cheapest standard single per station pair
gtfs_v1 <- gtfs_add_railfares(gtfs, fares, fares_version = 1)
# GTFS v2: adult/child/16-25 railcard singles and returns
gtfs_v2 <- gtfs_add_railfares(gtfs, fares, fares_version = 2,
railcards = "YNG")
# Scenario: departing 08:00 on 3 August, booked 1 July - peak-time
# walk-up fares plus the Advance tiers bookable a month ahead
gtfs_s <- gtfs_add_railfares(gtfs, fares, fares_version = 2,
travel_date = as.Date("2026-08-03"),
travel_time = "08:00",
booking_date = as.Date("2026-07-01"))
} # }
