Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Flex example page #301

Merged
merged 13 commits into from
Apr 10, 2024
Binary file added docs/assets/deviated _drop-off _route.zip
Binary file not shown.
Binary file added docs/assets/deviated_route_zones.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
324 changes: 324 additions & 0 deletions docs/schedule/examples/flex.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
<a class="pencil-link" href="https://github.com/MobilityData/gtfs.org/edit/main/docs/schedule/examples/flex.md" title="Edit this page" target="_blank">
<svg class="pencil" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10 20H6V4h7v5h5v3.1l2-2V8l-6-6H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h4v-2m10.2-7c.1 0 .3.1.4.2l1.3 1.3c.2.2.2.6 0 .8l-1 1-2.1-2.1 1-1c.1-.1.2-.2.4-.2m0 3.9L14.1 23H12v-2.1l6.1-6.1 2.1 2.1Z"></path></svg>
</a>

# Demand responsive services

<hr>

Flex is a GTFS extension project which was adopted officially into the GTFS specification in March 2024, its aims to facilitate discoverability of Demand Responsive Transportation Services.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: suggestion to link "demand responsive" and "GTFS Flex" together a little more clearly.

Demand-responsive services can be modeled in GTFS using the GTFS Flex spec extension, which was adopted in March 2024.


The following example demonstrates how to model different demand responsive service use cases using Flex. **Please note that the following examples are not necessarily an accurate or complete representation of the agencies’ services.**

## On-demand services within a single zone

