-
Notifications
You must be signed in to change notification settings - Fork 221
[GTFS-Fares v2] Add Distance-Based Fares #556
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
skalexch
wants to merge
20
commits into
master
Choose a base branch
from
MobilityDatafares-v2-distance-based
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
40854cc
Update reference.md
skalexch baff7e6
add distance join illustrations
skalexch 23ceceb
Update reference.md
skalexch 6891378
Add files via upload
skalexch 45461d3
simplify proposal
skalexch 9164bcd
remove files
skalexch 8673f3e
clean duration-limit description
skalexch c677847
Add restriction over joining distance-based and non-distance-based legs
skalexch 1aab2ce
fix bad description and typo
skalexch c81c963
Add "actual road or track distance" to the definition of distance_type
skalexch 123a290
add min_distance, max_distance to PK of fare_leg_rules.txt
skalexch 58439bb
Change min_distance and max_distance to float type
skalexch 2d5b7ac
Address Working Group comments
skalexch 8500a35
minor improvements
skalexch 7eedb7a
Fix formatting issues in reference.md
skalexch 4fc7004
Update fare_distance_units_traveled description
skalexch ff180b9
clarify distance fields and prohibit use of stop_sequence and fare_di…
skalexch eda4a75
address comments
skalexch 01a1ce0
remove duration_limit from proposal
skalexch 6df45cc
remove empty line
skalexch File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,6 +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 (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 | ||
|
|
@@ -304,6 +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: <br><br>`0` - Continuous stopping pickup. <br>`1` or empty - No continuous stopping pickup. <br>`2` - Must phone agency to arrange continuous stopping pickup. <br>`3` - Must coordinate with driver to arrange continuous stopping pickup. <br><br>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).<br><br>**Conditionally Forbidden**:<br>- **Forbidden** if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.<br> - 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: <br><br>`0` - Continuous stopping drop off. <br>`1` or empty - No continuous stopping drop off. <br>`2` - Must phone agency to arrange continuous stopping drop off. <br>`3` - Must coordinate with driver to arrange continuous stopping drop off. <br><br>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).<br><br>**Conditionally Forbidden**:<br>- **Forbidden** if `start_pickup_drop_off_window` or `end_pickup_drop_off_window` are defined.<br> - 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.<br><br>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). <hr>*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`. <br><br> `fare_distance_units_traveled` does not have to be in the same unit as `stop_times.shape_dist_traveled`. <br><br> 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. <br><br> **Conditionally Required** <br>- 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`.<br> - 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:<br><br>`0` - Times are considered approximate. <br>`1` - Times are considered exact. <br><br> 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.<br><br>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.<br><br>Recommended when `drop_off_type=2`. | | ||
|
|
@@ -463,7 +466,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_units, max_distance_units, fare_product_id`) | ||
|
|
||
| Fare rules for individual legs of travel. | ||
|
|
||
|
|
@@ -477,6 +480,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` | ||
| <br/> | ||
|
|
||
| 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. | ||
|
|
@@ -507,6 +512,8 @@ To process the cost of a leg: | |
| | `to_area_id` | Foreign ID referencing `areas.area_id` | Optional | Identifies an arrival area.<br><br>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.<br><br> 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`<br><br>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.<br><br>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.<br><br>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.<br><br>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<br>- The value of `timeframe_group_id` is equal to the `from_timeframe_group_id` value.<br>- The set of days identified by the record’s `service_id` contains the “current day” of the fare leg’s start time.<br>- 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.<br><br>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.<br><br>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.<br><br>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.<br><br>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<br>- The value of `timeframe_group_id` is equal to the `to_timeframe_group_id` value.<br>- The set of days identified by the record’s `service_id` contains the “current day” of the fare leg’s end time.<br>- 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.<br><br>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.<br><br>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 units required for the fare leg rule. `min_distance_units` is included in the lower bound of the interval.<br><br>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).<br><br> 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. <br><br> 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. <br><br>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).<br><br> 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.<br><br> 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.<br><br>An empty value for `rule_priority` is treated as zero. | | ||
|
|
||
|
|
@@ -520,13 +527,17 @@ 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_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.<br><br>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.<br><br>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`).<br><br>Conditionally Required:<br> - **Required** if `to_stop_id` is defined.<br> - 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`).<br><br>Conditionally Required:<br> - **Required** if `from_stop_id` is defined.<br> - Optional otherwise. | | ||
| | `duration_limit` | Positive integer | **Optional** | Defines the duration limit of the transfer between the legs.<br><br>Must be expressed in integer increments of seconds.<br><br>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`.<br><br>Valid options are:<br>`0` - Between the departure fare validation of the current leg and the arrival fare validation of the next leg.<br>`1` - Between the departure fare validation of the current leg and the departure fare validation of the next leg.<br>`2` - Between the arrival fare validation of the current leg and the departure fare validation of the next leg.<br>`3` - Between the arrival fare validation of the current leg and the arrival fare validation of the next leg.<br><br>Conditionally Required:<br>- Required if `fare_leg_join_rules.duration_limit` is defined.<br>- Forbidden if `fare_leg_join_rules.duration_limit` is empty. | | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps replace "current" and "next" by "first" and "last" |
||
|
|
||
|
|
||
| ### fare_transfer_rules.txt | ||
|
|
||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may be interpreted as matching the cumulative distance from the first stop? But passengers may board mid-route.
nit: [
min_distance_units,max_distance_units) bracket consistency