Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add Docked Bikeshare Systems (and Stops History) #441

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 163 additions & 0 deletions provider/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ This specification contains a data standard for *mobility as a service* provider
* [General Information](#general-information)
* [Trips](#trips)
* [Status Changes](#status-changes)
* [Stations](#stations)
* [Station Status Changes](#station-status-changes)
* [Realtime Data](#realtime-data)
- [GBFS](#GBFS)
- [Events](#events)
Expand Down Expand Up @@ -225,6 +227,44 @@ If the currency field is null, USD cents is implied.

[Top][toc]


### Station ID

A station is where a vehicle may be picked up and/or returned. Each station must have a UUID that is unique within the Provider's system. This is not to be confused with the station ID that may be found from a [GBFS](https://github.com/NABSA/gbfs/) endpoint.

[Top][toc]

### Vehicle

This model should be compatible with the proposed [`/vehicles` endpoint](https://github.com/openmobilityfoundation/mobility-data-specification/pull/376). Its main purpose for now is to represent a vehicle that is at a [station](#stations).

| Field | Type | Required/Optional | Comments |
| ----- | ---- | ----------------- | -------- |
| provider_id | UUID | Required | A UUID for the Provider, unique within MDS |
| device_id | UUID | | A unique device ID in UUID format, should match this device in Provider |
| vehicle_id | String | | The Vehicle Identification Number visible on the vehicle itself, should match this device in Provider |
| vehicle_type | Enum | Required | See [vehicle types](#vehicle-types) table |
| propulsion_type | Enum[] | Required | Array of [propulsion types](#propulsion-types); allows multiple values |

[Top][toc]

### Dock

A dock can represents a space to park a singular vehicle, e.g. a dock for a bicycle or a parking space for a car. It is the complement to a [vehicle](#vehicle).

| Field | Type | Required/Optional | Comments |
| ----- | ---- | ----------------- | -------- |
| vehicle_types | Enum[] | Required | Array of [vehicle types](#vehicle-types) that may occupy the dock |
| propulsion_types | Enum[] | Required | Array of [propulsion types](#propulsion-types) that may be at the dock |
| working | Boolean | Required | If true, denotes that the space may either accept or rent out vehicles. If false, the space can do neither |
| vehicle_present | Boolean | Required | |

To clarify, if a dock has a state of `working: false` and `vehicle_present: true` one could infer that either the vehicle has been taken out of service at the dock, or that the dock is jammed.

An empty dock a user is able to return a vehicle to would have the state `working: true` and `vehicle_present: false`.

[Top][toc]

## Trips

A trip represents a journey taken by a *mobility as a service* customer with a geo-tagged start and stop point.
Expand Down Expand Up @@ -259,6 +299,8 @@ Schema: [`trips` schema][trips-schema]
| `standard_cost` | Integer | Optional | The cost, in the currency defined in `currency`, that it would cost to perform that trip in the standard operation of the System (see [Costs & Currencies](#costs--currencies)) |
| `actual_cost` | Integer | Optional | The actual cost, in the currency defined in `currency`, paid by the customer of the *mobility as a service* provider (see [Costs & Currencies](#costs--currencies)) |
| `currency` | String | Optional, USD cents is implied if null.| An [ISO 4217 Alphabetic Currency Code](https://en.wikipedia.org/wiki/ISO_4217#Active_codes) representing the currency of the payee (see [Costs & Currencies](#costs--currencies)) |
| `station_start` | UUID | Required if Applicable | If a trip originates from a station, the [station id](#station-id) of the station |
| `station_end` | UUID | Required if Applicable | If a trip ends at a station, the [station id](#station-id) of the station |

### Trips Query Parameters

Expand Down Expand Up @@ -342,6 +384,7 @@ Schema: [`status_changes` schema][sc-schema]
| `event_time` | [timestamp][ts] | Required | Date/time that event occurred at. See [Event Times](#event-times) |
| `publication_time` | [timestamp][ts] | Optional | Date/time that event became available through the status changes endpoint |
| `event_location` | GeoJSON [Point Feature][geo] | Required | |
| `event_station_id` | UUID | Required if Applicable | If the event occurs at a station, the [station id](#station-id) of the station |
| `battery_pct` | Float | Required if Applicable | Percent battery charge of device, expressed between 0 and 1 |
| `associated_trip` | UUID | Required if Applicable | Trip UUID (foreign key to Trips API), required if `event_type_reason` is `user_pick_up` or `user_drop_off`, or for any other status change event that marks the end of a trip. |
| `associated_ticket` | String | Optional | Identifier for an associated ticket inside an Agency-maintained 311 or CRM system. |
Expand Down Expand Up @@ -381,6 +424,126 @@ Without an `event_time` query parameter, `/status_changes` shall return a `400 B

[Top][toc]

## Stations

A Station represents a station in a point in time, a “station state” to be verbose. Most commonly, this is a docked-bikeshare station, but we aim to let this support scooter corrals, or even parking spots for cars.

This API allows a user to query the historical (and potentially realtime) state of a provider's system of stations. Providers should provide data on their entire system on a daily cadence at the very least. Then, coupled with the `/station_status_changes` endpoint below, one could "fast-forward" to any other point in the day to derive the state of the system then.

Endpoint: `/stations`
Method: `GET`
Schema: To Be Generated
`data` Payload: `{ "stations": [] }`, an array of objects with the following structure

| Field | Type | Required/Optional | Comments |
| ----- | ---- | ----------------- | ----- |
| `provider_id` | UUID | Required | A UUID for the Provider, unique within MDS |
| `provider_name` | String | Required | The public-facing name of the Provider |
| `station_id` | UUID | Required | A station ID unique to the provider's system |
| `gbfs_station_id` | String | Optional | If provided, should match the station_id in the GBFS endpoints |
| `name` | String | Required | Public name of the station |
| `location` | GeoJSON [Point Feature][geo] | Required | |
| `status` | Enum | Required | See [station statuses](#station-statuses) table |
| `reported_at` | [timestamp][ts] | Required | Timestamp when the state of the station was captured |
| `vehicle_types` | Enum[] | Required | Array of [vehicle types](#vehicle-types) the station supports; allows multiple values |
| `propulsion_types` | Enum[] | Required | Array of [propulsion types](#propulsion-types) the station supports; allows multiple values |
| `vehicles` | Vehicles[] | Required | Array of [vehicles](#vehicle) at the station. Will be empty if no vehicles are at the station |
| `docks` | Docks[] | Optional | Array of [docks](#dock) at the station. If this field is null, it is assumed that the number of docks is irrelevant (i.e. a warehouse where users can return or take as many vehicles as needed) |

Folks familiar with [GBFS](https://github.com/NABSA/gbfs) will recognise some similarities, but keep in mind this is a “station state”, so it includes information on the vehicles and docks at a specific point in time as denoted by the `reported_at` field.

### Stations Query Parameters

The `/stations` API should allow querying stations with a combination of query parameters:

| Parameter | Format | Expected Output |
| --------- | ------ | --------------- |
| `timestamp` | [timestamp][ts] | The most recent state of each station as of the specified time |
| `station_ids` | Comma-separated string | All stations whose `station_id` is in the list of station IDs |

If `timestamp` is omitted, or in the future, the endpoint will return the most recent state of the stations. If `timestamp` is in the past, the state of the stations should be the most recent state up until the specified time. For example: if stations have `reported_at: 10` and `reported_at: 20`, and `timestamp=17` was supplied, the endpoint would return the state from `reported_at: 10`.

Unless the `station_ids` filter is explicitly requested, the response is expected to return one `station` object per station in the system, thus returning the entire system.

### Station Statuses

| `status` | Description |
| -------- | ----------- |
| `open` | The station is open and available to rent out or accept vehicles |
| `closed` | The station is closed. Either because of operating hours, repairs, or a seasonal closure |
| `decommissioned` | The station is permanently closed and will be removed from the system |

Some GBFS [station_status](https://github.com/NABSA/gbfs/blob/master/gbfs.md#station_statusjson) states such as `is_installed`, `is_renting`, or `is_returning` are purposefully omitted since they are more relevant for bikeshare riders than planners analysing historical data, but we're open to discussion of their inclusion if it helps users of MDS.

If docked-bikeshare stations are closed be cause of a [Canadian winter](https://github.com/NABSA/gbfs/pull/202#issuecomment-565586255), we expect the status to just be `closed`.

[Top][toc]

## Station Status Changes

The above `/stations` endpoint provides "snapshots" of every station in the system at least once a day, at the very least. If a user wanted the state of a station at a point in time other than what the `/stations` endpoint provides, they would use this one, which gives them a stream of station status changes.

A `station_status_change` represents the state of the station _after_ an event listed in the `change_type_reason` field. Otherwise, is the same as a `stations` object.

Endpoint: `/stations_status_changes`
Method: `GET`
Schema: To Be Generated
`data` Payload: `{ "station_status_changes": [] }`, an array of objects with the following structure

| Field | Type | Required/Optional | Comments |
| ----- | ---- | ----------------- | ----- |
| `provider_id` | UUID | Required | A UUID for the Provider, unique within MDS |
| `provider_name` | String | Required | The public-facing name of the Provider |
| `station_id` | UUID | Required | A station ID unique to the provider's system |
| `gbfs_station_id` | String | Optional | If provided, should match the station_id in the GBFS endpoints |
| `name` | String | Required | Public name of the station |
| `location` | GeoJSON [Point Feature][geo] | Required | |
| `status` | Enum | Required | See [station statuses](#station-statuses) table |
| `change_type_reason` | Enum | Required | See [station change type reasons](#station-change-type-reasons) table |
| `reported_at` | [timestamp][ts] | Required | Timestamp when the state of the station was captured |
| `vehicle_types` | Enum[] | Required | Array of [vehicle types](#vehicle-types) the station supports; allows multiple values |
| `propulsion_types` | Enum[] | Required | Array of [propulsion types](#propulsion-types) the station supports; allows multiple values |
| `vehicles` | Vehicles[] | Required | Array of [vehicles](#vehicle) at the station. Will be empty if no vehicles are at the station |
| `docks` | Docks[] | Optional | Array of [docks](#dock) at the station. If this field is null, it is assumed that the number of docks is irrelevant (i.e. a warehouse where users can return or take as many vehicles as needed) |

People may attempt to draw some parallels with the GBFS [station_information](https://github.com/NABSA/gbfs/blob/master/gbfs.md#station_informationjson) and [station_status](https://github.com/NABSA/gbfs/blob/master/gbfs.md#station_statusjson) endpoints, but here the two new MDS endpoints return objects that are not so different from one another. The difference is that one endpoint is guaranteed to provide the state for once a day; whereas the other returns state changes that have happened.

### Station Status Changes Query Parameters

The `/station_status_changes` API should allow querying stations status changes with a combination of query parameters:

| Parameter | Format | Expected Output |
| --------- | ------ | --------------- |
| `start_time` | [timestamp][ts] | station status changes where `start_time <= station_status_status_change.reported_at` |
| `end_time` | [timestamp][ts] | station status changes where `station_status_status_change.reported_at < end_time` |
| `station_ids` | Comma-separate list | All station states whose `station_id` is in the list of station IDs |

Both `start_time` and `end_time` are required. If either is omitted, the `/station_status_changes` endpoint should return a `400: Bad Request` response.

Unlike the `/stations` endpoint, it is possible for the `/station_status_changes` endpoint to return empty responses if no changes occurred during the specified time range.

### Station Change Type Reasons

| `change_type_reason` | Description |
| -------------------- | ----------- |
| `service_start` | The station is open |
| `service_end` | The station is closed |
| `maintenance` | The station is undergoing repairs, potentially changing some fields |
| `vehicle_drop_off` | A vehicle was returned by a user, so the `vehicles` and `docks` fields are updated |
| `vehicle_pick_up` | A vehicle was picked up by a user, so the `vehicles` and `docks` fields are updated |
| `vehicle_provider_drop_off` | A vehicle or more was/were dropped off up by the provider, so the `vehicles` and `docks` fields are updated |
| `vehicle_provider_pick_up` | A vehicle or more was/were picked up by the provider, so the `vehicles` and `docks` fields are updated |
| `vehicle_fueling` | A vehicle is now unavailable because it needs to be refueled |
| `vehicle_maintenance` | A vehicle is now unavailable because it needs to be repaired |
| `vehicle_unavailable` | A vehicle is now unavailable for other reasons |
| `vehicle_type_added` | The station offers a new vehicle type, changing the `vehicle_types` field and count fields |
| `vehicle_type_removed` | A vehicle type has been removed from the station, changing the `vehicle_types` field and count fields |
| `decommissioning` | The station is slated to be removed |

We encourage providers to comment on any common change type reasons that are missing.

[Top][toc]

## Realtime Data

### GBFS
Expand Down