Demand-repsonsive services can operate within a specific zone, allowing riders to book pickups at any point A within the zone and drop-offs at any point B within the same zone. An example of this is the [Heartland Express Transit](https://www.co.brown.mn.us/heartland-express-transit?view=category&id=56) service in Minnesota, USA.

<sup>[Download Heartland Express example dataset](../../../assets/on-demand_services_within_a_single_zone.zip)</sup>

### Define trips

Heartland Express service hours are as follow:

- Weekdays:
- 8:00 AM - 5:00 PM
- 6:15 AM – 5:45 PM (New Ulm zone only)
- Sunday: 8:00 AM - Noon (New Ulm zone only)

The New Ulm city zone is contained within the Brown County zone. To avoid the ["zone overlap constraint"](/schedule/examples/flex/#zone-overlap-constraint) issue, Heartland Express can be defined with four trips:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gives a 404; I think the appropriate way to refer to something in the same page is to use (#zone-overlap-constraint)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated! the previous link was working for me when I displayed the changes locally but change it to the suggested link, it should be ok now!


- Service in New Ulm zone from 6:15 AM to 8:00 AM on weekdays.
- County-wide service from 8:00 AM to 5:00 PM on weekdays.
- Service in New Ulm zone from 5:00 PM to 5:45 PM on weekdays.
- Service in New Ulm zone from 8:00 AM to 12:00 PM on Sundays.

[**trips.txt**](../../reference/#tripstxt)

route_id | service_id | trip_id
-- | -- | --
74362 | c_67295_b_77497_d_31 | t_5374945_b_77497_tn_0
74362 | c_67295_b_77497_d_31 | t_5374946_b_77497_tn_0
74362 | c_67295_b_77497_d_31 | t_5374944_b_77497_tn_0
74362 | c_67295_b_77497_d_64 | t_5374947_b_77497_tn_0

`service_id = c_67295_b_77497_d_31` refers to weekdays, `service_id = c_67295_b_77497_d_64` refers to Sunday.

### Define zone (GeoJSON locations)

Using locations.geojson to define the operational zone of Heartland Express service, separate zones must be defined for Brown County and New Ulm City. Below is a simplified GeoJSON defining the zone of Brown County:
```json
{
"type": "FeatureCollection",
"features": [
{
"id": "area_708",
"type": "Feature",
"geometry": {
"type": "Polygon",
# Simplified, only presenting 3 coordinates here.
"coordinates": [
[
[
-94.7805702,
44.4560958
],
[
-94.7805608,
44.4559928
],
[
-94.7805218,
44.4559649
]
]
]
},
"properties": {}
}
]
```

### Define booking rules

Here are booking rules that apply to all Heartland Express services:

- Ride requests should be made between 8 AM and 3 PM weekdays.
- Rides must be requested one business day prior to day of the ride.
- Ride requests can be made up to 14 days in advance.

Using `booking_type = 2` indicates that the service requires up to prior day(s) booking. `prior_notice_last_day = 1` and `prior_notice_start_day = 14` indicate indicate that the service can be booked as early as 14 days in advance and as late as the day before.

[**booking_rules.txt**](../../reference/#booking_rulestxt)

booking_rule_id | booking_type | prior_notice_start_day | prior_notice_start_time | prior_notice_last_day | prior_notice_last_time | message | phone_number | info_url
-- | -- | -- | -- | -- | -- | -- | -- | --
booking_route_74362 | 2 | 14 | 8:00:00 | 1 | 15:00:00 | Brown County Heartland Express provides door-to-door on-demand transportation. To request a ride, call 1-507-359-2717 or 1-800-707-2717 by 3pm at least one business day ahead of your trip. | (507) 359-2717 | https://www.co.brown.mn.us/heartland-express-transit

### Define stop times

The operating hours are defined using the `start_pickup_drop_off_window` and `end_pickup_drop_off_window` fields. Travel within the same zone requires two records in stop_times.txt with the same `location_id`.

- The first record with `pickup_type = 2` and `drop_off_type = 1` indicates that booking pickup is allowed in the zone.
- The second record with `pickup_type = 1` and `drop_off_type = 2` indicates that booking drop-off is allowed in the zone.

[**stop_times.txt**](../../reference/#stop_timestxt)

trip_id | location_id | stop_sequence | start_pickup_drop_off_window | end_pickup_drop_off_window | pickup_type | drop_off_type | pickup_booking_rule_id | drop_off_booking_rule_id
-- | -- | -- | -- | -- | -- | -- | -- | --
t_5374944_b_77497_tn_0 | area_715 | 1 | 06:15:00 | 08:00:00 | 2 | 1 | booking_route_74362 | booking_route_74362
t_5374944_b_77497_tn_0 | area_715 | 2 | 06:15:00 | 08:00:00 | 1 | 2 | booking_route_74362 | booking_route_74362
t_5374945_b_77497_tn_0 | area_708 | 1 | 08:00:00 | 17:00:00 | 2 | 1 | booking_route_74362 | booking_route_74362
t_5374945_b_77497_tn_0 | area_708 | 2 | 08:00:00 | 17:00:00 | 1 | 2 | booking_route_74362 | booking_route_74362
t_5374946_b_77497_tn_0 | area_715 | 1 | 17:00:00 | 17:45:00 | 2 | 1 | booking_route_74362 | booking_route_74362
t_5374946_b_77497_tn_0 | area_715 | 2 | 17:00:00 | 17:45:00 | 1 | 2 | booking_route_74362 | booking_route_74362
t_5374947_b_77497_tn_0 | area_715 | 1 | 08:00:00 | 12:00:00 | 2 | 1 | booking_route_74362 | booking_route_74362
t_5374947_b_77497_tn_0 | area_715 | 2 | 08:00:00 | 12:45:00 | 1 | 2 | booking_route_74362 | booking_route_74362

`area_715` refers to New Ulm City zone, `area_708` refers to Bronw County zone.

## On-demand services across multiple zones

Some demand-responsive services operate across multiple distinct zones, where riders can book pickups at any location A within one area and drop-offs at any location within another area. For example, [Minnesota River Valley Transit](https://www.saintpetermn.gov/330/Dial-a-Ride) offers on-demand services between Saint Peter and Kasota cities:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: do we need the "A"?


<sup>[Download River Valley Transit example dataset](../../../assets/on-demand_services_between_multiple_zones.zip)</sup>

### Define trips

Similar to the previous example, because service hours vary on different days, it's necessary to define trips separately for weekdays and Saturdays.

[**trips.txt**](../../reference/#tripstxt)

route_id | service_id | trip_id
-- | -- | --
74375 | weekdays | t_5298036_b_77503_tn_0
74375 | saturdays | t_5298041_b_77503_tn_0
74375 | weekdays | t_5298046_b_77503_tn_0
74375 | saturdays | t_5298051_b_77503_tn_0

(Define booking rules and zones using booking_rules.txt and locations.geojson in the same way as the previous example)

### Define stop times

The following data indicates that pickup is only allowed in one zone and drop-off is only allowed in another zone. pickup and drop-off in the same zone are not allowed.

[**stop_times.txt**](../../reference/#stop_timestxt)

trip_id | location_id | stop_sequence | start_pickup_drop_off_window | end_pickup_drop_off_window | pickup_type | drop_off_type | pickup_booking_rule_id | drop_off_booking_rule_id
-- | -- | -- | -- | -- | -- | -- | -- | --
t_5298036_b_77503_tn_0 | area_713 | 1 | 06:30:00 | 20:00:00 | 2 | 1 | booking_route_74375 | booking_route_74375
t_5298036_b_77503_tn_0 | area_714 | 2 | 06:30:00 | 20:00:00 | 1 | 2 | booking_route_74375 | booking_route_74375
t_5298041_b_77503_tn_0 | area_713 | 1 | 09:00:00 | 19:00:00 | 2 | 1 | booking_route_74375 | booking_route_74375
t_5298041_b_77503_tn_0 | area_714 | 2 | 09:00:00 | 19:00:00 | 1 | 2 | booking_route_74375 | booking_route_74375
t_5298046_b_77503_tn_0 | area_714 | 1 | 06:30:00 | 20:00:00 | 2 | 1 | booking_route_74375 | booking_route_74375
t_5298046_b_77503_tn_0 | area_713 | 2 | 06:30:00 | 20:00:00 | 1 | 2 | booking_route_74375 | booking_route_74375
t_5298051_b_77503_tn_0 | area_714 | 1 | 09:00:00 | 19:00:00 | 2 | 1 | booking_route_74375 | booking_route_74375
t_5298051_b_77503_tn_0 | area_713 | 2 | 09:00:00 | 19:00:00 | 1 | 2 | booking_route_74375 | booking_route_74375

## On-demand services where riders must be picked up and dropped off at specific locations

In certain demand-responsive services, riders are unable to specify pickup and drop-off at any location within a zone. Instead, riders can only book to be picked up and dropped off at specific designated stops(collection points/ virtual stops). An example of this is the [RufBus service](https://uvg-online.com/rufbus-angermuende/) in Angermünde and Gartz, Germany:

### Define trips

Route 476 offers on-demand services between each stop in the Angermünde region. They operate two services (one for weekdays and the other for weekends), with each one having a single trip_id associated.

[**trips.txt**](../../reference/#tripstxt)

route_id | service_id | trip_id
-- | -- | --
476 | on_demand_weekdays | 476_weekdays
476 | on_demand_weekends | 476_weekends

### Define location groups

As riders can book services between each stop, to avoid defining all stop-to-stop combinations in stop_times.txt, the appropriate approach is to define these stops as a location group using location_groups.txt and location_group_stops.txt.

[**location_groups.txt**](../../reference/#location_groupstxt)

location_group_id | location_group_name
-- | --
476_stops | durch den RufBus 476 bedientes Gebiet im Raum Angermünde

[**location_group_stops.txt**](../../reference/#location_group_stopstxt)

location_group_id | stop_id
-- | --
476_stops | de:12073:900340004::1
476_stops | de:12073:900340004::2
476_stops | de:12073:900340004::3
476_stops | de:12073:900340004::4
476_stops | de:12073:900340100::1
476_stops | de:12073:900340100::2
476_stops | ...

### Define booking rules

The 476 route service requires booking at least one hour in advance. Using `booking_type = 1` indicates that the service requires up to same-day booking with advance notice. The `prior_notice_duration_min = 60` indicates a requirement for booking at least 60 minutes in advance.

There are slight differences between weekday and weekend bookings, so separate booking rules can be defined for weekday and holiday services. More details can be provided in the `message` field. Information and booking page links can be provided in the `info_url` and `booking_url` fields.

[**booking_rules.txt**](../../reference/#booking_rulestxt)

booking_rule_id | booking_type | prior_notice_duration_min | message | phone_number | info_url | booking_url
-- | -- | -- | -- | -- | -- | --
flächenrufbus_angermünde_weekdays | 1 | 60 | Anmeldung mind. 60min vorher erforderlich, per Anruf zwischen 08:00 und 24:00 möglich, oder online rund um die Uhr | +49 3332 442 755 | https://uvg-online.com/rufbus-angermuende/ | https://uvg.tdimo.net/bapp/#/astBuchungenView
flächenrufbus_angermünde_weekends | 1 | 60 | 1€ Komfortzuschlag pro Person; Anmeldung mind. 60min vorher erforderlich, per Anruf zwischen 08:00 und 24:00 möglich, oder online rund um die Uhr | +49 3332 442 755 | https://uvg-online.com/rufbus-angermuende/ | https://uvg.tdimo.net/bapp/#/astBuchungenView

### Define stop times

The 476 route operates from 5:30 PM to 10:00 PM on weekdays and from 8:00 AM to 10:00 PM on weekends. The operating hours are defined using the `start_pickup_drop_off_window` and `end_pickup_drop_off_window` fields. Travel within the same location group requires two records in stop_times.txt with the same `location_group_id`.

- The first record with `pickup_type = 2` and `drop_off_type = 1` indicates that booking pickup is allowed at the location group.
- The second record with `pickup_type = 1` and `drop_off_type = 2` indicates that booking drop-off is allowed at the location group.

[**stop_times.txt**](../../reference/#stop_timestxt)

trip_id | location_group_id | stop_sequence | start_pickup_drop_off_window | end_pickup_drop_off_window | pickup_type | drop_off_type | pickup_booking_rule_id | drop_off_booking_rule_id
-- | -- | -- | -- | -- | -- | -- | -- | --
476_weekdays | 476_stops | 1 | 17:30:00 | 22:00:00 | 2 | 1 | flächenrufbus_angermünde_weekdays | flächenrufbus_angermünde_weekdays
476_weekdays | 476_stops | 2 | 17:30:00 | 22:00:00 | 1 | 2 | flächenrufbus_angermünde_weekdays | flächenrufbus_angermünde_weekdays
476_weekends | 476_stops | 1 | 08:00:00 | 22:00:00 | 2 | 1 | flächenrufbus-angermünde_weekdays | flächenrufbus_angermünde_weekends
476_weekends | 476_stops | 2 | 08:00:00 | 22:00:00 | 1 | 2 | flächenrufbus-angermünde_weekdays | flächenrufbus-angermünde_weekends

## Deviated route

"Route deviation" refers to services where the vehicle follows a fixed route with a set sequence of stops but has the flexibility to deviate from this route to pick up or drop off riders between stops. Typically, deviations are limited to maintain service punctuality, and prior booking is required for deviated pickups and drop-offs.

In this example, the [Hermann Express](https://www.newulmmn.gov/553/Hermann-Express-City-Bus-Service) service in New Ulm City allows users to be picked up only at fixed stops and to be droped off at any point within a specific deviation area between these stops.

**The example below has been simplified, download the [Hermann Express example dataset](../../../assets/deviated_drop-off_route.zip) for more details.**

### Define trips

Since this type of service still involves a series of fixed stops and a fixed schedule, defining trips is similar to normal fixed-route bus services. It requires defining the trips served by each route throughout all relevant service periods.

[**trips.txt**](../../reference/#tripstxt)

route_id | service_id | trip_id | share_id
-- | -- | -- | --
74513 | c_67295_b_77497_d_31 | t_5374704_b_77497_tn_0 | p_1426044
74513 | c_67295_b_77497_d_31 | t_5374699_b_77497_tn_0 | p_1426044
74513 | c_67295_b_77497_d_31 | t_5374698_b_77497_tn_0 | p_1426044
74513 | c_67295_b_77497_d_31 | t_5374697_b_77497_tn_0 | p_1426044
... | ... | ... | ...

### Define zones (GeoJSON location)

Using [locations.geojson] (../../reference/#locationsgeojson) to define zones for deviated route. Typically, deviations are limited to keep the service on schedule. Therefore, as the vehicle travels, the deviation area between each fixed stop may vary accordingly. The area for route deviation may look like the image below:

<div class="flex-photos">
<img src="../../../assets/deviated_route_zones.png" alt="deviated route zones">
</div>

### Define stop times

For fixed stops, define fields such as `arrival_time`, `departure_time`, and `stop_id` in a manner similar to normal bus routes. Between fixed stops, define the zones where deviation is allowed. `pickup_type = 1` and `drop_off_type = 3` indicates that deviated pickup is not allowed (limiting pickup to fixed stops only), and that riders must coordinate with the driver to be dropped off in the deviation zone.

[**stop_times.txt**](../../reference/#stop_timestxt)

trip_id | arrival_time | departure_time | stop_id | location_id | stop_sequence | start_pickup_drop_off_window | end_pickup_drop_off_window | pickup_type | drop_off_type | shape_dist_traveled | pickup_booking_rule_id | drop_off_booking_rule_id
-- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | --
t_5374696_b_77497_tn_0 | 08:00:00 | 08:00:00 | 4149546 | | 1 | | | | | 0 | |
t_5374696_b_77497_tn_0 | | | | radius_300_s_4149546_s_4149547 | 2 | 08:00:00 | 8:02:22 | 1 | 3 | | booking_route_74513 | booking_route_74513
t_5374696_b_77497_tn_0 | 08:02:22 | 08:02:22 | 4149547 | | 3 | | | | | 1221.627114 | |
t_5374696_b_77497_tn_0 | | | | radius_300_s_4149546_s_4149548 | 4 | 08:02:22 | 8:03:00 | 1 | 3 | | booking_route_74513 | booking_route_74513
t_5374696_b_77497_tn_0 | 08:03:22 | 08:03:22 | 4149548 | | 5 | | | | | 1548.216356 | |
t_5374696_b_77497_tn_0 | | | | radius_300_s_4149546_s_4149549 | 6 | 08:03:22 | 8:05:00 | 1 | 3 | | booking_route_74513 | booking_route_74513
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ...
t_5374696_b_77497_tn_0 | 08:50:00 | 08:50:00 | 4210601 | | 35 | | | | | 23429.19558 | |
t_5374696_b_77497_tn_0 | 08:56:00 | 08:56:00 | 4149564 | | 36 | | | | | 25320.8471 | |

## Routing behavior

### Ignoring intermediate stop times records with pickup/drop-off Windows

When providing routing or travel time between the origin and destination, data consumers should ignore intermediate stop_times.txt records that have `start_pickup_drop_off_window` and `end_pickup_drop_off_window` defined. For example:

trip_id | location_id | stop_sequence | pickup_type | drop_off_type | start_pickup_drop_off_window | end_pickup_drop_off_window
-- | -- | -- | -- | -- | -- | --
tripA | Zone1 | 1 | 2 | 1 | 08:00:00 | 18:00:00
tripA | Zone2 | 2 | 1 | 2 | 08:00:00 | 14:00:00
tripA | Zone3 | 3 | 1 | 2 | 10:00:00 | 18:00:00

Consumers should not take Zone2 into consideration when providing routing or travel time for a trip from Zone1 to Zone3.

### Zone overlap constraint

Simultaneous overlap of locations.geojson `id` geometry, `start/end_pickup_drop_off_window` time, and `pickup_type` or `drop_off_type` between two or more stop_times.txt records with the same `trip_id` is forbidden.

For example:
(Where `northportland` refers to a zone within `portland`)

**Forbidden**

trip_id | location_id | stop_sequence | pickup_type | drop_off_type | start_pickup_drop_off_window | end_pickup_drop_off_window
-- | -- | -- | -- | -- | -- | --
tripA | portland | 1 | 2 | 1 | 08:00:00 | 12:00:00
tripA | northportland | 2 | 2 | 1 | 10:00:00 | 14:00:00
tripA | vancouver | 3 | 1 | 2 | 10:00:00 | 14:00:00

**Allowed**

trip_id | location_id | stop_sequence | pickup_type | drop_off_type | start_pickup_drop_off_window | end_pickup_drop_off_window
-- | -- | -- | -- | -- | -- | --
tripA | portland | 1 | 2 | 1 | 08:00:00 | 12:00:00
tripA | northportland | 2 | 2 | 1 | 12:00:00 | 14:00:00
tripA | vancouver | 3 | 1 | 2 | 10:00:00 | 14:00:00

or

trip_id | location_id | stop_sequence | pickup_type | drop_off_type | start_pickup_drop_off_window | end_pickup_drop_off_window
-- | -- | -- | -- | -- | -- | --
tripA | portland | 1 | 2 | 1 | 08:00:00 | 12:00:00
tripA | northportland | 2 | 1 | 2 | 10:00:00 | 14:00:00
tripA | vancouver | 3 | 1 | 2 | 10:00:00 | 14:00:00

or

trip_id | location_id | stop_sequence | pickup_type | drop_off_type | start_pickup_drop_off_window | end_pickup_drop_off_window
-- | -- | -- | -- | -- | -- | --
tripA | portland | 1 | 2 | 1 | 08:00:00 | 12:00:00
tripA | gresham | 2 | 2 | 1 | 10:00:00 | 14:00:00
tripA | vancouver | 3 | 1 | 2 | 10:00:00 | 14:00:00

<hr>
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,7 @@ nav:
- Translations: schedule/examples/translations.md
- Feed information: schedule/examples/feed-info.md
- Dataset attributions: schedule/examples/attributions.md
- Demand responsive services: schedule/examples/flex.md
- Changes:
- schedule/changes/index.md
- Specification Amendment Process: schedule/process.md
Expand Down