A useful access analysis tool should allow analyzing hypothetical transit networks, not just existing ones. As such, these tools can modify transit routes and their schedules.
Consider Conveyal’s scenario editor, as shown in this demo,

Clipped from Conveyal’s product demo.
or this clip demonstrating Remix’s route editing functionality.

Clipped from this video.
While these tools accept modifications with a point-and-click interface1, I take a different approach. My previous access analysis tools accepted a modifications file in a text format that is both machine- and human-readable2. In March, I added basic network modification functionality to arf, using an updated version of this approach.
In arf, the unit of route modification is a pair of a route and type. For many transit routes, there are just two types: inbound and outbound. Some may have more, like trips that are truncated or run an express variant, for instance3. For a transit planner to realize their overall vision for the route, they may need to edit each type in a different way.
The following illustrates how arf can be used to make changes to a network, then describe the access implications. I will modify two King County Metro routes serving Seattle, routes 3 and 11. Route 3 goes from Madrona, through Downtown Seattle, to the Summit area of Capitol Hill. Route 11 travels from Madison Park to Downtown Seattle via Capitol Hill. I want to see if connecting Summit to the Capitol Hill Link light rail station is better for access than Summit’s present connection to downtown via route 3. To do this, I’ll send route 11 to Summit instead of downtown. This will also boost Summit’s frequency and span of service. Riders who use the portion of route 11 between Capitol Hill Station and downtown will have to transfer, but have a fast and frequent option in the Link.
Route 3 will simply be truncated so it no longer serves Summit.
To start, use arf to generate a template that will become the modifications file.
arf modification-template 3-11 --areas 237385 --gtfs "http://metro.kingcounty.gov/gtfs/google_transit.zip,https://gtfs.sound.obaweb.org/prod/40_gtfs.zip" --date 2026-03-31 --routes 3,11
This creates a file named 3-11.json, an abridged version of which is replicated below. In reality, the StopsComment shows every stop that makes up the trip type, and TripsComment lists every time at which this type of trip runs.
{
"StopModifications": [],
"RouteModifications": [
{
"RouteName": "11",
"TripType": "T1",
"RoutingModifications": [],
"ScheduleModifications": [],
"StopsComment": [
"1) Pine St & 9th Ave (1085)",
...
"28) E Mcgilvra St & 42nd Ave E (12210)"
],
"TripsComment": [
"23:37:00",
...
"00:17:00"
]
},
{
"RouteName": "11",
"TripType": "T2",
"RoutingModifications": [],
"ScheduleModifications": [],
"StopsComment": [
"1) E Mcgilvra St & 42nd Ave E (12210)",
...
"22) Pine St & 9th Ave (1085)"
],
"TripsComment": [
"00:00:00",
...
"00:00:00"
]
},
{
"RouteName": "3",
"TripType": "T1",
"RoutingModifications": [],
"ScheduleModifications": [],
"StopsComment": [
"1) Bellevue Ave E & Bellevue Pl E (13390)",
...
"28) 34th Ave & E Union St (12690)"
],
"TripsComment": [
"07:31:00",
...
"19:01:00"
]
},
{
"RouteName": "3",
"TripType": "T2",
"RoutingModifications": [],
"ScheduleModifications": [],
"StopsComment": [
"1) 3rd Ave & Pine St (430)",
...
"21) 34th Ave & E Union St (12690)"
],
"TripsComment": [
"23:43:00",
...
"00:13:00"
]
},
{
"RouteName": "3",
"TripType": "T3",
"RoutingModifications": [],
"ScheduleModifications": [],
"StopsComment": [
"1) Bellevue Ave E & Bellevue Pl E (13390)",
...
"13) S Jackson St & 5th Ave S (1471)"
],
"TripsComment": [
"19:24:00"
]
},
{
"RouteName": "3",
"TripType": "T4",
"RoutingModifications": [],
"ScheduleModifications": [],
"StopsComment": [
"1) 34th Ave & E Union St (12690)",
...
"29) Bellevue Ave E & Bellevue Pl E (13390)"
],
"TripsComment": [
"06:35:00",
...
"18:34:00"
]
},
{
"RouteName": "3",
"TripType": "T5",
"RoutingModifications": [],
"ScheduleModifications": [],
"StopsComment": [
"1) 34th Ave & E Union St (12690)",
...
"21) 3rd Ave & Virginia St (600)"
],
"TripsComment": [
"23:42:00",
...
"00:12:00"
]
}
]
}
The RouteModifications list is populated with entries for every type of route 3 and 11 trip that runs on the date specified in the command, March 31, 2026. The StopsComment and TripsComment fields identify what kind of service the numeric trip types indicate. For instance, route 11 type T1 is the standard outbound route 11, route 3 type T4 signifies trips from Madrona to Summit that run most of the day, and route 3 type T3 is a one-off trip wherein a bus originating in Summit returns to the garage via South Jackson Street.
For route 3, T2 and T5 already represent the desired endpoint of running from downtown to Madrona, and vice versa. T1, T3, and T4 must be changed to skip Summit and instead follow the paths of T2 and T5.
Route 3 type T1 must be truncated at 3rd Ave & Union St, then extended to 3rd Ave & Pine St to match T2. This is done by adding two directives to the RoutingModifications list within T1.
"RoutingModifications": [
{
"Operation": "TRUNCATE",
"Position": 9,
"End": "BEGINNING"
},
{
"Operation": "EXTEND",
"End": "BEGINNING",
"StopIds": [
"430"
]
}
],
The first directive changes the route to begin at its ninth stop. T1’s StopsComment associates each stops name with its numeric position, making it useful for this. T2’s StopsComment aids in creating the second directive, because it provides the stop id for 3rd Ave & Pine St. The same directives can be copied and pasted into T3’s RoutingModifications, since the same number of stops must be lopped off the beginning and replaced by 3rd Ave & Pine St.
The approach for modifying T4 is similar:
"RoutingModifications": [
{
"Operation": "TRUNCATE",
"Position": 20,
"End": "ENDING"
},
{
"Operation": "EXTEND",
"End": "ENDING",
"StopIds": [
"600"
]
}
],
Modifying route 11 is done in the same way. With those changes in place, the modifications file will look like the following. To save space, I’ve deleted the comment fields, as they are optional from arf’s perspective.
{
"StopModifications": [],
"RouteModifications": [
{
"RouteName": "11",
"TripType": "T1",
"RoutingModifications": [
{
"Operation": "TRUNCATE",
"Position": 9,
"End": "BEGINNING"
},
{
"Operation": "EXTEND",
"End": "BEGINNING",
"StopIds": [
"13390",
"13410",
"13420",
"13430"
]
}
],
"ScheduleModifications": []
},
{
"RouteName": "11",
"TripType": "T2",
"RoutingModifications": [
{
"Operation": "TRUNCATE",
"Position": 18,
"End": "ENDING"
},
{
"Operation": "EXTEND",
"End": "ENDING",
"StopIds": [
"13480",
"13500",
"13510",
"13390"
]
}
],
"ScheduleModifications": []
},
{
"RouteName": "3",
"TripType": "T1",
"RoutingModifications": [
{
"Operation": "TRUNCATE",
"Position": 9,
"End": "BEGINNING"
},
{
"Operation": "EXTEND",
"End": "BEGINNING",
"StopIds": [
"430"
]
}
],
"ScheduleModifications": []
},
{
"RouteName": "3",
"TripType": "T2",
"RoutingModifications": [],
"ScheduleModifications": []
},
{
"RouteName": "3",
"TripType": "T3",
"RoutingModifications": [
{
"Operation": "TRUNCATE",
"Position": 9,
"End": "BEGINNING"
},
{
"Operation": "EXTEND",
"End": "BEGINNING",
"StopIds": [
"430"
]
}
],
"ScheduleModifications": []
},
{
"RouteName": "3",
"TripType": "T4",
"RoutingModifications": [
{
"Operation": "TRUNCATE",
"Position": 20,
"End": "ENDING"
},
{
"Operation": "EXTEND",
"End": "ENDING",
"StopIds": [
"600"
]
}
],
"ScheduleModifications": []
},
{
"RouteName": "3",
"TripType": "T5",
"RoutingModifications": [],
"ScheduleModifications": []
}
]
}
Arf would reject this file though. When extending a route, arf needs to estimate the time it would take to traverse consecutive stops. When available, it uses durations found in the agency’s schedule4. Currently, there is no transit service that runs between Bellevue Ave E & E Denny Way and E Olive Way & Summit Ave E, or between E John St & Broadway E and Summit Ave E & E Olive Way, though. Therefore, it is necessary to explicitly provide an estimate for these stop pairs.
To do this, add directives to the StopModifications list. These will insert a one minute duration between the currently-disconnected stops.
"StopModifications": [
{
"Operation": "RELATE",
"Relationship": {
"Origin": "13430",
"Destination": "29268",
"Time": 60
}
},
{
"Operation": "RELATE",
"Relationship": {
"Origin": "29262",
"Destination": "13480",
"Time": 60
}
}
],
With these directives in place, arf will accept the file.
Understanding the access implications of these changes first necessitates knowing the baseline access. The following command computes access using the default parameters: a full-day analysis with a 30 minute time budget.
arf access 3-11-baseline --areas 237385 --gtfs "http://metro.kingcounty.gov/gtfs/google_transit.zip,https://gtfs.sound.obaweb.org/prod/40_gtfs.zip" --date 2026-03-31 --outputs sector-segment-map --concurrency 8
The baseline has 145,452,630,516 combinations of origin, destination, and time of day for which a walking and transit trip is possible within 30 minutes.
Now the modification file can be used to compute the access with the change in place. Incorporate it into arf’s operations by adding the --modifications flag.
arf access 3-11-restructure --areasFile 237385-ways.json --gtfs "http://metro.kingcounty.gov/gtfs/google_transit.zip,https://gtfs.sound.obaweb.org/prod/40_gtfs.zip" --date 2026-03-31 --outputs sector-segment-map --concurrency 8 --modifications 3-11.json
This results in 145,473,588,172 possible combinations, which is an increase, but only of .01% overall. To best understand where within Seattle the change has improved and degraded access, I’ll be working on the ability to generate a network-wide comparative map in April. For now, the comparative, side-by-side, and blink maps demonstrate the change’s impact on individual sectors. The animation below shows some examples: the overall improvements for those originating in Summit, the marginal access loss to the Denny Triangle from Madison Park, and largely unchanged access in the Cherry Hill section of route 3.
While the access gain is small, it is achieved in spite of a service cut. Arf can determine by how much the modifications reduced service time expenditure.
arf service-time --areasFile 237385-ways.json --gtfs "http://metro.kingcounty.gov/gtfs/google_transit.zip,https://gtfs.sound.obaweb.org/prod/40_gtfs.zip" --date 2026-03-31 --outputs sector-segment-map --concurrency 8
5336 hours, 13 minutes, and 10 seconds are required to run one day of weekday service currently.
The --modifications flag can be added to the service-time command as well.
arf service-time --areasFile 237385-ways.json --gtfs "http://metro.kingcounty.gov/gtfs/google_transit.zip,https://gtfs.sound.obaweb.org/prod/40_gtfs.zip" --date 2026-03-31 --outputs sector-segment-map --concurrency 8 --modifications 3-11.json
This shows that the expenditure has been reduced to 5314 hours, 37 minutes, and 50 seconds.
In theory, the saved time could be reinvested in new service, improving access further without an increase in King County Metro’s costs. It may be possible to boost route 11 from 20-minute to 15-minute headways most of the day, meeting Metro’s definition of frequent service. Alternatively, extending route 11 to Eastlake would provide a new east-west transit connection in Seattle. In practice, whether these investments are feasible is more complicated. Agencies incur expenses whenever a driver is working, even if their vehicle is not serving passengers. Schedules that specify blocks, collections of trips that are served by the same vehicle, account for at least the layover component of this out-of-service time. Grouping modified trips into new blocks, and measuring the duration of all blocks for the original and modified networks, would provide more insight into whether hypothetical changes are feasible.
Arf doesn’t understand blocks yet, nor can it adjust headways or add the new stops necessary to model running transit service from Summit to Eastlake. I plan to implement this functionality in April.
-
It looks like Conveyal may also support specifying network modifications in json, but this is not described in its documentation or shown in the demo videos. ↩︎
-
When using the BusGraphs Access Analyzer, this was the workflow for modifying a network:
- Use the ride component to create a network from a transit agency’s GTFS files.
- Use a network viewer web interface to look up the ids of the routings associated with the transit routes one wants to change. Routings are sequences of stops, and technically have a many-to-many relationship with routes. While routes are familiar to transit riders and planners, routings are a concept internal to the Access Analyzer.
- Create the modifications file containing directives for modifying routings.
- Submit this file to ride creating a new, network.
- Use this network in an access analysis.
Working with routings was clunky and error prone. It was easy to forget a routing, edit one twice, or edit the wrong one inadvertently. Also, routing ids were not consistent, even when networks were produced from the same GTFS files. A transit agency could make a minimal change to its schedule, such that changes in the modifications file could still be applicable, but the modifications file could no longer be used because the routing ids in the new network would be entirely different. Nevertheless, I successfully used this approach for three full network redesigns in Seattle. Arf’s templated approach is a significant refinement. ↩︎
-
Types are numbered in a consistent order that:
- Groups together types with the same direction specified in GTFS.
- Puts types with more stops ahead of types with fewer stops.
- Breaks ties arbitrarily, but consistently, based on which stops make up the type.
This means that even if a route changes slightly between revisions of an agency’s schedule, the type numbers specified in the modifications file will likely still correspond to the intended type. ↩︎
-
In specific, arf determines all the stop pairs that are direct neighbors on trips, looks at all the scheduled durations between those pairs, sorts them by time of day of the trip, and chooses the duration based on the closest time of day for the point in the trip that it is extending. ↩︎