From 40854cc557190c86fdc2984973c37b02a37a908d Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Tue, 22 Apr 2025 08:53:24 -0400 Subject: [PATCH 01/18] Update reference.md --- gtfs/spec/en/reference.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index f4a21be3..8064153a 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -26,6 +26,7 @@ This document defines the format and structure of the files that comprise a GTFS - [fare\_products.txt](#fare_productstxt) - [fare\_leg\_rules.txt](#fare_leg_rulestxt) - [fare_leg_join_rules.txt](#fare_leg_join_rulestxt) + - [fare\_leg\_distance\_rules.txt](#fare_leg_distance_rulestxt) - [fare\_transfer\_rules.txt](#fare_transfer_rulestxt) - [areas.txt](#areastxt) - [stop_areas.txt](#stop_areastxt) @@ -63,6 +64,8 @@ This section defines terms that are used throughout this document. * **Sub-journey** - Two or more legs that comprise a subset of a journey. * **Fare product** - Purchassable fare products that can be used to pay for or validate travel. * **Effective Fare Leg** - A sub-journey of two or more legs that should be treated as a single leg for matching rules in [fare_leg_rules.txt](#fare_leg_rulestxt) for the purposes of fare calculation. +* **Distance-Based Leg** - A leg which is based on the distance (fare units) traveled. In [fare_leg_rules.txt](#fare_leg_rulestxt), a distance-based leg is a leg that has at least fare_leg_rules.min_distance or fare_leg_rules.max_distance assigned (not empty). +**Effective Distance-Based Fare Leg** - A sub-journey of two or more distance-based legs that should be treated as a single distance-based leg for matching rules in [fare_leg_rules.txt](#fare_leg_rulestxt) for the purposes of fare calculation for distance-based legs only. ### Presence @@ -129,6 +132,7 @@ This specification defines the following files: | [fare_products.txt](#fare_productstxt) | Optional | To describe the different types of tickets or fares that can be purchased by riders.

File [fare_products.txt](#fare_productstxt) describes fare products that are not represented in [fare_attributes.txt](#fare_attributestxt) and [fare_rules.txt](#fare_rulestxt). As such, the use of [fare_products.txt](#fare_productstxt) is entirely separate from files [fare_attributes.txt](#fare_attributestxt) and [fare_rules.txt](#fare_rulestxt). | | [fare_leg_rules.txt](#fare_leg_rulestxt) | Optional | Fare rules for individual legs of travel.

File [fare_leg_rules.txt](#fare_leg_rulestxt) provides a more detailed method for modeling fare structures. As such, the use of [fare_leg_rules.txt](#fare_leg_rulestxt) is entirely separate from files [fare_attributes.txt](#fare_attributestxt) and [fare_rules.txt](#fare_rulestxt). | | [fare_leg_join_rules.txt](#fare_leg_join_rulestxt) | Optional | Rules for defining two or more legs should be considered as a single **effective fare leg** for the purposes of matching against rules in [fare_leg_rules.txt](#fare_leg_rulestxt)| +| [fare_leg_distance_rules.txt](#fare_leg_distance_rulestxt) | Conditionally Forbidden | Rules for defining two or more distance-based legs that should be considered as a single effective distance-based fare leg for the purposes of matching against rules in [fare_leg_rules.txt](#fare_leg_rulestxt)

Conditionally Forbidden:
- **Forbidden** if no distance-based leg exists in [fare_leg_rules.txt](#fare_leg_rulestxt).
- Optional otherwise. | | [fare_transfer_rules.txt](#fare_transfer_rulestxt) | Optional | Fare rules for transfers between legs of travel.

Along with [fare_leg_rules.txt](#fare_leg_rulestxt), file [fare_transfer_rules.txt](#fare_transfer_rulestxt) provides a more detailed method for modeling fare structures. As such, the use of [fare_transfer_rules.txt](#fare_transfer_rulestxt) is entirely separate from files [fare_attributes.txt](#fare_attributestxt) and [fare_rules.txt](#fare_rulestxt). | | [areas.txt](#areastxt) | Optional | Area grouping of locations. | | [stop_areas.txt](#stop_areastxt) | Optional | Rules to assign stops to areas. | @@ -304,6 +308,7 @@ Primary key (`trip_id`, `stop_sequence`) | `continuous_pickup` | Enum | **Conditionally Forbidden** | Indicates that the rider can board the transit vehicle at any point along the vehicle’s travel path as described by [shapes.txt](#shapestxt), from this `stop_time` to the next `stop_time` in the trip’s `stop_sequence`. Valid options are:

`0` - Continuous stopping pickup.
`1` or empty - No continuous stopping pickup.
`2` - Must phone agency to arrange continuous stopping pickup.
`3` - Must coordinate with driver to arrange continuous stopping pickup.

If this field is populated, it overrides any continuous pickup behavior defined in [routes.txt](#routestxt). If this field is empty, the `stop_time` inherits any continuous pickup behavior defined in [routes.txt](#routestxt).

**Conditionally Forbidden**:
- **Forbidden** if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.
- Optional otherwise. | | `continuous_drop_off` | Enum | **Conditionally Forbidden** | Indicates that the rider can alight from the transit vehicle at any point along the vehicle’s travel path as described by [shapes.txt](#shapestxt), from this `stop_time` to the next `stop_time` in the trip’s `stop_sequence`. Valid options are:

`0` - Continuous stopping drop off.
`1` or empty - No continuous stopping drop off.
`2` - Must phone agency to arrange continuous stopping drop off.
`3` - Must coordinate with driver to arrange continuous stopping drop off.

If this field is populated, it overrides any continuous drop-off behavior defined in [routes.txt](#routestxt). If this field is empty, the `stop_time` inherits any continuous drop-off behavior defined in [routes.txt](#routestxt).

**Conditionally Forbidden**:
- **Forbidden** if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.
- Optional otherwise. | | `shape_dist_traveled` | Non-negative float | Optional | Actual distance traveled along the associated shape, from the first stop to the stop specified in this record. This field specifies how much of the shape to draw between any two stops during a trip. Must be in the same units used in [shapes.txt](#shapestxt). Values used for `shape_dist_traveled` must increase along with `stop_sequence`; they must not be used to show reverse travel along a route.

Recommended for routes that have looping or inlining (the vehicle crosses or travels over the same portion of alignment in one trip). See [`shapes.shape_dist_traveled`](#shapestxt).
*Example: If a bus travels a distance of 5.25 kilometers from the start of the shape to the stop,`shape_dist_traveled`=`5.25`.*| +| `fare_units_traveled` | Non-negative float | Conditionally Required | Fare distance units traveled along the trip, from the first stop to the stop specified in this record. The fare units might be in the same units used in `stop_times.shape_dist_traveled` for fare structures that use an expression of the traveled distance. `fare_units_traveled` can also be equal to `stop_times.shape_dist_traveled`. Values used for `fare_units_traveled` must increase along with stop_sequence; they must not be used to show reverse travel along a route.

**Conditionally Required**
- Required if there is any `leg_group_id` in `fare_leg_rules.txt` that contains `fare_leg_rules.min_distance` or `fare_leg_rules.max_distance` and is associated with a `networks.network_id` or an `areas.area_id` that is covered in the `trips.trip_id` of this stop time.
- Forbidden otherwise.| | `timepoint` | Enum | Optional | Indicates if arrival and departure times for a stop are strictly adhered to by the vehicle or if they are instead approximate and/or interpolated times. This field allows a GTFS producer to provide interpolated stop-times, while indicating that the times are approximate. Valid options are:

`0` - Times are considered approximate.
`1` - Times are considered exact.

All records of [stop_times.txt](#stop_timestxt) with defined arrival or departure times should have timepoint values populated. If no timepoint values are provided, all times are considered exact. | | `pickup_booking_rule_id` | Foreign ID referencing `booking_rules.booking_rule_id` | Optional | Identifies the boarding booking rule at this stop time.

Recommended when `pickup_type=2`. | | `drop_off_booking_rule_id` | Foreign ID referencing `booking_rules.booking_rule_id` | Optional | Identifies the alighting booking rule at this stop time.

Recommended when `drop_off_type=2`. | @@ -507,6 +512,9 @@ To process the cost of a leg: | `to_area_id` | Foreign ID referencing `areas.area_id` | Optional | Identifies an arrival area.

If the `rule_priority` field does not exist AND there are no matching `fare_leg_rules.to_area_id` values to the `area_id` being filtered, empty `fare_leg_rules.to_area_id` will be matched by default.

An empty entry in `fare_leg_rules.to_area_id` corresponds to all areas defined in `areas.area_id` excluding the ones listed under `fare_leg_rules.to_area_id`

If the `rule_priority` field exists in the file, an empty `fare_leg_rules.to_area_id` indicates that the arrival area of the leg does not affect the matching of this rule.

When matching against an [effective fare leg of multiple legs](#fare_leg_join_rulestxt), the last leg of the effective fare leg is used for determining the arrival area. | | `from_timeframe_group_id` | Foreign ID referencing `timeframes.timeframe_group_id` | Optional | Defines the timeframe for the fare validation event at the start of the fare leg.

The “start time” of the fare leg is the time at which the event is scheduled to occur. For example, the time could be the scheduled departure time of a bus at the start of a fare leg where the rider boards and validates their fare. For the rule matching semantics below, the start time is computed in local time, as determined by [Local Time Semantics](#localtimesemantics) of [timeframes.txt](#timeframestxt). The stop or station of the fare leg’s departure event should be used for timezone resolution, where appropriate.

For a fare leg rule that specifies a `from_timeframe_group_id`, that rule will match a particular leg if there exists at least one record in [timeframes.txt](#timeframestxt) where all of the following conditions are true
- The value of `timeframe_group_id` is equal to the `from_timeframe_group_id` value.
- The set of days identified by the record’s `service_id` contains the “current day” of the fare leg’s start time.
- The “time-of-day” of the fare leg's start time is greater than or equal to the record’s `timeframes.start_time` value and less than the `timeframes.end_time` value.

An empty `fare_leg_rules.from_timeframe_group_id` indicates that the start time of the leg does not affect the matching of this rule.

When matching against an [effective fare leg of multiple legs](#fare_leg_join_rulestxt), the first leg of the effective fare leg is used for determining the starting fare validation event. | | `to_timeframe_group_id` | Foreign ID referencing `timeframes.timeframe_group_id` | Optional | Defines the timeframe for the fare validation event at the end of the fare leg.

The “end time” of the fare leg is the time at which the event is scheduled to occur. For example, the time could be the scheduled arrival time of a bus at the end of a fare leg where the rider gets off and validates their fare. For the rule matching semantics below, the end time is computed in local time, as determined by [Local Time Semantics](#localtimesemantics) of [timeframes.txt](#timeframestxt). The stop or station of the fare leg’s arrival event should be used for timezone resolution, where appropriate.

For a fare leg rule that specifies a `to_timeframe_group_id`, that rule will match a particular leg if there exists at least one record in [timeframes.txt](#timeframestxt) where all of the following conditions are true
- The value of `timeframe_group_id` is equal to the `to_timeframe_group_id` value.
- The set of days identified by the record’s `service_id` contains the “current day” of the fare leg’s end time.
- The “time-of-day” of the fare leg's end time is greater than or equal to the record’s `timeframes.start_time` value and less than the `timeframes.end_time` value.

An empty `fare_leg_rules.to_timeframe_group_id` indicates that the end time of the leg does not affect the matching of this rule.

When matching against an [effective fare leg of multiple legs](#fare_leg_join_rulestxt), the last leg of the effective fare leg is used for determining the ending fare validation event. | +| `min_distance` | Non-negative integer | Optional | Minimum travel distance required for the fare leg rule. `min_distance` is included in the lower bound of the interval.

If the lowest distance bound is zero, `fare_leg_rules.min_distance` can be empty. Otherwise, `fare_leg_rules.min_distance` must be less than `fare_leg_rules.max_distance`.| +| `max_distance` | Non-negative integer | Optional | Maximum travel distance required for the fare leg rule. `max_distance` is excluded from the upper bound of the interval.

If the upper distance bound is undefined (i.e., the conceptual upper limit), `fare_leg_rules.max_distance` can be empty. Otherwise, `fare_leg_rules.max_distance` must be greater than `fare_leg_rules.min_distance`.| +| `distance_type` | Enum | Conditionally Required | Indicates the type of distance for `fare_leg_rules.min_distance` and `fare_leg_rules.max_distance`. Valid options are:
`0` - Number of stops traveled on the leg between the origin stop and the destination stop. The number of stops calculated are equal to the number of links traveled between consecutive stop pairs.
`1` - Distance traveled on the leg between the origin stop and the destination stop. Units must be consistent with `stop_times.fare_units_traveled`.

**Conditionally Required**
- Required if either `fare_leg_rules.min_distance` or `fare_leg_rules.max_distance` are defined.
- Forbidden if both `fare_leg_rules.min_distance` and `fare_leg_rules.max_distance` are empty.| | `fare_product_id` | Foreign ID referencing `fare_products.fare_product_id` | **Required** | The fare product required to travel the leg. | | `rule_priority` | Non-negative integer | Optional | Defines the order of priority in which matching rules are applied to legs, allowing certain rules to take precedence over others. When multiple entries in `fare_leg_rules.txt` match, the rule or set of rules with the highest value for `rule_priority` will be selected.

An empty value for `rule_priority` is treated as zero. | @@ -528,6 +536,25 @@ For a sub-journey of two consecutive legs with a transfer, if the transfer match | `from_stop_id` | Foreign ID referencing `stops.stop_id`| **Conditionally Required** | Matches a pre-transfer leg that ends at the specified stop (`location_type=0` or empty) or station (`location_type=1`).

Conditionally Required:
- **Required** if `to_stop_id` is defined.
- Optional otherwise. | | `to_stop_id` | Foreign ID referencing `stops.stop_id`| **Conditionally Required** | Matches a post-transfer leg that starts at the specified stop (`location_type=0` or empty) or station (`location_type=1`).

Conditionally Required:
- **Required** if `from_stop_id` is defined.
- Optional otherwise. | +### fare_leg_distance_rules.txt + +File: **Conditionally Forbidden** + +Primary Key (`from_network_id, to_network_id`) + +For a sub-journey of two consecutive distance-based legs with a transfer, if the transfer matches all matching predicates specified by a particular record in the file, then those two distance-based legs should be considered as a single **effective distance-based fare leg** for the purposes of matching against rules in [`fare_leg_rules.txt`](#fare_leg_rulestxt). +- If a matching predicate field value is blank or unspecified for a particular record in the file, then that field should be ignored for the purposes of matching. +- When a sub-journey contains consecutive transfers that each match a join rule, then the entire sub-journey should be considered as a single **effective distance-based fare leg**. + +| Field Name | Type | Presence | Description | +| ------ | ------ | ------ | ------ | +| `from_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a pre-transfer distance-based leg that uses the specified route network. | +| `to_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a post-transfer distance-based leg that uses the specified route network. | +| `resulting_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | The resulting network of the effective leg. This is used for the purposes of matching against rules in [fare_leg_rules.txt](#fare_leg_rulestxt). | +| `duration_limit` | Positive integer | **Optional** | Defines the duration limit of the transfer between the distance-based legs.

Must be expressed in integer increments of seconds.

If there is no duration limit, `fare_leg_distance_rules.duration_limit` must be empty. | +| `duration_limit_type` | Enum | **Conditionally Required** | Defines the relative start and end of `fare_leg_distance_rules.duration_limit`.

Valid options are:
`0` - Between the departure fare validation of the current leg and the arrival fare validation of the next leg.
`1` - Between the departure fare validation of the current leg and the departure fare validation of the next leg.
`2` - Between the arrival fare validation of the current leg and the departure fare validation of the next leg.
`3` - Between the arrival fare validation of the current leg and the arrival fare validation of the next leg.

Conditionally Required:
- Required if `fare_leg_distance_rules.duration_limit` is defined.
- Forbidden if `fare_leg_distance_rules.duration_limit` is empty. | +| `distance_leg_join_type` | Enum| **Optional** | Defines the joining process of the distance-based legs.

Valid options are:
`0` - Join distance legs at once. The result of joining two distance legs is one effective distance-based fare leg whose distance is the sum of the distances of the sub-legs.

`1` - Join fare legs cumulatively. The result of joining two distance legs is two consecutive effective distance-based fare legs, the first effective leg is the first leg, the second effective leg is the effective distance-based fare leg whose distance is the sum of the distances of the sub-legs.

| + ### fare_transfer_rules.txt File: **Optional** From baff7e6e354a9ad0e9a728db9556f0a8971db8e0 Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Tue, 22 Apr 2025 08:54:06 -0400 Subject: [PATCH 02/18] add distance join illustrations --- gtfs/spec/en/distance_join_type_0.svg | 225 +++++++++++++++++++++ gtfs/spec/en/distance_join_type_1.svg | 271 ++++++++++++++++++++++++++ 2 files changed, 496 insertions(+) create mode 100644 gtfs/spec/en/distance_join_type_0.svg create mode 100644 gtfs/spec/en/distance_join_type_1.svg diff --git a/gtfs/spec/en/distance_join_type_0.svg b/gtfs/spec/en/distance_join_type_0.svg new file mode 100644 index 00000000..27151aeb --- /dev/null +++ b/gtfs/spec/en/distance_join_type_0.svg @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gtfs/spec/en/distance_join_type_1.svg b/gtfs/spec/en/distance_join_type_1.svg new file mode 100644 index 00000000..ab3d4967 --- /dev/null +++ b/gtfs/spec/en/distance_join_type_1.svg @@ -0,0 +1,271 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 23ceceb94fa61ccf99ff23f73068e8df0055357a Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Tue, 22 Apr 2025 08:55:28 -0400 Subject: [PATCH 03/18] Update reference.md --- gtfs/spec/en/reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index 8064153a..3ed7ba27 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -553,7 +553,7 @@ For a sub-journey of two consecutive distance-based legs with a transfer, if the | `resulting_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | The resulting network of the effective leg. This is used for the purposes of matching against rules in [fare_leg_rules.txt](#fare_leg_rulestxt). | | `duration_limit` | Positive integer | **Optional** | Defines the duration limit of the transfer between the distance-based legs.

Must be expressed in integer increments of seconds.

If there is no duration limit, `fare_leg_distance_rules.duration_limit` must be empty. | | `duration_limit_type` | Enum | **Conditionally Required** | Defines the relative start and end of `fare_leg_distance_rules.duration_limit`.

Valid options are:
`0` - Between the departure fare validation of the current leg and the arrival fare validation of the next leg.
`1` - Between the departure fare validation of the current leg and the departure fare validation of the next leg.
`2` - Between the arrival fare validation of the current leg and the departure fare validation of the next leg.
`3` - Between the arrival fare validation of the current leg and the arrival fare validation of the next leg.

Conditionally Required:
- Required if `fare_leg_distance_rules.duration_limit` is defined.
- Forbidden if `fare_leg_distance_rules.duration_limit` is empty. | -| `distance_leg_join_type` | Enum| **Optional** | Defines the joining process of the distance-based legs.

Valid options are:
`0` - Join distance legs at once. The result of joining two distance legs is one effective distance-based fare leg whose distance is the sum of the distances of the sub-legs.

`1` - Join fare legs cumulatively. The result of joining two distance legs is two consecutive effective distance-based fare legs, the first effective leg is the first leg, the second effective leg is the effective distance-based fare leg whose distance is the sum of the distances of the sub-legs.

| +| `distance_leg_join_type` | Enum| **Optional** | Defines the joining process of the distance-based legs.

Valid options are:
`0` - Join distance legs at once. The result of joining two distance legs is one effective distance-based fare leg whose distance is the sum of the distances of the sub-legs.

`1` - Join fare legs cumulatively. The result of joining two distance legs is two consecutive effective distance-based fare legs, the first effective leg is the first leg, the second effective leg is the effective distance-based fare leg whose distance is the sum of the distances of the sub-legs.

| ### fare_transfer_rules.txt From 6891378aa0b5623c00622479d4eec80e7d74c595 Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Tue, 22 Apr 2025 08:59:21 -0400 Subject: [PATCH 04/18] Add files via upload --- gtfs/spec/en/distance_join_type_0.svg | 226 +-------------------- gtfs/spec/en/distance_join_type_1.svg | 272 +------------------------- 2 files changed, 2 insertions(+), 496 deletions(-) diff --git a/gtfs/spec/en/distance_join_type_0.svg b/gtfs/spec/en/distance_join_type_0.svg index 27151aeb..7f769acd 100644 --- a/gtfs/spec/en/distance_join_type_0.svg +++ b/gtfs/spec/en/distance_join_type_0.svg @@ -1,225 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/gtfs/spec/en/distance_join_type_1.svg b/gtfs/spec/en/distance_join_type_1.svg index ab3d4967..7e3cd908 100644 --- a/gtfs/spec/en/distance_join_type_1.svg +++ b/gtfs/spec/en/distance_join_type_1.svg @@ -1,271 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file From 45461d32de8c775a740189c8de6bb9188534be4e Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Fri, 6 Jun 2025 09:06:55 +0000 Subject: [PATCH 05/18] simplify proposal --- gtfs/spec/en/reference.md | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index 3ed7ba27..d7ac53e2 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -26,7 +26,6 @@ This document defines the format and structure of the files that comprise a GTFS - [fare\_products.txt](#fare_productstxt) - [fare\_leg\_rules.txt](#fare_leg_rulestxt) - [fare_leg_join_rules.txt](#fare_leg_join_rulestxt) - - [fare\_leg\_distance\_rules.txt](#fare_leg_distance_rulestxt) - [fare\_transfer\_rules.txt](#fare_transfer_rulestxt) - [areas.txt](#areastxt) - [stop_areas.txt](#stop_areastxt) @@ -132,7 +131,6 @@ This specification defines the following files: | [fare_products.txt](#fare_productstxt) | Optional | To describe the different types of tickets or fares that can be purchased by riders.

File [fare_products.txt](#fare_productstxt) describes fare products that are not represented in [fare_attributes.txt](#fare_attributestxt) and [fare_rules.txt](#fare_rulestxt). As such, the use of [fare_products.txt](#fare_productstxt) is entirely separate from files [fare_attributes.txt](#fare_attributestxt) and [fare_rules.txt](#fare_rulestxt). | | [fare_leg_rules.txt](#fare_leg_rulestxt) | Optional | Fare rules for individual legs of travel.

File [fare_leg_rules.txt](#fare_leg_rulestxt) provides a more detailed method for modeling fare structures. As such, the use of [fare_leg_rules.txt](#fare_leg_rulestxt) is entirely separate from files [fare_attributes.txt](#fare_attributestxt) and [fare_rules.txt](#fare_rulestxt). | | [fare_leg_join_rules.txt](#fare_leg_join_rulestxt) | Optional | Rules for defining two or more legs should be considered as a single **effective fare leg** for the purposes of matching against rules in [fare_leg_rules.txt](#fare_leg_rulestxt)| -| [fare_leg_distance_rules.txt](#fare_leg_distance_rulestxt) | Conditionally Forbidden | Rules for defining two or more distance-based legs that should be considered as a single effective distance-based fare leg for the purposes of matching against rules in [fare_leg_rules.txt](#fare_leg_rulestxt)

Conditionally Forbidden:
- **Forbidden** if no distance-based leg exists in [fare_leg_rules.txt](#fare_leg_rulestxt).
- Optional otherwise. | | [fare_transfer_rules.txt](#fare_transfer_rulestxt) | Optional | Fare rules for transfers between legs of travel.

Along with [fare_leg_rules.txt](#fare_leg_rulestxt), file [fare_transfer_rules.txt](#fare_transfer_rulestxt) provides a more detailed method for modeling fare structures. As such, the use of [fare_transfer_rules.txt](#fare_transfer_rulestxt) is entirely separate from files [fare_attributes.txt](#fare_attributestxt) and [fare_rules.txt](#fare_rulestxt). | | [areas.txt](#areastxt) | Optional | Area grouping of locations. | | [stop_areas.txt](#stop_areastxt) | Optional | Rules to assign stops to areas. | @@ -535,25 +533,9 @@ For a sub-journey of two consecutive legs with a transfer, if the transfer match | `to_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a post-transfer leg that uses the specified route network. If specified, the same `from_network_id` must also be specified. | | `from_stop_id` | Foreign ID referencing `stops.stop_id`| **Conditionally Required** | Matches a pre-transfer leg that ends at the specified stop (`location_type=0` or empty) or station (`location_type=1`).

Conditionally Required:
- **Required** if `to_stop_id` is defined.
- Optional otherwise. | | `to_stop_id` | Foreign ID referencing `stops.stop_id`| **Conditionally Required** | Matches a post-transfer leg that starts at the specified stop (`location_type=0` or empty) or station (`location_type=1`).

Conditionally Required:
- **Required** if `from_stop_id` is defined.
- Optional otherwise. | - -### fare_leg_distance_rules.txt - -File: **Conditionally Forbidden** - -Primary Key (`from_network_id, to_network_id`) - -For a sub-journey of two consecutive distance-based legs with a transfer, if the transfer matches all matching predicates specified by a particular record in the file, then those two distance-based legs should be considered as a single **effective distance-based fare leg** for the purposes of matching against rules in [`fare_leg_rules.txt`](#fare_leg_rulestxt). -- If a matching predicate field value is blank or unspecified for a particular record in the file, then that field should be ignored for the purposes of matching. -- When a sub-journey contains consecutive transfers that each match a join rule, then the entire sub-journey should be considered as a single **effective distance-based fare leg**. - -| Field Name | Type | Presence | Description | -| ------ | ------ | ------ | ------ | -| `from_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a pre-transfer distance-based leg that uses the specified route network. | -| `to_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a post-transfer distance-based leg that uses the specified route network. | -| `resulting_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | The resulting network of the effective leg. This is used for the purposes of matching against rules in [fare_leg_rules.txt](#fare_leg_rulestxt). | | `duration_limit` | Positive integer | **Optional** | Defines the duration limit of the transfer between the distance-based legs.

Must be expressed in integer increments of seconds.

If there is no duration limit, `fare_leg_distance_rules.duration_limit` must be empty. | | `duration_limit_type` | Enum | **Conditionally Required** | Defines the relative start and end of `fare_leg_distance_rules.duration_limit`.

Valid options are:
`0` - Between the departure fare validation of the current leg and the arrival fare validation of the next leg.
`1` - Between the departure fare validation of the current leg and the departure fare validation of the next leg.
`2` - Between the arrival fare validation of the current leg and the departure fare validation of the next leg.
`3` - Between the arrival fare validation of the current leg and the arrival fare validation of the next leg.

Conditionally Required:
- Required if `fare_leg_distance_rules.duration_limit` is defined.
- Forbidden if `fare_leg_distance_rules.duration_limit` is empty. | -| `distance_leg_join_type` | Enum| **Optional** | Defines the joining process of the distance-based legs.

Valid options are:
`0` - Join distance legs at once. The result of joining two distance legs is one effective distance-based fare leg whose distance is the sum of the distances of the sub-legs.

`1` - Join fare legs cumulatively. The result of joining two distance legs is two consecutive effective distance-based fare legs, the first effective leg is the first leg, the second effective leg is the effective distance-based fare leg whose distance is the sum of the distances of the sub-legs.

| + ### fare_transfer_rules.txt From 9164bcda1f8cecad788847422db729fcda37ac3a Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Fri, 6 Jun 2025 09:11:50 +0000 Subject: [PATCH 06/18] remove files --- gtfs/spec/en/distance_join_type_0.svg | 1 - gtfs/spec/en/distance_join_type_1.svg | 1 - gtfs/spec/en/reference.md | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 gtfs/spec/en/distance_join_type_0.svg delete mode 100644 gtfs/spec/en/distance_join_type_1.svg diff --git a/gtfs/spec/en/distance_join_type_0.svg b/gtfs/spec/en/distance_join_type_0.svg deleted file mode 100644 index 7f769acd..00000000 --- a/gtfs/spec/en/distance_join_type_0.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/gtfs/spec/en/distance_join_type_1.svg b/gtfs/spec/en/distance_join_type_1.svg deleted file mode 100644 index 7e3cd908..00000000 --- a/gtfs/spec/en/distance_join_type_1.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index d7ac53e2..74a6e7d5 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -64,7 +64,6 @@ This section defines terms that are used throughout this document. * **Fare product** - Purchassable fare products that can be used to pay for or validate travel. * **Effective Fare Leg** - A sub-journey of two or more legs that should be treated as a single leg for matching rules in [fare_leg_rules.txt](#fare_leg_rulestxt) for the purposes of fare calculation. * **Distance-Based Leg** - A leg which is based on the distance (fare units) traveled. In [fare_leg_rules.txt](#fare_leg_rulestxt), a distance-based leg is a leg that has at least fare_leg_rules.min_distance or fare_leg_rules.max_distance assigned (not empty). -**Effective Distance-Based Fare Leg** - A sub-journey of two or more distance-based legs that should be treated as a single distance-based leg for matching rules in [fare_leg_rules.txt](#fare_leg_rulestxt) for the purposes of fare calculation for distance-based legs only. ### Presence @@ -526,6 +525,7 @@ For a sub-journey of two consecutive legs with a transfer, if the transfer match - Unless overridden explicitly by `from_stop_id` and `to_stop_id`, the last station of the pre-transfer leg and the first station of the post-transfer leg must be the same for the record. - If a matching predicate field value is blank or unspecified for a particular record in the file, then that field should be ignored for the purposes of matching. - When a sub-journey contains consecutive transfers that each match a join rule, then the entire sub-journey should be considered as a single **effective fare leg**. +- In the case of distance-based legs, the effective leg's total distance is the sum of the distances of its legs. | Field Name | Type | Presence | Description | | ------ | ------ | ------ | ------ | From 8673f3ead554721e5b1f8e71986892b1afe4975d Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Fri, 6 Jun 2025 09:13:20 +0000 Subject: [PATCH 07/18] clean duration-limit description --- gtfs/spec/en/reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index 74a6e7d5..288fde6e 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -533,7 +533,7 @@ For a sub-journey of two consecutive legs with a transfer, if the transfer match | `to_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a post-transfer leg that uses the specified route network. If specified, the same `from_network_id` must also be specified. | | `from_stop_id` | Foreign ID referencing `stops.stop_id`| **Conditionally Required** | Matches a pre-transfer leg that ends at the specified stop (`location_type=0` or empty) or station (`location_type=1`).

Conditionally Required:
- **Required** if `to_stop_id` is defined.
- Optional otherwise. | | `to_stop_id` | Foreign ID referencing `stops.stop_id`| **Conditionally Required** | Matches a post-transfer leg that starts at the specified stop (`location_type=0` or empty) or station (`location_type=1`).

Conditionally Required:
- **Required** if `from_stop_id` is defined.
- Optional otherwise. | -| `duration_limit` | Positive integer | **Optional** | Defines the duration limit of the transfer between the distance-based legs.

Must be expressed in integer increments of seconds.

If there is no duration limit, `fare_leg_distance_rules.duration_limit` must be empty. | +| `duration_limit` | Positive integer | **Optional** | Defines the duration limit of the transfer between the legs.

Must be expressed in integer increments of seconds.

If there is no duration limit, `fare_leg_distance_rules.duration_limit` must be empty. | | `duration_limit_type` | Enum | **Conditionally Required** | Defines the relative start and end of `fare_leg_distance_rules.duration_limit`.

Valid options are:
`0` - Between the departure fare validation of the current leg and the arrival fare validation of the next leg.
`1` - Between the departure fare validation of the current leg and the departure fare validation of the next leg.
`2` - Between the arrival fare validation of the current leg and the departure fare validation of the next leg.
`3` - Between the arrival fare validation of the current leg and the arrival fare validation of the next leg.

Conditionally Required:
- Required if `fare_leg_distance_rules.duration_limit` is defined.
- Forbidden if `fare_leg_distance_rules.duration_limit` is empty. | From c677847b020aa897a290f797fac78bce2c05867b Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Thu, 21 Aug 2025 14:47:39 -0400 Subject: [PATCH 08/18] Add restriction over joining distance-based and non-distance-based legs --- gtfs/spec/en/reference.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index 288fde6e..d7218df0 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -529,8 +529,8 @@ For a sub-journey of two consecutive legs with a transfer, if the transfer match | Field Name | Type | Presence | Description | | ------ | ------ | ------ | ------ | -| `from_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a pre-transfer leg that uses the specified route network. If specified, the same `to_network_id` must also be specified. | -| `to_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a post-transfer leg that uses the specified route network. If specified, the same `from_network_id` must also be specified. | +| `from_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a pre-transfer leg that uses the specified route network. If specified, the same `to_network_id` must also be specified. If `to_network_id` is associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`, then `from_network_id` needs to be associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`. (Cannot join a distance-based leg to a leg that is not distance-based).| +| `to_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a post-transfer leg that uses the specified route network. If specified, the same `from_network_id` must also be specified. If `from_network_id` is associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`, then `to_network_id` needs to be associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`. (Cannot join a distance-based leg to a leg that is not distance-based).| | `from_stop_id` | Foreign ID referencing `stops.stop_id`| **Conditionally Required** | Matches a pre-transfer leg that ends at the specified stop (`location_type=0` or empty) or station (`location_type=1`).

Conditionally Required:
- **Required** if `to_stop_id` is defined.
- Optional otherwise. | | `to_stop_id` | Foreign ID referencing `stops.stop_id`| **Conditionally Required** | Matches a post-transfer leg that starts at the specified stop (`location_type=0` or empty) or station (`location_type=1`).

Conditionally Required:
- **Required** if `from_stop_id` is defined.
- Optional otherwise. | | `duration_limit` | Positive integer | **Optional** | Defines the duration limit of the transfer between the legs.

Must be expressed in integer increments of seconds.

If there is no duration limit, `fare_leg_distance_rules.duration_limit` must be empty. | From 1aab2ce71484e1512f7805a044fce66c803811e2 Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Thu, 21 Aug 2025 14:56:40 -0400 Subject: [PATCH 09/18] fix bad description and typo --- gtfs/spec/en/reference.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index d7218df0..e3593a60 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -305,7 +305,7 @@ Primary key (`trip_id`, `stop_sequence`) | `continuous_pickup` | Enum | **Conditionally Forbidden** | Indicates that the rider can board the transit vehicle at any point along the vehicle’s travel path as described by [shapes.txt](#shapestxt), from this `stop_time` to the next `stop_time` in the trip’s `stop_sequence`. Valid options are:

`0` - Continuous stopping pickup.
`1` or empty - No continuous stopping pickup.
`2` - Must phone agency to arrange continuous stopping pickup.
`3` - Must coordinate with driver to arrange continuous stopping pickup.

If this field is populated, it overrides any continuous pickup behavior defined in [routes.txt](#routestxt). If this field is empty, the `stop_time` inherits any continuous pickup behavior defined in [routes.txt](#routestxt).

**Conditionally Forbidden**:
- **Forbidden** if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.
- Optional otherwise. | | `continuous_drop_off` | Enum | **Conditionally Forbidden** | Indicates that the rider can alight from the transit vehicle at any point along the vehicle’s travel path as described by [shapes.txt](#shapestxt), from this `stop_time` to the next `stop_time` in the trip’s `stop_sequence`. Valid options are:

`0` - Continuous stopping drop off.
`1` or empty - No continuous stopping drop off.
`2` - Must phone agency to arrange continuous stopping drop off.
`3` - Must coordinate with driver to arrange continuous stopping drop off.

If this field is populated, it overrides any continuous drop-off behavior defined in [routes.txt](#routestxt). If this field is empty, the `stop_time` inherits any continuous drop-off behavior defined in [routes.txt](#routestxt).

**Conditionally Forbidden**:
- **Forbidden** if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.
- Optional otherwise. | | `shape_dist_traveled` | Non-negative float | Optional | Actual distance traveled along the associated shape, from the first stop to the stop specified in this record. This field specifies how much of the shape to draw between any two stops during a trip. Must be in the same units used in [shapes.txt](#shapestxt). Values used for `shape_dist_traveled` must increase along with `stop_sequence`; they must not be used to show reverse travel along a route.

Recommended for routes that have looping or inlining (the vehicle crosses or travels over the same portion of alignment in one trip). See [`shapes.shape_dist_traveled`](#shapestxt).
*Example: If a bus travels a distance of 5.25 kilometers from the start of the shape to the stop,`shape_dist_traveled`=`5.25`.*| -| `fare_units_traveled` | Non-negative float | Conditionally Required | Fare distance units traveled along the trip, from the first stop to the stop specified in this record. The fare units might be in the same units used in `stop_times.shape_dist_traveled` for fare structures that use an expression of the traveled distance. `fare_units_traveled` can also be equal to `stop_times.shape_dist_traveled`. Values used for `fare_units_traveled` must increase along with stop_sequence; they must not be used to show reverse travel along a route.

**Conditionally Required**
- Required if there is any `leg_group_id` in `fare_leg_rules.txt` that contains `fare_leg_rules.min_distance` or `fare_leg_rules.max_distance` and is associated with a `networks.network_id` or an `areas.area_id` that is covered in the `trips.trip_id` of this stop time.
- Forbidden otherwise.| +| `fare_units_traveled` | Non-negative float | Conditionally Required | Fare distance units traveled along the trip, from the first stop to the stop specified in this record. The fare units might be in the same units used in `stop_times.shape_dist_traveled` for fare structures that use an expression of the traveled distance. `fare_units_traveled` can also be equal to `stop_times.shape_dist_traveled`. Values used for `fare_units_traveled` must increase along with stop_sequence; they must not be used to show reverse travel along a route.

**Conditionally Required**
- Required if the `route_id` of the `trip_id` of this stop time is part of a `network_id` which is associated to a distance-based `leg_group_id` in `fare_leg_rules.txt` whose `network_id`.
- Forbidden otherwise.| | `timepoint` | Enum | Optional | Indicates if arrival and departure times for a stop are strictly adhered to by the vehicle or if they are instead approximate and/or interpolated times. This field allows a GTFS producer to provide interpolated stop-times, while indicating that the times are approximate. Valid options are:

`0` - Times are considered approximate.
`1` - Times are considered exact.

All records of [stop_times.txt](#stop_timestxt) with defined arrival or departure times should have timepoint values populated. If no timepoint values are provided, all times are considered exact. | | `pickup_booking_rule_id` | Foreign ID referencing `booking_rules.booking_rule_id` | Optional | Identifies the boarding booking rule at this stop time.

Recommended when `pickup_type=2`. | | `drop_off_booking_rule_id` | Foreign ID referencing `booking_rules.booking_rule_id` | Optional | Identifies the alighting booking rule at this stop time.

Recommended when `drop_off_type=2`. | @@ -529,12 +529,12 @@ For a sub-journey of two consecutive legs with a transfer, if the transfer match | Field Name | Type | Presence | Description | | ------ | ------ | ------ | ------ | -| `from_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a pre-transfer leg that uses the specified route network. If specified, the same `to_network_id` must also be specified. If `to_network_id` is associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`, then `from_network_id` needs to be associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`. (Cannot join a distance-based leg to a leg that is not distance-based).| -| `to_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a post-transfer leg that uses the specified route network. If specified, the same `from_network_id` must also be specified. If `from_network_id` is associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`, then `to_network_id` needs to be associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`. (Cannot join a distance-based leg to a leg that is not distance-based).| +| `from_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a pre-transfer leg that uses the specified route network. If specified, the same `to_network_id` must also be specified.

If `to_network_id` is associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`, then `from_network_id` needs to be associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`. (Cannot join a distance-based leg to a leg that is not distance-based).| +| `to_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a post-transfer leg that uses the specified route network. If specified, the same `from_network_id` must also be specified.

If `from_network_id` is associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`, then `to_network_id` needs to be associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`. (Cannot join a distance-based leg to a leg that is not distance-based).| | `from_stop_id` | Foreign ID referencing `stops.stop_id`| **Conditionally Required** | Matches a pre-transfer leg that ends at the specified stop (`location_type=0` or empty) or station (`location_type=1`).

Conditionally Required:
- **Required** if `to_stop_id` is defined.
- Optional otherwise. | | `to_stop_id` | Foreign ID referencing `stops.stop_id`| **Conditionally Required** | Matches a post-transfer leg that starts at the specified stop (`location_type=0` or empty) or station (`location_type=1`).

Conditionally Required:
- **Required** if `from_stop_id` is defined.
- Optional otherwise. | -| `duration_limit` | Positive integer | **Optional** | Defines the duration limit of the transfer between the legs.

Must be expressed in integer increments of seconds.

If there is no duration limit, `fare_leg_distance_rules.duration_limit` must be empty. | -| `duration_limit_type` | Enum | **Conditionally Required** | Defines the relative start and end of `fare_leg_distance_rules.duration_limit`.

Valid options are:
`0` - Between the departure fare validation of the current leg and the arrival fare validation of the next leg.
`1` - Between the departure fare validation of the current leg and the departure fare validation of the next leg.
`2` - Between the arrival fare validation of the current leg and the departure fare validation of the next leg.
`3` - Between the arrival fare validation of the current leg and the arrival fare validation of the next leg.

Conditionally Required:
- Required if `fare_leg_distance_rules.duration_limit` is defined.
- Forbidden if `fare_leg_distance_rules.duration_limit` is empty. | +| `duration_limit` | Positive integer | **Optional** | Defines the duration limit of the transfer between the legs.

Must be expressed in integer increments of seconds.

If there is no duration limit, `fare_leg_join_rules.duration_limit` must be empty. | +| `duration_limit_type` | Enum | **Conditionally Required** | Defines the relative start and end of `fare_leg_join_rules.duration_limit`.

Valid options are:
`0` - Between the departure fare validation of the current leg and the arrival fare validation of the next leg.
`1` - Between the departure fare validation of the current leg and the departure fare validation of the next leg.
`2` - Between the arrival fare validation of the current leg and the departure fare validation of the next leg.
`3` - Between the arrival fare validation of the current leg and the arrival fare validation of the next leg.

Conditionally Required:
- Required if `fare_leg_join_rules.duration_limit` is defined.
- Forbidden if `fare_leg_join_rules.duration_limit` is empty. | ### fare_transfer_rules.txt From c81c96389249f5a19ddc9d55d621e73e1dca5c47 Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Tue, 26 Aug 2025 08:40:20 -0400 Subject: [PATCH 10/18] Add "actual road or track distance" to the definition of distance_type --- gtfs/spec/en/reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index e3593a60..d4e1315d 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -511,7 +511,7 @@ To process the cost of a leg: | `to_timeframe_group_id` | Foreign ID referencing `timeframes.timeframe_group_id` | Optional | Defines the timeframe for the fare validation event at the end of the fare leg.

The “end time” of the fare leg is the time at which the event is scheduled to occur. For example, the time could be the scheduled arrival time of a bus at the end of a fare leg where the rider gets off and validates their fare. For the rule matching semantics below, the end time is computed in local time, as determined by [Local Time Semantics](#localtimesemantics) of [timeframes.txt](#timeframestxt). The stop or station of the fare leg’s arrival event should be used for timezone resolution, where appropriate.

For a fare leg rule that specifies a `to_timeframe_group_id`, that rule will match a particular leg if there exists at least one record in [timeframes.txt](#timeframestxt) where all of the following conditions are true
- The value of `timeframe_group_id` is equal to the `to_timeframe_group_id` value.
- The set of days identified by the record’s `service_id` contains the “current day” of the fare leg’s end time.
- The “time-of-day” of the fare leg's end time is greater than or equal to the record’s `timeframes.start_time` value and less than the `timeframes.end_time` value.

An empty `fare_leg_rules.to_timeframe_group_id` indicates that the end time of the leg does not affect the matching of this rule.

When matching against an [effective fare leg of multiple legs](#fare_leg_join_rulestxt), the last leg of the effective fare leg is used for determining the ending fare validation event. | | `min_distance` | Non-negative integer | Optional | Minimum travel distance required for the fare leg rule. `min_distance` is included in the lower bound of the interval.

If the lowest distance bound is zero, `fare_leg_rules.min_distance` can be empty. Otherwise, `fare_leg_rules.min_distance` must be less than `fare_leg_rules.max_distance`.| | `max_distance` | Non-negative integer | Optional | Maximum travel distance required for the fare leg rule. `max_distance` is excluded from the upper bound of the interval.

If the upper distance bound is undefined (i.e., the conceptual upper limit), `fare_leg_rules.max_distance` can be empty. Otherwise, `fare_leg_rules.max_distance` must be greater than `fare_leg_rules.min_distance`.| -| `distance_type` | Enum | Conditionally Required | Indicates the type of distance for `fare_leg_rules.min_distance` and `fare_leg_rules.max_distance`. Valid options are:
`0` - Number of stops traveled on the leg between the origin stop and the destination stop. The number of stops calculated are equal to the number of links traveled between consecutive stop pairs.
`1` - Distance traveled on the leg between the origin stop and the destination stop. Units must be consistent with `stop_times.fare_units_traveled`.

**Conditionally Required**
- Required if either `fare_leg_rules.min_distance` or `fare_leg_rules.max_distance` are defined.
- Forbidden if both `fare_leg_rules.min_distance` and `fare_leg_rules.max_distance` are empty.| +| `distance_type` | Enum | Conditionally Required | Indicates the type of distance for `fare_leg_rules.min_distance` and `fare_leg_rules.max_distance`. Valid options are:
`0` - Number of stops traveled on the leg between the origin stop and the destination stop. The number of stops calculated are equal to the number of links traveled between consecutive stop pairs.
`1` - Actual road or track distance traveled on the leg between the origin stop and the destination stop. Units must be consistent with `stop_times.fare_units_traveled`.

**Conditionally Required**
- Required if either `fare_leg_rules.min_distance` or `fare_leg_rules.max_distance` are defined.
- Forbidden if both `fare_leg_rules.min_distance` and `fare_leg_rules.max_distance` are empty.| | `fare_product_id` | Foreign ID referencing `fare_products.fare_product_id` | **Required** | The fare product required to travel the leg. | | `rule_priority` | Non-negative integer | Optional | Defines the order of priority in which matching rules are applied to legs, allowing certain rules to take precedence over others. When multiple entries in `fare_leg_rules.txt` match, the rule or set of rules with the highest value for `rule_priority` will be selected.

An empty value for `rule_priority` is treated as zero. | From 123a2907b5943605cb7dce41f4691b08ce3b8d44 Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Tue, 16 Sep 2025 18:16:54 -0400 Subject: [PATCH 11/18] add min_distance, max_distance to PK of fare_leg_rules.txt --- gtfs/spec/en/reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index d4e1315d..0bacef0c 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -465,7 +465,7 @@ Used to describe the range of fares available for purchase by riders or taken in File: **Optional** -Primary key (`network_id, from_area_id, to_area_id, from_timeframe_group_id, to_timeframe_group_id, fare_product_id`) +Primary key (`network_id, from_area_id, to_area_id, from_timeframe_group_id, to_timeframe_group_id, min_distance, max_distance, fare_product_id`) Fare rules for individual legs of travel. From 58439bbd021665b0887bdb5fa77754b8b38a443a Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Thu, 23 Oct 2025 10:55:58 -0400 Subject: [PATCH 12/18] Change min_distance and max_distance to float type Updated distance fields to use float instead of integer. --- gtfs/spec/en/reference.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index 0bacef0c..93f66730 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -509,8 +509,8 @@ To process the cost of a leg: | `to_area_id` | Foreign ID referencing `areas.area_id` | Optional | Identifies an arrival area.

If the `rule_priority` field does not exist AND there are no matching `fare_leg_rules.to_area_id` values to the `area_id` being filtered, empty `fare_leg_rules.to_area_id` will be matched by default.

An empty entry in `fare_leg_rules.to_area_id` corresponds to all areas defined in `areas.area_id` excluding the ones listed under `fare_leg_rules.to_area_id`

If the `rule_priority` field exists in the file, an empty `fare_leg_rules.to_area_id` indicates that the arrival area of the leg does not affect the matching of this rule.

When matching against an [effective fare leg of multiple legs](#fare_leg_join_rulestxt), the last leg of the effective fare leg is used for determining the arrival area. | | `from_timeframe_group_id` | Foreign ID referencing `timeframes.timeframe_group_id` | Optional | Defines the timeframe for the fare validation event at the start of the fare leg.

The “start time” of the fare leg is the time at which the event is scheduled to occur. For example, the time could be the scheduled departure time of a bus at the start of a fare leg where the rider boards and validates their fare. For the rule matching semantics below, the start time is computed in local time, as determined by [Local Time Semantics](#localtimesemantics) of [timeframes.txt](#timeframestxt). The stop or station of the fare leg’s departure event should be used for timezone resolution, where appropriate.

For a fare leg rule that specifies a `from_timeframe_group_id`, that rule will match a particular leg if there exists at least one record in [timeframes.txt](#timeframestxt) where all of the following conditions are true
- The value of `timeframe_group_id` is equal to the `from_timeframe_group_id` value.
- The set of days identified by the record’s `service_id` contains the “current day” of the fare leg’s start time.
- The “time-of-day” of the fare leg's start time is greater than or equal to the record’s `timeframes.start_time` value and less than the `timeframes.end_time` value.

An empty `fare_leg_rules.from_timeframe_group_id` indicates that the start time of the leg does not affect the matching of this rule.

When matching against an [effective fare leg of multiple legs](#fare_leg_join_rulestxt), the first leg of the effective fare leg is used for determining the starting fare validation event. | | `to_timeframe_group_id` | Foreign ID referencing `timeframes.timeframe_group_id` | Optional | Defines the timeframe for the fare validation event at the end of the fare leg.

The “end time” of the fare leg is the time at which the event is scheduled to occur. For example, the time could be the scheduled arrival time of a bus at the end of a fare leg where the rider gets off and validates their fare. For the rule matching semantics below, the end time is computed in local time, as determined by [Local Time Semantics](#localtimesemantics) of [timeframes.txt](#timeframestxt). The stop or station of the fare leg’s arrival event should be used for timezone resolution, where appropriate.

For a fare leg rule that specifies a `to_timeframe_group_id`, that rule will match a particular leg if there exists at least one record in [timeframes.txt](#timeframestxt) where all of the following conditions are true
- The value of `timeframe_group_id` is equal to the `to_timeframe_group_id` value.
- The set of days identified by the record’s `service_id` contains the “current day” of the fare leg’s end time.
- The “time-of-day” of the fare leg's end time is greater than or equal to the record’s `timeframes.start_time` value and less than the `timeframes.end_time` value.

An empty `fare_leg_rules.to_timeframe_group_id` indicates that the end time of the leg does not affect the matching of this rule.

When matching against an [effective fare leg of multiple legs](#fare_leg_join_rulestxt), the last leg of the effective fare leg is used for determining the ending fare validation event. | -| `min_distance` | Non-negative integer | Optional | Minimum travel distance required for the fare leg rule. `min_distance` is included in the lower bound of the interval.

If the lowest distance bound is zero, `fare_leg_rules.min_distance` can be empty. Otherwise, `fare_leg_rules.min_distance` must be less than `fare_leg_rules.max_distance`.| -| `max_distance` | Non-negative integer | Optional | Maximum travel distance required for the fare leg rule. `max_distance` is excluded from the upper bound of the interval.

If the upper distance bound is undefined (i.e., the conceptual upper limit), `fare_leg_rules.max_distance` can be empty. Otherwise, `fare_leg_rules.max_distance` must be greater than `fare_leg_rules.min_distance`.| +| `min_distance` | Non-negative float | Optional | Minimum travel distance required for the fare leg rule. `min_distance` is included in the lower bound of the interval.

If the lowest distance bound is zero, `fare_leg_rules.min_distance` can be empty. Otherwise, `fare_leg_rules.min_distance` must be less than `fare_leg_rules.max_distance`.| +| `max_distance` | Non-negative float | Optional | Maximum travel distance required for the fare leg rule. `max_distance` is excluded from the upper bound of the interval.

If the upper distance bound is undefined (i.e., the conceptual upper limit), `fare_leg_rules.max_distance` can be empty. Otherwise, `fare_leg_rules.max_distance` must be greater than `fare_leg_rules.min_distance`.| | `distance_type` | Enum | Conditionally Required | Indicates the type of distance for `fare_leg_rules.min_distance` and `fare_leg_rules.max_distance`. Valid options are:
`0` - Number of stops traveled on the leg between the origin stop and the destination stop. The number of stops calculated are equal to the number of links traveled between consecutive stop pairs.
`1` - Actual road or track distance traveled on the leg between the origin stop and the destination stop. Units must be consistent with `stop_times.fare_units_traveled`.

**Conditionally Required**
- Required if either `fare_leg_rules.min_distance` or `fare_leg_rules.max_distance` are defined.
- Forbidden if both `fare_leg_rules.min_distance` and `fare_leg_rules.max_distance` are empty.| | `fare_product_id` | Foreign ID referencing `fare_products.fare_product_id` | **Required** | The fare product required to travel the leg. | | `rule_priority` | Non-negative integer | Optional | Defines the order of priority in which matching rules are applied to legs, allowing certain rules to take precedence over others. When multiple entries in `fare_leg_rules.txt` match, the rule or set of rules with the highest value for `rule_priority` will be selected.

An empty value for `rule_priority` is treated as zero. | From 2d5b7acff117be26d52b8653752f304035246052 Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Fri, 7 Nov 2025 11:49:57 -0500 Subject: [PATCH 13/18] Address Working Group comments --- gtfs/spec/en/reference.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index 93f66730..2f756367 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -63,7 +63,8 @@ This section defines terms that are used throughout this document. * **Sub-journey** - Two or more legs that comprise a subset of a journey. * **Fare product** - Purchassable fare products that can be used to pay for or validate travel. * **Effective Fare Leg** - A sub-journey of two or more legs that should be treated as a single leg for matching rules in [fare_leg_rules.txt](#fare_leg_rulestxt) for the purposes of fare calculation. -* **Distance-Based Leg** - A leg which is based on the distance (fare units) traveled. In [fare_leg_rules.txt](#fare_leg_rulestxt), a distance-based leg is a leg that has at least fare_leg_rules.min_distance or fare_leg_rules.max_distance assigned (not empty). +* **Distance Unit** (or Fare distance unit) - Any unit used to calculate the fare for distance-based fare structures. The unit can be a distance measure (represented in meters, km, miles, etc) or any relevant incremental unit (e.g. number of stops crossed). The distance unit does not necessarily correspond to a physical measure of distance. It may also be adjusted by adding or subtracting arbitrary values to represent fare discounts or supplements. +* **Distance-Based Leg** - A leg whose fare which is based on the distance units traveled. In [fare_leg_rules.txt](#fare_leg_rulestxt), a distance-based leg is a leg that has at least fare_leg_rules.min_distance_units or fare_leg_rules.max_distance_units assigned (not empty). ### Presence @@ -305,7 +306,8 @@ Primary key (`trip_id`, `stop_sequence`) | `continuous_pickup` | Enum | **Conditionally Forbidden** | Indicates that the rider can board the transit vehicle at any point along the vehicle’s travel path as described by [shapes.txt](#shapestxt), from this `stop_time` to the next `stop_time` in the trip’s `stop_sequence`. Valid options are:

`0` - Continuous stopping pickup.
`1` or empty - No continuous stopping pickup.
`2` - Must phone agency to arrange continuous stopping pickup.
`3` - Must coordinate with driver to arrange continuous stopping pickup.

If this field is populated, it overrides any continuous pickup behavior defined in [routes.txt](#routestxt). If this field is empty, the `stop_time` inherits any continuous pickup behavior defined in [routes.txt](#routestxt).

**Conditionally Forbidden**:
- **Forbidden** if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.
- Optional otherwise. | | `continuous_drop_off` | Enum | **Conditionally Forbidden** | Indicates that the rider can alight from the transit vehicle at any point along the vehicle’s travel path as described by [shapes.txt](#shapestxt), from this `stop_time` to the next `stop_time` in the trip’s `stop_sequence`. Valid options are:

`0` - Continuous stopping drop off.
`1` or empty - No continuous stopping drop off.
`2` - Must phone agency to arrange continuous stopping drop off.
`3` - Must coordinate with driver to arrange continuous stopping drop off.

If this field is populated, it overrides any continuous drop-off behavior defined in [routes.txt](#routestxt). If this field is empty, the `stop_time` inherits any continuous drop-off behavior defined in [routes.txt](#routestxt).

**Conditionally Forbidden**:
- **Forbidden** if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.
- Optional otherwise. | | `shape_dist_traveled` | Non-negative float | Optional | Actual distance traveled along the associated shape, from the first stop to the stop specified in this record. This field specifies how much of the shape to draw between any two stops during a trip. Must be in the same units used in [shapes.txt](#shapestxt). Values used for `shape_dist_traveled` must increase along with `stop_sequence`; they must not be used to show reverse travel along a route.

Recommended for routes that have looping or inlining (the vehicle crosses or travels over the same portion of alignment in one trip). See [`shapes.shape_dist_traveled`](#shapestxt).
*Example: If a bus travels a distance of 5.25 kilometers from the start of the shape to the stop,`shape_dist_traveled`=`5.25`.*| -| `fare_units_traveled` | Non-negative float | Conditionally Required | Fare distance units traveled along the trip, from the first stop to the stop specified in this record. The fare units might be in the same units used in `stop_times.shape_dist_traveled` for fare structures that use an expression of the traveled distance. `fare_units_traveled` can also be equal to `stop_times.shape_dist_traveled`. Values used for `fare_units_traveled` must increase along with stop_sequence; they must not be used to show reverse travel along a route.

**Conditionally Required**
- Required if the `route_id` of the `trip_id` of this stop time is part of a `network_id` which is associated to a distance-based `leg_group_id` in `fare_leg_rules.txt` whose `network_id`.
- Forbidden otherwise.| +| `fare_distance_units_traveled` | Non-negative integer | Conditionally Required | Fare distance units traveled along the trip, from the first stop to the stop specified in this record. `fare_distance_units_traveled` does not have to be in the same unit as `stop_times.shape_dist_traveled`.

If the fare is calculated based on the distance covered by the route shape, `fare_distance_units_traveled` may correspond to `stop_times.shape_dist_traveled` (measured in meters) or to its rounded value (floor or ceiling), depending on the fare structure. If the fare is calculated based on stops, `fare_distance_units_traveled` may represent the number of stops crossed. `fare_distance_units_traveled` may also be adjusted by adding or subtracting arbitrary values to represent fare discounts or supplements. +

**Conditionally Required**
- Required if the `route_id` of the `trip_id` of this stop time is part of a `network_id` which is associated to a distance-based `leg_group_id` in `fare_leg_rules.txt`.
- Forbidden otherwise.| | `timepoint` | Enum | Optional | Indicates if arrival and departure times for a stop are strictly adhered to by the vehicle or if they are instead approximate and/or interpolated times. This field allows a GTFS producer to provide interpolated stop-times, while indicating that the times are approximate. Valid options are:

`0` - Times are considered approximate.
`1` - Times are considered exact.

All records of [stop_times.txt](#stop_timestxt) with defined arrival or departure times should have timepoint values populated. If no timepoint values are provided, all times are considered exact. | | `pickup_booking_rule_id` | Foreign ID referencing `booking_rules.booking_rule_id` | Optional | Identifies the boarding booking rule at this stop time.

Recommended when `pickup_type=2`. | | `drop_off_booking_rule_id` | Foreign ID referencing `booking_rules.booking_rule_id` | Optional | Identifies the alighting booking rule at this stop time.

Recommended when `drop_off_type=2`. | @@ -465,7 +467,7 @@ Used to describe the range of fares available for purchase by riders or taken in File: **Optional** -Primary key (`network_id, from_area_id, to_area_id, from_timeframe_group_id, to_timeframe_group_id, min_distance, max_distance, fare_product_id`) +Primary key (`network_id, from_area_id, to_area_id, from_timeframe_group_id, to_timeframe_group_id, min_distance_units, max_distance_units, fare_product_id`) Fare rules for individual legs of travel. @@ -479,6 +481,8 @@ To process the cost of a leg: - `fare_leg_rules.to_area_id` - `fare_leg_rules.from_timeframe_group_id` - `fare_leg_rules.to_timeframe_group_id` + - `fare_leg_rules.min_distance_units` + - `fare_leg_rules.max_distance_units`
2. If the leg exactly matches a record in [fare_leg_rules.txt](#fare_leg_rulestxt) based on the characteristics of travel, that record must be processed to determine the cost of the leg. This file handles empty entries in two ways: empty semantics OR rule_priority. @@ -509,9 +513,8 @@ To process the cost of a leg: | `to_area_id` | Foreign ID referencing `areas.area_id` | Optional | Identifies an arrival area.

If the `rule_priority` field does not exist AND there are no matching `fare_leg_rules.to_area_id` values to the `area_id` being filtered, empty `fare_leg_rules.to_area_id` will be matched by default.

An empty entry in `fare_leg_rules.to_area_id` corresponds to all areas defined in `areas.area_id` excluding the ones listed under `fare_leg_rules.to_area_id`

If the `rule_priority` field exists in the file, an empty `fare_leg_rules.to_area_id` indicates that the arrival area of the leg does not affect the matching of this rule.

When matching against an [effective fare leg of multiple legs](#fare_leg_join_rulestxt), the last leg of the effective fare leg is used for determining the arrival area. | | `from_timeframe_group_id` | Foreign ID referencing `timeframes.timeframe_group_id` | Optional | Defines the timeframe for the fare validation event at the start of the fare leg.

The “start time” of the fare leg is the time at which the event is scheduled to occur. For example, the time could be the scheduled departure time of a bus at the start of a fare leg where the rider boards and validates their fare. For the rule matching semantics below, the start time is computed in local time, as determined by [Local Time Semantics](#localtimesemantics) of [timeframes.txt](#timeframestxt). The stop or station of the fare leg’s departure event should be used for timezone resolution, where appropriate.

For a fare leg rule that specifies a `from_timeframe_group_id`, that rule will match a particular leg if there exists at least one record in [timeframes.txt](#timeframestxt) where all of the following conditions are true
- The value of `timeframe_group_id` is equal to the `from_timeframe_group_id` value.
- The set of days identified by the record’s `service_id` contains the “current day” of the fare leg’s start time.
- The “time-of-day” of the fare leg's start time is greater than or equal to the record’s `timeframes.start_time` value and less than the `timeframes.end_time` value.

An empty `fare_leg_rules.from_timeframe_group_id` indicates that the start time of the leg does not affect the matching of this rule.

When matching against an [effective fare leg of multiple legs](#fare_leg_join_rulestxt), the first leg of the effective fare leg is used for determining the starting fare validation event. | | `to_timeframe_group_id` | Foreign ID referencing `timeframes.timeframe_group_id` | Optional | Defines the timeframe for the fare validation event at the end of the fare leg.

The “end time” of the fare leg is the time at which the event is scheduled to occur. For example, the time could be the scheduled arrival time of a bus at the end of a fare leg where the rider gets off and validates their fare. For the rule matching semantics below, the end time is computed in local time, as determined by [Local Time Semantics](#localtimesemantics) of [timeframes.txt](#timeframestxt). The stop or station of the fare leg’s arrival event should be used for timezone resolution, where appropriate.

For a fare leg rule that specifies a `to_timeframe_group_id`, that rule will match a particular leg if there exists at least one record in [timeframes.txt](#timeframestxt) where all of the following conditions are true
- The value of `timeframe_group_id` is equal to the `to_timeframe_group_id` value.
- The set of days identified by the record’s `service_id` contains the “current day” of the fare leg’s end time.
- The “time-of-day” of the fare leg's end time is greater than or equal to the record’s `timeframes.start_time` value and less than the `timeframes.end_time` value.

An empty `fare_leg_rules.to_timeframe_group_id` indicates that the end time of the leg does not affect the matching of this rule.

When matching against an [effective fare leg of multiple legs](#fare_leg_join_rulestxt), the last leg of the effective fare leg is used for determining the ending fare validation event. | -| `min_distance` | Non-negative float | Optional | Minimum travel distance required for the fare leg rule. `min_distance` is included in the lower bound of the interval.

If the lowest distance bound is zero, `fare_leg_rules.min_distance` can be empty. Otherwise, `fare_leg_rules.min_distance` must be less than `fare_leg_rules.max_distance`.| -| `max_distance` | Non-negative float | Optional | Maximum travel distance required for the fare leg rule. `max_distance` is excluded from the upper bound of the interval.

If the upper distance bound is undefined (i.e., the conceptual upper limit), `fare_leg_rules.max_distance` can be empty. Otherwise, `fare_leg_rules.max_distance` must be greater than `fare_leg_rules.min_distance`.| -| `distance_type` | Enum | Conditionally Required | Indicates the type of distance for `fare_leg_rules.min_distance` and `fare_leg_rules.max_distance`. Valid options are:
`0` - Number of stops traveled on the leg between the origin stop and the destination stop. The number of stops calculated are equal to the number of links traveled between consecutive stop pairs.
`1` - Actual road or track distance traveled on the leg between the origin stop and the destination stop. Units must be consistent with `stop_times.fare_units_traveled`.

**Conditionally Required**
- Required if either `fare_leg_rules.min_distance` or `fare_leg_rules.max_distance` are defined.
- Forbidden if both `fare_leg_rules.min_distance` and `fare_leg_rules.max_distance` are empty.| +| `min_distance_units` | Non-negative integer | Optional | Minimum travel distance required for the fare leg rule. `min_distance_units` is included in the lower bound of the interval.

If the lowest distance bound is zero, `fare_leg_rules.min_distance_units` can be empty. Otherwise, `fare_leg_rules.min_distance_units` must be less than `fare_leg_rules.max_distance_units`.| +| `max_distance_units` | Non-negative integer | Optional | Maximum travel distance required for the fare leg rule. `max_distance_units` is excluded from the upper bound of the interval.

If the upper distance bound is undefined (i.e., the conceptual upper limit), `fare_leg_rules.max_distance_units` can be empty. Otherwise, `fare_leg_rules.max_distance_units` must be greater than `fare_leg_rules.min_distance_units`.| | `fare_product_id` | Foreign ID referencing `fare_products.fare_product_id` | **Required** | The fare product required to travel the leg. | | `rule_priority` | Non-negative integer | Optional | Defines the order of priority in which matching rules are applied to legs, allowing certain rules to take precedence over others. When multiple entries in `fare_leg_rules.txt` match, the rule or set of rules with the highest value for `rule_priority` will be selected.

An empty value for `rule_priority` is treated as zero. | From 8500a35ec9912b443654050ca15e7ca0329b6fc5 Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Fri, 7 Nov 2025 11:54:52 -0500 Subject: [PATCH 14/18] minor improvements --- gtfs/spec/en/reference.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index 2f756367..d259af3a 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -63,8 +63,8 @@ This section defines terms that are used throughout this document. * **Sub-journey** - Two or more legs that comprise a subset of a journey. * **Fare product** - Purchassable fare products that can be used to pay for or validate travel. * **Effective Fare Leg** - A sub-journey of two or more legs that should be treated as a single leg for matching rules in [fare_leg_rules.txt](#fare_leg_rulestxt) for the purposes of fare calculation. -* **Distance Unit** (or Fare distance unit) - Any unit used to calculate the fare for distance-based fare structures. The unit can be a distance measure (represented in meters, km, miles, etc) or any relevant incremental unit (e.g. number of stops crossed). The distance unit does not necessarily correspond to a physical measure of distance. It may also be adjusted by adding or subtracting arbitrary values to represent fare discounts or supplements. -* **Distance-Based Leg** - A leg whose fare which is based on the distance units traveled. In [fare_leg_rules.txt](#fare_leg_rulestxt), a distance-based leg is a leg that has at least fare_leg_rules.min_distance_units or fare_leg_rules.max_distance_units assigned (not empty). +* **Distance Unit** (or Fare distance unit) - Any unit used to calculate the fare for distance-based fare structures. The unit can be a distance measure (e.g., meters, kilometers, miles) or any other incremental unit (e.g., number of stops crossed). The distance unit does not necessarily correspond to a physical measure of distance. It may also be adjusted by adding or subtracting arbitrary values to represent fare discounts or supplements. +* **Distance-Based Leg** - A leg whose fare is based on the distance units traveled. In [fare_leg_rules.txt](#fare_leg_rulestxt), a distance-based leg is a leg that has at least fare_leg_rules.min_distance_units or fare_leg_rules.max_distance_units assigned (not empty). ### Presence @@ -532,8 +532,8 @@ For a sub-journey of two consecutive legs with a transfer, if the transfer match | Field Name | Type | Presence | Description | | ------ | ------ | ------ | ------ | -| `from_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a pre-transfer leg that uses the specified route network. If specified, the same `to_network_id` must also be specified.

If `to_network_id` is associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`, then `from_network_id` needs to be associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`. (Cannot join a distance-based leg to a leg that is not distance-based).| -| `to_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a post-transfer leg that uses the specified route network. If specified, the same `from_network_id` must also be specified.

If `from_network_id` is associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`, then `to_network_id` needs to be associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`. (Cannot join a distance-based leg to a leg that is not distance-based).| +| `from_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a pre-transfer leg that uses the specified route network. If specified, the same `to_network_id` must also be specified.

If `to_network_id` is associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`, then `from_network_id` needs to be associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`. (A distance-based leg cannot be joined with a leg that is not distance-based).| +| `to_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a post-transfer leg that uses the specified route network. If specified, the same `from_network_id` must also be specified.

If `from_network_id` is associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`, then `to_network_id` needs to be associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`. (A distance-based leg cannot be joined with a leg that is not distance-based).| | `from_stop_id` | Foreign ID referencing `stops.stop_id`| **Conditionally Required** | Matches a pre-transfer leg that ends at the specified stop (`location_type=0` or empty) or station (`location_type=1`).

Conditionally Required:
- **Required** if `to_stop_id` is defined.
- Optional otherwise. | | `to_stop_id` | Foreign ID referencing `stops.stop_id`| **Conditionally Required** | Matches a post-transfer leg that starts at the specified stop (`location_type=0` or empty) or station (`location_type=1`).

Conditionally Required:
- **Required** if `from_stop_id` is defined.
- Optional otherwise. | | `duration_limit` | Positive integer | **Optional** | Defines the duration limit of the transfer between the legs.

Must be expressed in integer increments of seconds.

If there is no duration limit, `fare_leg_join_rules.duration_limit` must be empty. | From 7eedb7ae58eaf1f42187db1b80861469788f9ac5 Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Fri, 7 Nov 2025 11:59:50 -0500 Subject: [PATCH 15/18] Fix formatting issues in reference.md Updated formatting for better clarity and consistency. --- gtfs/spec/en/reference.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index d259af3a..57eeddff 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -64,7 +64,7 @@ This section defines terms that are used throughout this document. * **Fare product** - Purchassable fare products that can be used to pay for or validate travel. * **Effective Fare Leg** - A sub-journey of two or more legs that should be treated as a single leg for matching rules in [fare_leg_rules.txt](#fare_leg_rulestxt) for the purposes of fare calculation. * **Distance Unit** (or Fare distance unit) - Any unit used to calculate the fare for distance-based fare structures. The unit can be a distance measure (e.g., meters, kilometers, miles) or any other incremental unit (e.g., number of stops crossed). The distance unit does not necessarily correspond to a physical measure of distance. It may also be adjusted by adding or subtracting arbitrary values to represent fare discounts or supplements. -* **Distance-Based Leg** - A leg whose fare is based on the distance units traveled. In [fare_leg_rules.txt](#fare_leg_rulestxt), a distance-based leg is a leg that has at least fare_leg_rules.min_distance_units or fare_leg_rules.max_distance_units assigned (not empty). +* **Distance-Based Leg** - A leg whose fare is based on the distance units traveled. In [fare_leg_rules.txt](#fare_leg_rulestxt), a distance-based leg is a leg that has at least `fare_leg_rules.min_distance_units` or `fare_leg_rules.max_distance_units` assigned (not empty). ### Presence @@ -306,8 +306,7 @@ Primary key (`trip_id`, `stop_sequence`) | `continuous_pickup` | Enum | **Conditionally Forbidden** | Indicates that the rider can board the transit vehicle at any point along the vehicle’s travel path as described by [shapes.txt](#shapestxt), from this `stop_time` to the next `stop_time` in the trip’s `stop_sequence`. Valid options are:

`0` - Continuous stopping pickup.
`1` or empty - No continuous stopping pickup.
`2` - Must phone agency to arrange continuous stopping pickup.
`3` - Must coordinate with driver to arrange continuous stopping pickup.

If this field is populated, it overrides any continuous pickup behavior defined in [routes.txt](#routestxt). If this field is empty, the `stop_time` inherits any continuous pickup behavior defined in [routes.txt](#routestxt).

**Conditionally Forbidden**:
- **Forbidden** if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.
- Optional otherwise. | | `continuous_drop_off` | Enum | **Conditionally Forbidden** | Indicates that the rider can alight from the transit vehicle at any point along the vehicle’s travel path as described by [shapes.txt](#shapestxt), from this `stop_time` to the next `stop_time` in the trip’s `stop_sequence`. Valid options are:

`0` - Continuous stopping drop off.
`1` or empty - No continuous stopping drop off.
`2` - Must phone agency to arrange continuous stopping drop off.
`3` - Must coordinate with driver to arrange continuous stopping drop off.

If this field is populated, it overrides any continuous drop-off behavior defined in [routes.txt](#routestxt). If this field is empty, the `stop_time` inherits any continuous drop-off behavior defined in [routes.txt](#routestxt).

**Conditionally Forbidden**:
- **Forbidden** if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.
- Optional otherwise. | | `shape_dist_traveled` | Non-negative float | Optional | Actual distance traveled along the associated shape, from the first stop to the stop specified in this record. This field specifies how much of the shape to draw between any two stops during a trip. Must be in the same units used in [shapes.txt](#shapestxt). Values used for `shape_dist_traveled` must increase along with `stop_sequence`; they must not be used to show reverse travel along a route.

Recommended for routes that have looping or inlining (the vehicle crosses or travels over the same portion of alignment in one trip). See [`shapes.shape_dist_traveled`](#shapestxt).
*Example: If a bus travels a distance of 5.25 kilometers from the start of the shape to the stop,`shape_dist_traveled`=`5.25`.*| -| `fare_distance_units_traveled` | Non-negative integer | Conditionally Required | Fare distance units traveled along the trip, from the first stop to the stop specified in this record. `fare_distance_units_traveled` does not have to be in the same unit as `stop_times.shape_dist_traveled`.

If the fare is calculated based on the distance covered by the route shape, `fare_distance_units_traveled` may correspond to `stop_times.shape_dist_traveled` (measured in meters) or to its rounded value (floor or ceiling), depending on the fare structure. If the fare is calculated based on stops, `fare_distance_units_traveled` may represent the number of stops crossed. `fare_distance_units_traveled` may also be adjusted by adding or subtracting arbitrary values to represent fare discounts or supplements. -

**Conditionally Required**
- Required if the `route_id` of the `trip_id` of this stop time is part of a `network_id` which is associated to a distance-based `leg_group_id` in `fare_leg_rules.txt`.
- Forbidden otherwise.| +| `fare_distance_units_traveled` | Non-negative integer | Conditionally Required | Fare distance units traveled along the trip, from the first stop to the stop specified in this record. `fare_distance_units_traveled` does not have to be in the same unit as `stop_times.shape_dist_traveled`.

If the fare is calculated based on the distance covered by the route shape, `fare_distance_units_traveled` may correspond to `stop_times.shape_dist_traveled` (measured in meters) or to its rounded value (floor or ceiling), depending on the fare structure. If the fare is calculated based on stops, `fare_distance_units_traveled` may represent the number of stops crossed. `fare_distance_units_traveled` may also be adjusted by adding or subtracting arbitrary values to represent fare discounts or supplements.

**Conditionally Required**
- Required if the `route_id` of the `trip_id` of this stop time is part of a `network_id` which is associated to a distance-based `leg_group_id` in `fare_leg_rules.txt`.
- Forbidden otherwise.| | `timepoint` | Enum | Optional | Indicates if arrival and departure times for a stop are strictly adhered to by the vehicle or if they are instead approximate and/or interpolated times. This field allows a GTFS producer to provide interpolated stop-times, while indicating that the times are approximate. Valid options are:

`0` - Times are considered approximate.
`1` - Times are considered exact.

All records of [stop_times.txt](#stop_timestxt) with defined arrival or departure times should have timepoint values populated. If no timepoint values are provided, all times are considered exact. | | `pickup_booking_rule_id` | Foreign ID referencing `booking_rules.booking_rule_id` | Optional | Identifies the boarding booking rule at this stop time.

Recommended when `pickup_type=2`. | | `drop_off_booking_rule_id` | Foreign ID referencing `booking_rules.booking_rule_id` | Optional | Identifies the alighting booking rule at this stop time.

Recommended when `drop_off_type=2`. | From 4fc70040742ae82c34b8402f5804510c35ac3a4e Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Fri, 7 Nov 2025 12:08:13 -0500 Subject: [PATCH 16/18] Update fare_distance_units_traveled description --- gtfs/spec/en/reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index 57eeddff..1c961ee2 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -306,7 +306,7 @@ Primary key (`trip_id`, `stop_sequence`) | `continuous_pickup` | Enum | **Conditionally Forbidden** | Indicates that the rider can board the transit vehicle at any point along the vehicle’s travel path as described by [shapes.txt](#shapestxt), from this `stop_time` to the next `stop_time` in the trip’s `stop_sequence`. Valid options are:

`0` - Continuous stopping pickup.
`1` or empty - No continuous stopping pickup.
`2` - Must phone agency to arrange continuous stopping pickup.
`3` - Must coordinate with driver to arrange continuous stopping pickup.

If this field is populated, it overrides any continuous pickup behavior defined in [routes.txt](#routestxt). If this field is empty, the `stop_time` inherits any continuous pickup behavior defined in [routes.txt](#routestxt).

**Conditionally Forbidden**:
- **Forbidden** if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.
- Optional otherwise. | | `continuous_drop_off` | Enum | **Conditionally Forbidden** | Indicates that the rider can alight from the transit vehicle at any point along the vehicle’s travel path as described by [shapes.txt](#shapestxt), from this `stop_time` to the next `stop_time` in the trip’s `stop_sequence`. Valid options are:

`0` - Continuous stopping drop off.
`1` or empty - No continuous stopping drop off.
`2` - Must phone agency to arrange continuous stopping drop off.
`3` - Must coordinate with driver to arrange continuous stopping drop off.

If this field is populated, it overrides any continuous drop-off behavior defined in [routes.txt](#routestxt). If this field is empty, the `stop_time` inherits any continuous drop-off behavior defined in [routes.txt](#routestxt).

**Conditionally Forbidden**:
- **Forbidden** if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.
- Optional otherwise. | | `shape_dist_traveled` | Non-negative float | Optional | Actual distance traveled along the associated shape, from the first stop to the stop specified in this record. This field specifies how much of the shape to draw between any two stops during a trip. Must be in the same units used in [shapes.txt](#shapestxt). Values used for `shape_dist_traveled` must increase along with `stop_sequence`; they must not be used to show reverse travel along a route.

Recommended for routes that have looping or inlining (the vehicle crosses or travels over the same portion of alignment in one trip). See [`shapes.shape_dist_traveled`](#shapestxt).
*Example: If a bus travels a distance of 5.25 kilometers from the start of the shape to the stop,`shape_dist_traveled`=`5.25`.*| -| `fare_distance_units_traveled` | Non-negative integer | Conditionally Required | Fare distance units traveled along the trip, from the first stop to the stop specified in this record. `fare_distance_units_traveled` does not have to be in the same unit as `stop_times.shape_dist_traveled`.

If the fare is calculated based on the distance covered by the route shape, `fare_distance_units_traveled` may correspond to `stop_times.shape_dist_traveled` (measured in meters) or to its rounded value (floor or ceiling), depending on the fare structure. If the fare is calculated based on stops, `fare_distance_units_traveled` may represent the number of stops crossed. `fare_distance_units_traveled` may also be adjusted by adding or subtracting arbitrary values to represent fare discounts or supplements.

**Conditionally Required**
- Required if the `route_id` of the `trip_id` of this stop time is part of a `network_id` which is associated to a distance-based `leg_group_id` in `fare_leg_rules.txt`.
- Forbidden otherwise.| +| `fare_distance_units_traveled` | Non-negative integer | Conditionally Required | Fare distance units traveled along the trip, from the first stop to the stop specified in this record. `fare_distance_units_traveled` is used to match a fare leg rule from [fare_leg_rules.txt](#fare_leg_rulestxt) whose distance interval [`min_distance_units`, `max_distance_units`) includes `fare_distance_units_traveled`.

`fare_distance_units_traveled` does not have to be in the same unit as `stop_times.shape_dist_traveled`.

If the fare is calculated based on the distance covered by the route shape, `fare_distance_units_traveled` may correspond to `stop_times.shape_dist_traveled` (measured in meters) or to its rounded value (floor or ceiling), depending on the fare structure. If the fare is calculated based on stops, `fare_distance_units_traveled` may represent the number of stops crossed. `fare_distance_units_traveled` may also be adjusted by adding or subtracting arbitrary values to represent fare discounts or supplements.

**Conditionally Required**
- Required if the `route_id` of the `trip_id` of this stop time is part of a `network_id` which is associated to a distance-based `leg_group_id` in `fare_leg_rules.txt`.
- Forbidden otherwise.| | `timepoint` | Enum | Optional | Indicates if arrival and departure times for a stop are strictly adhered to by the vehicle or if they are instead approximate and/or interpolated times. This field allows a GTFS producer to provide interpolated stop-times, while indicating that the times are approximate. Valid options are:

`0` - Times are considered approximate.
`1` - Times are considered exact.

All records of [stop_times.txt](#stop_timestxt) with defined arrival or departure times should have timepoint values populated. If no timepoint values are provided, all times are considered exact. | | `pickup_booking_rule_id` | Foreign ID referencing `booking_rules.booking_rule_id` | Optional | Identifies the boarding booking rule at this stop time.

Recommended when `pickup_type=2`. | | `drop_off_booking_rule_id` | Foreign ID referencing `booking_rules.booking_rule_id` | Optional | Identifies the alighting booking rule at this stop time.

Recommended when `drop_off_type=2`. | From ff180b927677a847d22956384af01ebc4fb1504e Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Mon, 17 Nov 2025 13:11:13 -0500 Subject: [PATCH 17/18] clarify distance fields and prohibit use of stop_sequence and fare_dist_traveled --- gtfs/spec/en/reference.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index 1c961ee2..845a5add 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -512,8 +512,8 @@ To process the cost of a leg: | `to_area_id` | Foreign ID referencing `areas.area_id` | Optional | Identifies an arrival area.

If the `rule_priority` field does not exist AND there are no matching `fare_leg_rules.to_area_id` values to the `area_id` being filtered, empty `fare_leg_rules.to_area_id` will be matched by default.

An empty entry in `fare_leg_rules.to_area_id` corresponds to all areas defined in `areas.area_id` excluding the ones listed under `fare_leg_rules.to_area_id`

If the `rule_priority` field exists in the file, an empty `fare_leg_rules.to_area_id` indicates that the arrival area of the leg does not affect the matching of this rule.

When matching against an [effective fare leg of multiple legs](#fare_leg_join_rulestxt), the last leg of the effective fare leg is used for determining the arrival area. | | `from_timeframe_group_id` | Foreign ID referencing `timeframes.timeframe_group_id` | Optional | Defines the timeframe for the fare validation event at the start of the fare leg.

The “start time” of the fare leg is the time at which the event is scheduled to occur. For example, the time could be the scheduled departure time of a bus at the start of a fare leg where the rider boards and validates their fare. For the rule matching semantics below, the start time is computed in local time, as determined by [Local Time Semantics](#localtimesemantics) of [timeframes.txt](#timeframestxt). The stop or station of the fare leg’s departure event should be used for timezone resolution, where appropriate.

For a fare leg rule that specifies a `from_timeframe_group_id`, that rule will match a particular leg if there exists at least one record in [timeframes.txt](#timeframestxt) where all of the following conditions are true
- The value of `timeframe_group_id` is equal to the `from_timeframe_group_id` value.
- The set of days identified by the record’s `service_id` contains the “current day” of the fare leg’s start time.
- The “time-of-day” of the fare leg's start time is greater than or equal to the record’s `timeframes.start_time` value and less than the `timeframes.end_time` value.

An empty `fare_leg_rules.from_timeframe_group_id` indicates that the start time of the leg does not affect the matching of this rule.

When matching against an [effective fare leg of multiple legs](#fare_leg_join_rulestxt), the first leg of the effective fare leg is used for determining the starting fare validation event. | | `to_timeframe_group_id` | Foreign ID referencing `timeframes.timeframe_group_id` | Optional | Defines the timeframe for the fare validation event at the end of the fare leg.

The “end time” of the fare leg is the time at which the event is scheduled to occur. For example, the time could be the scheduled arrival time of a bus at the end of a fare leg where the rider gets off and validates their fare. For the rule matching semantics below, the end time is computed in local time, as determined by [Local Time Semantics](#localtimesemantics) of [timeframes.txt](#timeframestxt). The stop or station of the fare leg’s arrival event should be used for timezone resolution, where appropriate.

For a fare leg rule that specifies a `to_timeframe_group_id`, that rule will match a particular leg if there exists at least one record in [timeframes.txt](#timeframestxt) where all of the following conditions are true
- The value of `timeframe_group_id` is equal to the `to_timeframe_group_id` value.
- The set of days identified by the record’s `service_id` contains the “current day” of the fare leg’s end time.
- The “time-of-day” of the fare leg's end time is greater than or equal to the record’s `timeframes.start_time` value and less than the `timeframes.end_time` value.

An empty `fare_leg_rules.to_timeframe_group_id` indicates that the end time of the leg does not affect the matching of this rule.

When matching against an [effective fare leg of multiple legs](#fare_leg_join_rulestxt), the last leg of the effective fare leg is used for determining the ending fare validation event. | -| `min_distance_units` | Non-negative integer | Optional | Minimum travel distance required for the fare leg rule. `min_distance_units` is included in the lower bound of the interval.

If the lowest distance bound is zero, `fare_leg_rules.min_distance_units` can be empty. Otherwise, `fare_leg_rules.min_distance_units` must be less than `fare_leg_rules.max_distance_units`.| -| `max_distance_units` | Non-negative integer | Optional | Maximum travel distance required for the fare leg rule. `max_distance_units` is excluded from the upper bound of the interval.

If the upper distance bound is undefined (i.e., the conceptual upper limit), `fare_leg_rules.max_distance_units` can be empty. Otherwise, `fare_leg_rules.max_distance_units` must be greater than `fare_leg_rules.min_distance_units`.| +| `min_distance_units` | Non-negative integer | Optional | Minimum travel distance units required for the fare leg rule. `min_distance_units` is included in the lower bound of the interval.

For a fare leg rule that specifies a `min_distance_units`, the leg rule will match a particular leg where `min_distance_units` is less than or equal to the leg's `stop_times.fare_distance_units_traveled` (or sum of `stop_times.fare_distance_units_traveled` for effective fare legs).

Only `stop_times.fare_distance_units_traveled` can be used to match legs for distance-based leg rules, `stop_times.stop_sequence` and `stop_times.shape_dist_traveled` cannot be used to match legs.

If the lowest distance bound is zero, `fare_leg_rules.min_distance_units` can be empty. Otherwise, `fare_leg_rules.min_distance_units` must be less than `fare_leg_rules.max_distance_units`.| +| `max_distance_units` | Non-negative integer | Optional | Maximum travel distance units required for the fare leg rule. `max_distance_units` is excluded from the upper bound of the interval.

For a fare leg rule that specifies a `max_distance_units`, the leg rule will match a particular leg where `max_distance_units` is strictly greater than the leg's `stop_times.fare_distance_units_traveled` (or sum of `stop_times.fare_distance_units_traveled` for effective fare legs).

Only `stop_times.fare_distance_units_traveled` can be used to match legs for distance-based leg rules, `stop_times.stop_sequence` and `stop_times.shape_dist_traveled` cannot be used to match legs.

If the upper distance bound is undefined (i.e., the conceptual upper limit), `fare_leg_rules.max_distance_units` can be empty. Otherwise, `fare_leg_rules.max_distance_units` must be greater than `fare_leg_rules.min_distance_units`.| | `fare_product_id` | Foreign ID referencing `fare_products.fare_product_id` | **Required** | The fare product required to travel the leg. | | `rule_priority` | Non-negative integer | Optional | Defines the order of priority in which matching rules are applied to legs, allowing certain rules to take precedence over others. When multiple entries in `fare_leg_rules.txt` match, the rule or set of rules with the highest value for `rule_priority` will be selected.

An empty value for `rule_priority` is treated as zero. | From eda4a753e1524dfcac911fa67fab64a764b9a882 Mon Sep 17 00:00:00 2001 From: Skander Chouchene Date: Mon, 24 Nov 2025 14:25:15 -0500 Subject: [PATCH 18/18] address comments --- gtfs/spec/en/reference.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gtfs/spec/en/reference.md b/gtfs/spec/en/reference.md index 845a5add..ffbdb6c0 100644 --- a/gtfs/spec/en/reference.md +++ b/gtfs/spec/en/reference.md @@ -306,7 +306,7 @@ Primary key (`trip_id`, `stop_sequence`) | `continuous_pickup` | Enum | **Conditionally Forbidden** | Indicates that the rider can board the transit vehicle at any point along the vehicle’s travel path as described by [shapes.txt](#shapestxt), from this `stop_time` to the next `stop_time` in the trip’s `stop_sequence`. Valid options are:

`0` - Continuous stopping pickup.
`1` or empty - No continuous stopping pickup.
`2` - Must phone agency to arrange continuous stopping pickup.
`3` - Must coordinate with driver to arrange continuous stopping pickup.

If this field is populated, it overrides any continuous pickup behavior defined in [routes.txt](#routestxt). If this field is empty, the `stop_time` inherits any continuous pickup behavior defined in [routes.txt](#routestxt).

**Conditionally Forbidden**:
- **Forbidden** if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.
- Optional otherwise. | | `continuous_drop_off` | Enum | **Conditionally Forbidden** | Indicates that the rider can alight from the transit vehicle at any point along the vehicle’s travel path as described by [shapes.txt](#shapestxt), from this `stop_time` to the next `stop_time` in the trip’s `stop_sequence`. Valid options are:

`0` - Continuous stopping drop off.
`1` or empty - No continuous stopping drop off.
`2` - Must phone agency to arrange continuous stopping drop off.
`3` - Must coordinate with driver to arrange continuous stopping drop off.

If this field is populated, it overrides any continuous drop-off behavior defined in [routes.txt](#routestxt). If this field is empty, the `stop_time` inherits any continuous drop-off behavior defined in [routes.txt](#routestxt).

**Conditionally Forbidden**:
- **Forbidden** if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.
- Optional otherwise. | | `shape_dist_traveled` | Non-negative float | Optional | Actual distance traveled along the associated shape, from the first stop to the stop specified in this record. This field specifies how much of the shape to draw between any two stops during a trip. Must be in the same units used in [shapes.txt](#shapestxt). Values used for `shape_dist_traveled` must increase along with `stop_sequence`; they must not be used to show reverse travel along a route.

Recommended for routes that have looping or inlining (the vehicle crosses or travels over the same portion of alignment in one trip). See [`shapes.shape_dist_traveled`](#shapestxt).
*Example: If a bus travels a distance of 5.25 kilometers from the start of the shape to the stop,`shape_dist_traveled`=`5.25`.*| -| `fare_distance_units_traveled` | Non-negative integer | Conditionally Required | Fare distance units traveled along the trip, from the first stop to the stop specified in this record. `fare_distance_units_traveled` is used to match a fare leg rule from [fare_leg_rules.txt](#fare_leg_rulestxt) whose distance interval [`min_distance_units`, `max_distance_units`) includes `fare_distance_units_traveled`.

`fare_distance_units_traveled` does not have to be in the same unit as `stop_times.shape_dist_traveled`.

If the fare is calculated based on the distance covered by the route shape, `fare_distance_units_traveled` may correspond to `stop_times.shape_dist_traveled` (measured in meters) or to its rounded value (floor or ceiling), depending on the fare structure. If the fare is calculated based on stops, `fare_distance_units_traveled` may represent the number of stops crossed. `fare_distance_units_traveled` may also be adjusted by adding or subtracting arbitrary values to represent fare discounts or supplements.

**Conditionally Required**
- Required if the `route_id` of the `trip_id` of this stop time is part of a `network_id` which is associated to a distance-based `leg_group_id` in `fare_leg_rules.txt`.
- Forbidden otherwise.| +| `fare_distance_units_traveled` | Non-negative integer | Conditionally Required | Fare distance units traveled along the trip, from the first stop to the stop specified in this record. `fare_distance_units_traveled` is used to match a fare leg rule from [fare_leg_rules.txt](#fare_leg_rulestxt) whose distance interval [`min_distance_units`, `max_distance_units`] includes the `fare_distance_units_traveled` covered so far in the journey starting from the stop that the rider boards at (the difference between `fare_distance_units_traveled` at the destination and `fare_distance_units_traveled` at the origin of the trip).

`fare_distance_units_traveled` does not have to be in the same unit as `stop_times.shape_dist_traveled`.

If the fare is calculated based on the distance covered by the route shape, `fare_distance_units_traveled` may correspond to `stop_times.shape_dist_traveled` (measured in meters) or to its rounded value (floor or ceiling), depending on the fare structure. If the fare is calculated based on stops, `fare_distance_units_traveled` may represent the number of stops crossed. `fare_distance_units_traveled` may also be adjusted by adding or subtracting arbitrary values to represent fare discounts or supplements.

**Conditionally Required**
- Required if the `route_id` of the `trip_id` of this stop time is part of a `network_id` which is associated to a distance-based `leg_group_id` in `fare_leg_rules.txt`.
- Forbidden otherwise.| | `timepoint` | Enum | Optional | Indicates if arrival and departure times for a stop are strictly adhered to by the vehicle or if they are instead approximate and/or interpolated times. This field allows a GTFS producer to provide interpolated stop-times, while indicating that the times are approximate. Valid options are:

`0` - Times are considered approximate.
`1` - Times are considered exact.

All records of [stop_times.txt](#stop_timestxt) with defined arrival or departure times should have timepoint values populated. If no timepoint values are provided, all times are considered exact. | | `pickup_booking_rule_id` | Foreign ID referencing `booking_rules.booking_rule_id` | Optional | Identifies the boarding booking rule at this stop time.

Recommended when `pickup_type=2`. | | `drop_off_booking_rule_id` | Foreign ID referencing `booking_rules.booking_rule_id` | Optional | Identifies the alighting booking rule at this stop time.

Recommended when `drop_off_type=2`. | @@ -535,8 +535,8 @@ For a sub-journey of two consecutive legs with a transfer, if the transfer match | `to_network_id` | Foreign ID referencing `routes.network_id` or `networks.network_id`| **Required** | Matches a post-transfer leg that uses the specified route network. If specified, the same `from_network_id` must also be specified.

If `from_network_id` is associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`, then `to_network_id` needs to be associated with at least one distance-based `leg_group_id` in `fare_leg_rules.txt`. (A distance-based leg cannot be joined with a leg that is not distance-based).| | `from_stop_id` | Foreign ID referencing `stops.stop_id`| **Conditionally Required** | Matches a pre-transfer leg that ends at the specified stop (`location_type=0` or empty) or station (`location_type=1`).

Conditionally Required:
- **Required** if `to_stop_id` is defined.
- Optional otherwise. | | `to_stop_id` | Foreign ID referencing `stops.stop_id`| **Conditionally Required** | Matches a post-transfer leg that starts at the specified stop (`location_type=0` or empty) or station (`location_type=1`).

Conditionally Required:
- **Required** if `from_stop_id` is defined.
- Optional otherwise. | -| `duration_limit` | Positive integer | **Optional** | Defines the duration limit of the transfer between the legs.

Must be expressed in integer increments of seconds.

If there is no duration limit, `fare_leg_join_rules.duration_limit` must be empty. | -| `duration_limit_type` | Enum | **Conditionally Required** | Defines the relative start and end of `fare_leg_join_rules.duration_limit`.

Valid options are:
`0` - Between the departure fare validation of the current leg and the arrival fare validation of the next leg.
`1` - Between the departure fare validation of the current leg and the departure fare validation of the next leg.
`2` - Between the arrival fare validation of the current leg and the departure fare validation of the next leg.
`3` - Between the arrival fare validation of the current leg and the arrival fare validation of the next leg.

Conditionally Required:
- Required if `fare_leg_join_rules.duration_limit` is defined.
- Forbidden if `fare_leg_join_rules.duration_limit` is empty. | +| `duration_limit` | Positive integer | **Optional** | Defines the duration limit of the transfer between the legs that constitute the effective leg.

Must be expressed in integer increments of seconds.

If there is no duration limit, `fare_leg_join_rules.duration_limit` must be empty. | +| `duration_limit_type` | Enum | **Conditionally Required** | Defines the relative start and end of `fare_leg_join_rules.duration_limit`.

Valid options are:
`0` - Between the departure fare validation of the first leg in the effective leg and the arrival fare validation of the last leg in the effective leg.
`1` - Between the departure fare validation of the first leg in the effective leg and the departure fare validation of the last leg in the effective leg.
`2` - Between the arrival fare validation of the first leg in the effective leg and the departure fare validation of the last leg in the effective leg.
`3` - Between the arrival fare validation of the first leg in the effective leg and the arrival fare validation of the last leg in the effective leg.

When an effective leg with the same `from_network_id` and `to_network_id` is matched multiple times consecutively within a multi-leg journey, the `duration_limit` specified by the effective leg should be measured starting from the first matched leg.

Conditionally Required:
- **Required** if `fare_leg_join_rules.duration_limit` is defined.
- **Forbidden** if `fare_leg_join_rules.duration_limit` is empty. | ### fare_transfer_rules.txt