build_graph: Build a transit network graph

View source: R/build_graph.R

build_graphR Documentation

Build a transit network graph

Description

Builds a frequency-based transit network from the trips that depart within a time of day, and returns it as a set of tables in the General Modeling Network Specification (GMNS) format or as an {igraph} or {tidygraph} graph. The network can be used in transit assignment models and in graph analyses, such as finding the shortest travel times between stops.

Usage

build_graph(
  gtfs,
  from,
  to,
  format = "gmns",
  max_walk_dist = 300,
  walk_speed = 5
)

Arguments

gtfs

A GTFS object, as created by read_gtfs().

from

A string. The starting point of the time of day, in the "HH:MM:SS" format.

to

A string. The ending point of the time of day, in the "HH:MM:SS" format. Must be later than from.

format

A string. The format of the graph: "gmns" (the default), "igraph" or "tidygraph". The last two require the packages of the same name, which the function offers to install in interactive sessions.

max_walk_dist

A number. The maximum great-circle distance, in meters, between two stops connected by walking transfer links. Defaults to 300.

walk_speed

A number. The walking speed, in km/h, used to calculate the travel time of transfer links. Defaults to 5.

Value

With format = "gmns", a list with the data.tables node, link and config, named after the GMNS files. With format = "igraph", a directed igraph graph, and with format = "tidygraph", a directed tbl_graph. Their vertices and edges have the columns of the node and link tables as attributes, and the name of each vertex is its node_id, as a string.

Details

Nodes (node_type) are "stop" nodes, one per stop visited by the trips, at the stop's coordinates, and "service" nodes, one per route and direction at each of its stops, at the coordinates of the stop (their parent_node_id). Links (link_type) are all directed:

  • "in_vehicle" links connect the service nodes of consecutive stops of the trips. travel_time is the mean time from the departure at a stop to the arrival at the next, over the trips with both times (decreasing times are not used), weighted by their departures. departures is the number of departures of the trips of the network that run through the link. length is the great-circle distance between the stops, as in get_stop_distances(), and free_speed the length divided by the travel time (NA when the travel time is 0 or either is missing).

  • "boarding" links connect each stop to the service nodes from which trips depart. departures is the number of departures from the stop of the trips of the network, so it may include departures after to from stops far along their routes. travel_time is half the mean headway (the length of the time of day divided by departures).

  • "alighting" links connect each service node back to its stop, with a travel_time of 0.

  • "transfer" links connect, in both directions, the stops at most max_walk_dist apart. travel_time is the length divided by walk_speed, which is also their free_speed.

Lengths are in meters, travel times in minutes and speeds in km/h. Boarding and alighting links have a length of 0 and an NA free_speed; alighting and transfer links have NA departures. route_id, direction_id and inferred_direction are those of the link's service node, and NA in transfer links.

The network includes the trips that depart from their first stop at or after from and before to, as in get_route_frequency(), so trips already running at from are not included. Trips listed in frequencies are included once per departure generated within the time of day, with their stop_times entries as templates. Trips without stop_times entries are not included. The trips of every service_id are pooled, so the feed should usually be filtered to a single day first, with filter_by_date() or filter_by_weekday(). Departures after midnight have times past "24:00:00", so use a time of day such as "24:00:00"-"26:00:00" for them. Trips whose times wrongly go back to "00:00:00" after midnight are placed by their earliest time.

Within a trip, consecutive stop_times entries at the same stop are one visit, arriving at their first known time and departing at their last. A trip that visits a stop more than once uses a different service node at each visit (visit 1, 2, ...).

Directions come from the direction_id field of trips. Trips without it (or with a blank direction_id) get an inferred_direction instead: trips of a route that share a pair of consecutive stops, in the same order, are in the same direction, and so are trips that share pairs with them. The opposite directions of routes that share a pair of stops in the same order (e.g. through a one-way loop or street) are thus inferred as a single direction. Trips with a blank direction_id are never assigned the direction of other trips of their route.

In-vehicle links whose trips all have blank or decreasing times have an NA travel_time, with a warning; igraph::distances() doesn't accept it as a weight, and blank times can be filled beforehand with interpolate_stop_times(). Alighting links, in-vehicle links between stops with the same times and transfer links between stops at the same coordinates have a travel time of 0; igraph::closeness() and igraph::betweenness() require positive weights.

Departures are counted at every visit but the last of each trip, even with a blank departure time, unlike in get_stop_frequency(). The transfers and pathways tables, parent_stations and the pickup_type and drop_off_type fields are not used. Large values of max_walk_dist in dense feeds raise a warning when comparing the pairs of stops may use a lot of memory, or an error when there are too many pairs. Transfer links are not created across the antimeridian.

In the GMNS format, node_id, x_coord, y_coord, node_type, parent_node_id, link_id, from_node_id, to_node_id, directed, length and free_speed are GMNS fields; the other columns are additional fields, as allowed by GMNS. config lists the units, the coordinate reference system (EPSG:4326), the GMNS version (0.96) and the time of day. Stops not listed in stops or without coordinates have NA coordinates, with a warning (GMNS requires them). Free speeds may exceed the 200 km/h maximum checked by GMNS validators.

See Also

The "Building transit network graphs" vignette (vignette("graphs", package = "gtfstools")), which shows how to analyse and plot the network with {igraph}, {tidygraph}, {sfnetworks} and {ggraph}.

Examples


data_path <- system.file("extdata/spo_gtfs.zip", package = "gtfstools")
gtfs <- read_gtfs(data_path)

# the services of every day are pooled together, so only the services of a
# single weekday are kept
gtfs <- filter_by_weekday(gtfs, "wednesday")

graph <- build_graph(gtfs, from = "07:00:00", to = "08:00:00")
names(graph)
head(graph$node)
head(graph$link)

# the tables can be written to the csv files of a GMNS network
gmns_dir <- tempfile("gmns")
dir.create(gmns_dir)
for (table in names(graph)) {
  gmns_file <- file.path(gmns_dir, paste0(table, ".csv"))
  data.table::fwrite(graph[[table]], gmns_file)
}
list.files(gmns_dir)


# the shortest travel times, in minutes, from a stop to the other stops
graph <- build_graph(
  gtfs,
  from = "07:00:00",
  to = "08:00:00",
  format = "igraph"
)
stop_nodes <- igraph::V(graph)[igraph::V(graph)$node_type == "stop"]
travel_times <- igraph::distances(
  graph,
  v = stop_nodes[1],
  to = stop_nodes,
  mode = "out",
  weights = igraph::E(graph)$travel_time
)

# stops that can't be reached have an infinite travel time
is_reachable <- is.finite(travel_times)
sum(is_reachable)
summary(travel_times[is_reachable])


gtfstools documentation built on Oct. 28, 2026, 5:13 p.m.