diff --git a/.github/ISSUE_TEMPLATE/add-agency-id.md b/.github/ISSUE_TEMPLATE/add-agency-id.md new file mode 100644 index 00000000..210c2a84 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/add-agency-id.md @@ -0,0 +1,33 @@ +--- +name: Add Agency ID +about: Create Agency ID for use in MDS +title: 'Add Agency ID: [Agency Name]' +labels: admin +assignees: '' + +--- + +**Note:** See the [Adding an MDS Agency ID](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/Adding-an-MDS-Agency-ID) page for more help. + +Opening this issue will allow you as a city or governmental agency get an official agency ID for use across MDS globally, and include in your Requirements URL. + +**Fields needed from you for the [agencies.csv file](https://github.com/openmobilityfoundation/mobility-data-specification/blob/main/agencies.csv).** + +_All fields are required._ + +1. **agency_country_iso_code** - Two letter ISO country code where your agency is located. + - ... +1. **agency_state** - Geographic abbreviation or name of the state where your agency is located. + - ... +1. **agency_city** - Geographic name of the city where your agency is located. + - ... +1. **agency_name** - Full name of your governmental city/agency. + - ... +1. **agency_id** - A random UUID version 4. There are lots of way to generate a unique UUID, like using this website. + - ... +1. **department_url** - URL to the home page of your agency's department. + - ... +1. **requirement_url** - Full https:// URL where your current Policy Requirements endpoint is located. + - ... + +Additionally, please provide your name and role within your agency to help with verification. diff --git a/.github/ISSUE_TEMPLATE/add-provider-id.md b/.github/ISSUE_TEMPLATE/add-provider-id.md new file mode 100644 index 00000000..9b5ad30f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/add-provider-id.md @@ -0,0 +1,29 @@ +--- +name: Add Provider ID +about: Create Provider ID for use in MDS +title: 'Add Provider ID: [Provider Name]' +labels: admin +assignees: '' + +--- + +**Note:** See the [Adding an MDS Provider ID](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/Adding-an-MDS-Provider-ID) page for more help. + +Opening this issue will allow you as a mobility service provider get an official provider ID for use across MDS globally. + +**Fields needed from you for the [providers.csv file](https://github.com/openmobilityfoundation/mobility-data-specification/blob/main/providers.csv).** + +_All fields are required._ + +1. **provider_name** - Short name of your company. + - ... +1. **provider_id** - A random UUID version 4. There are lots of way to generate a unique UUID, like using this [website](https://www.uuidgenerator.net/version4). + - ... +1. **url** - URL to the home page of your company. + - ... +1. **mds_api_url** - Full https:// URL where your root MDS feeds are located. + - ... +1. **gbfs_api_url** - Full https:// URL where your public GBFS feeds are located. + - ... + +Additionally, please provide your name and role within your agency to help with verification. diff --git a/CODEOWNERS b/CODEOWNERS index 8dc8e483..22519abe 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -3,12 +3,5 @@ ## All MDS approvals * @openmobilityfoundation/omf-admin -## Provider Services Working Group -/provider/* @openmobilityfoundation/provider-maintainers @openmobilityfoundation/omf-admin - -## City Services Working Group Specific, also JSON Schema -/agency/* @openmobilityfoundation/city-services-maintainers @openmobilityfoundation/omf-admin -/policy/* @openmobilityfoundation/city-services-maintainers @openmobilityfoundation/omf-admin - -## schema is changed by provider and city services working group -/schema/* @openmobilityfoundation/provider-maintainers @openmobilityfoundation/city-services-maintainers @openmobilityfoundation/omf-admin +## MDS Working Group +* @openmobilityfoundation/mds-maintainers @openmobilityfoundation/omf-admin diff --git a/README.md b/README.md index 99d3e230..f2218813 100644 --- a/README.md +++ b/README.md @@ -13,18 +13,19 @@ - [Cities Using MDS](#cities-using-mds) - [Providers Using MDS](#providers-using-mds) - [Software Companies Using MDS](#software-companies-using-mds) +- [Data Privacy](#data-privacy) - [Use Cases](#use-cases) - [Related Projects](#related-projects) # About -The Mobility Data Specification (**MDS**), a project of the [Open Mobility Foundation](http://www.openmobilityfoundation.org) (OMF), is a set of Application Programming Interfaces (APIs) focused on dockless e-scooters, bicycles, mopeds and carshare. Inspired by projects like [GTFS](https://developers.google.com/transit/gtfs/reference/) and [GBFS](https://github.com/NABSA/gbfs), the goals of MDS are to provide a standardized way for municipalities or other regulatory agencies to ingest, compare and analyze data from mobility service providers, and to give municipalities the ability to express regulation in machine-readable formats. +The Mobility Data Specification (**MDS**), a project of the [Open Mobility Foundation](http://www.openmobilityfoundation.org) (**OMF**), is a set of Application Programming Interfaces (APIs) focused on shared mobility services such as dockless scooters, bicycles, mopeds, and carshare, and inspired by projects like [GTFS](https://developers.google.com/transit/gtfs/reference/) and [GBFS](https://github.com/NABSA/gbfs). MDS is a digital tool that helps cities better manage transportation in the public right of way, standardizing communication and data-sharing between cities and private mobility providers, allowing cities to share and validate policy digitally, and enabling vehicle management and better outcomes for residents. -**MDS** helps cities interact with companies who operate dockless scooters, bicycles, mopeds and carshare in the public right-of-way. MDS is a key piece of digital infrastructure that supports the effective implementation of mobility policies in cities around the world. For a high level overview, see the [About MDS](https://www.openmobilityfoundation.org/about-mds/) page on the OMF website. +**MDS** is a key piece of digital infrastructure that supports the effective implementation of mobility policies in cities around the world. For a high level overview and visuals, see the [About MDS](https://www.openmobilityfoundation.org/about-mds/) page on the OMF website. ![MDS Main Logo](https://i.imgur.com/AiUedl3.png) -**MDS** is an open-source project. It was originally created by the [Los Angeles Department of Transportation](http://ladot.io) (LADOT). In November 2019, stewardship of MDS and the ownership of this repository were transferred to the Open Mobility Foundation. GitHub automatically redirects any links to this repository in the `CityOfLosAngeles` organization to the `openmobilityfoundation` instead. MDS continues to be used by LADOT and [many other municipalities](#cities-using-mds). +**MDS** is an open-source project originally created by the [Los Angeles Department of Transportation](http://ladot.io) (LADOT). In November 2019, stewardship of MDS and the ownership of this repository were transferred to the [Open Mobility Foundation](http://www.openmobilityfoundation.org). GitHub automatically redirects any links to this repository from the `CityOfLosAngeles` organization to the `openmobilityfoundation` instead. MDS continues to be used by LADOT and [many other municipalities](#cities-using-mds) and companies. [Top][toc] @@ -70,13 +71,13 @@ The [`metrics`](/metrics) API endpoints are intended to be implemented by regula ## Modularity -MDS is designed to be a modular kit-of-parts. Regulatory agencies can use the components of the API that are appropriate for their needs. An agency may choose to use only `agency`, `provider`, or `policy`. Other APIs like `geography`, `jurisdiction`, and `metrics` can be used in coordination as described with these APIs or sometimes on their own. Or agencies may select specific elements (endpoints) from each API to help them implement their goals. Development of the APIs takes place under the guidance of either the OMF's [City Services](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/MDS-City-Services-Working-Group) or [Provider Services](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/MDS-Provider-Services-Working-Group) Working Groups. +MDS is designed to be a modular kit-of-parts. Regulatory agencies can use the components of the API that are appropriate for their needs. An agency may choose to use only `agency`, `provider`, or `policy`. Other APIs like `geography`, `jurisdiction`, and `metrics` can be used in coordination as described with these APIs or sometimes on their own. Or agencies may select specific elements (endpoints) from each API to help them implement their goals. Development of the APIs takes place under the guidance of the OMF's [MDS Working Group](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/MDS-Working-Group). Many parts of the MDS definitions and APIs align across each other. In these cases, consolidated information can be found on the [General Information](/general-information.md) page. You can read more in our **[Understanding the different MDS APIs](https://github.com/openmobilityfoundation/governance/blob/main/technical/Understanding-MDS-APIs.md)** guide. -![MDS APIs and Endpoints](https://i.imgur.com/L5s927a.png) +![MDS APIs and Endpoints](https://i.imgur.com/bfUtCif.png) ## GBFS Requirement @@ -86,34 +87,34 @@ All MDS compatible Provider feeds [must also expose](/provider/README.md#gbfs) a # Get Involved -To stay up to date on MDS releases, meetings, and events, please **subscribe to the [mds-announce](https://groups.google.com/a/groups.openmobilityfoundation.org/forum/#!forum/mds-announce) mailing list** and read our **[Community Wiki](https://github.com/openmobilityfoundation/mobility-data-specification/wiki)**. +To stay up to date on MDS, please **subscribe to the [mds-announce](https://groups.google.com/a/groups.openmobilityfoundation.org/g/mds-announce) mailing list** for general updates, the **[wg-mds](https://groups.google.com/a/groups.openmobilityfoundation.org/g/wg-mds) mailing list** for Working Group details and meetings, and read our **[Community Wiki](https://github.com/openmobilityfoundation/mobility-data-specification/wiki)**. -The Mobility Data Specification is an open source project with all development taking place on GitHub. Comments and ideas can be shared by [starting a discussion](https://github.com/openmobilityfoundation/mobility-data-specification/discussions), [creating an issue](https://github.com/openmobilityfoundation/mobility-data-specification/issues), and specific changes can be suggested by [opening a pull request](https://github.com/openmobilityfoundation/mobility-data-specification/pulls). Before contributing, please review our OMF [CONTRIBUTING page](https://github.com/openmobilityfoundation/governance/blob/main/CONTRIBUTING.md) and our [CODE OF CONDUCT page](https://github.com/openmobilityfoundation/governance/blob/main/CODE_OF_CONDUCT.md) to understand guidelines and policies for participation . +The Mobility Data Specification is an open source project with all development taking place on GitHub and public meetings through our [MDS Working Group](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/MDS-Working-Group). Comments and ideas can be shared by [starting a discussion](https://github.com/openmobilityfoundation/mobility-data-specification/discussions), [creating an issue](https://github.com/openmobilityfoundation/mobility-data-specification/issues), suggesting changes with [a pull request](https://github.com/openmobilityfoundation/mobility-data-specification/pulls), and attending meetings. Before contributing, please review our OMF [CONTRIBUTING](https://github.com/openmobilityfoundation/governance/blob/main/CONTRIBUTING.md) and [CODE OF CONDUCT](https://github.com/openmobilityfoundation/governance/blob/main/CODE_OF_CONDUCT.md) pages to understand guidelines and policies for participation. -You can learn more about the polices, methodologies, and tools in the MDS ecosystem in the [Mobility Data Management State of Practice](https://github.com/openmobilityfoundation/privacy-committee/blob/main/products/state-of-the-practice.md) wiki. To help cities put the right privacy policies in place, the OMF [Privacy, Security, and Transparency Committee](https://github.com/openmobilityfoundation/privacy-committee) has created a comprehensive best-practices document called the [MDS Privacy Guide for Cities](https://github.com/openmobilityfoundation/governance/raw/main/documents/OMF-MDS-Privacy-Guide-for-Cities.pdf). +**Read our [How to Get Involved in the Open Mobility Foundation](https://www.openmobilityfoundation.org/how-to-get-involved-in-the-open-mobility-foundation/) blog post for more detail and an overview of how the OMF is organized.** -You can also get involved in development by joining an [OMF working group](https://github.com/openmobilityfoundation/mobility-data-specification/wiki#omf-meetings). The working groups maintain the OMF GitHub repositories and work through issues and pull requests. Each working group has its own mailing list for non-technical discussion and planning: - -Working Group | Mailing List | Description ---- | --- | --- -Provider Services | [mds-provider-services](https://groups.google.com/a/groups.openmobilityfoundation.org/forum/#!forum/mds-provider-services) | Manages the [`provider`][provider] API within MDS. -City Services | [mds-city-services](https://groups.google.com/a/groups.openmobilityfoundation.org/forum/#!forum/mds-city-services) | Manages the [`agency`][agency], [`policy`][policy], [`geography`][geography], [`jurisdiction`][jurisdiction], and [`metrics`](metrics) APIs within MDS, as well as the [`mds-core`](https://github.com/openmobilityfoundation/mds-core) and [`mds-compliance-mobile`](https://github.com/openmobilityfoundation/mds-compliance-mobile) reference implementations. - -You can view info about current and past releases, the public OMF calendar, and review planning calls in the [wiki](https://github.com/openmobilityfoundation/mobility-data-specification/wiki). - -For questions about MDS please contact by email at [info@openmobilityfoundation.org](mailto:info@openmobilityfoundation.org) or [on our website](https://www.openmobilityfoundation.org/get-in-touch/). Media inquiries to [media@openmobilityfoundation.org](mailto:media@openmobilityfoundation.org). +For other questions about MDS or media inquiries please contact the OMF directly [on our website](https://www.openmobilityfoundation.org/get-in-touch/). ## Membership -OMF Members (public agencies and commercial companies) have additional participation opportunities with leadership roles on our [Board of Directors](https://www.openmobilityfoundation.org/about/), [Privacy, Security, and Transparency Committee](https://github.com/openmobilityfoundation/privacy-committee), [Technology Council](https://github.com/openmobilityfoundation/governance/wiki/Technology-Council), and [Strategy Committee](https://github.com/openmobilityfoundation/governance/wiki/Strategy-Committee), as well as the steering committees of all [Working Groups](https://github.com/openmobilityfoundation/mobility-data-specification/wiki#omf-meetings). +OMF Members (public agencies and commercial companies) have additional participation opportunities with leadership roles within our OMF [governance](https://github.com/openmobilityfoundation/governance#omf-scope-of-work): -Read about [how to become an OMF member](https://www.openmobilityfoundation.org/how-to-become-a-member/) and [contact us](https://mailchi.mp/openmobilityfoundation/membership) for more details. +- [Board of Directors](https://www.openmobilityfoundation.org/about/) +- [Privacy, Security, and Transparency Committee](https://github.com/openmobilityfoundation/privacy-committee) +- [Technology Council](https://github.com/openmobilityfoundation/governance/wiki/Technology-Council) +- [Strategy Committee](https://github.com/openmobilityfoundation/governance/wiki/Strategy-Committee) +- [Advisory Committee](https://github.com/openmobilityfoundation/governance/wiki/Advisory-Committee) +- Steering committees of all [Working Groups](https://github.com/openmobilityfoundation/mobility-data-specification/wiki#omf-meetings), currently: + - [MDS Working Group](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/MDS-Working-Group) + - [Curb Working Group](https://github.com/openmobilityfoundation/curb-data-specification/wiki) + +Read about [how to become an OMF member](https://www.openmobilityfoundation.org/how-to-become-a-member/), [how to get involved and our governance model](https://www.openmobilityfoundation.org/how-to-get-involved-in-the-open-mobility-foundation/), and [contact us](https://mailchi.mp/openmobilityfoundation/membership) for more details. [Top][toc] # Versions -MDS has a **current release** (version 1.1.0), **previous releases** (both recommended and longer recommended for use), and **upcoming releases** in development. For a full list of releases, their status, recommended versions, and timelines, see the [Official MDS Releases](https://github.com/openmobilityfoundation/governance/wiki/Releases) page. +MDS has a **current release** (version 1.2.0), **previous releases** (both recommended and longer recommended for use), and **upcoming releases** in development. For a full list of releases, their status, recommended versions, and timelines, see the [Official MDS Releases](https://github.com/openmobilityfoundation/governance/wiki/Releases) page. The OMF provides guidance on upgrading for cities, providers, and software companies, and sample permit language for cities. See our [MDS Version Guidance](https://github.com/openmobilityfoundation/governance/blob/main/technical/OMF-MDS-Version-Guidance.md) for best practices on how and when to upgrade MDS as new versions become available. Our complimentary [MDS Policy Language Guidance](https://github.com/openmobilityfoundation/governance/blob/main/technical/OMF-MDS-Policy-Language-Guidance.md) document is for cities writing MDS into their operating policy and includes sample policy language. @@ -133,11 +134,13 @@ The MDS specification is versioned using Git tags and [semantic versioning](http # Cities Using MDS -More than 115 cities and public agencies around the world use MDS, and it has been implemented by most major [mobility service providers](#providers-using-mds). +More than 150 cities and public agencies across 6 continents around the world use MDS, and it has been implemented by most major [mobility service providers](#providers-using-mds). - See our **[list of cities using MDS](https://www.openmobilityfoundation.org/mds-users/#cities-using-mds)** with links to public mobility websites and policy/permit documents. Please let us know [via our website](https://www.openmobilityfoundation.org/get-in-touch/) or in the [public discussion area](https://github.com/openmobilityfoundation/mobility-data-specification/discussions) if you are an agency using MDS so we can add you to the city resource list, especially if you have published your policies or documents publicly. +To add yourself to the [agency list](/agencies.csv) and add your [Policy Requirement link](/provider.md#requirements), please let us know [via our website](https://www.openmobilityfoundation.org/get-in-touch/) or open an [Issue](https://github.com/openmobilityfoundation/mobility-data-specification/issues) or [Pull Request](https://github.com/openmobilityfoundation/mobility-data-specification/pulls). Find out how in our [Adding an Agency ID](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/Adding-an-MDS-Agency-ID) help document. + [Top][toc] # Providers Using MDS @@ -146,7 +149,7 @@ Over two dozen mobility service providers (MSPs) around the world use MDS, allow - See our **[list of providers using MDS](https://www.openmobilityfoundation.org/mds-users/#mobility-providers-using-mds)**. For a table list with unique IDs, see the MDS [provider list](/providers.csv). -To add yourself to the provider list, please let us know [via our website](https://www.openmobilityfoundation.org/get-in-touch/) or open an [Issue](https://github.com/openmobilityfoundation/mobility-data-specification/issues) or [Pull Request](https://github.com/openmobilityfoundation/mobility-data-specification/pulls). +To add yourself to the provider list, please let us know [via our website](https://www.openmobilityfoundation.org/get-in-touch/) or open an [Issue](https://github.com/openmobilityfoundation/mobility-data-specification/issues) or [Pull Request](https://github.com/openmobilityfoundation/mobility-data-specification/pulls). Find out how in our [Adding an Provider ID](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/Adding-an-MDS-Provider-ID) help document. [Top][toc] @@ -160,6 +163,22 @@ Please [let us know](https://www.openmobilityfoundation.org/get-in-touch/) if yo [Top][toc] +# Data Privacy + +MDS includes information about vehicles, their location, and trips taken on those vehicles to allow agencies to regulate their use in the public right of way and to conduct transportation planning and analysis. While MDS is not designed to convey personal information about the users of shared mobility services, data collected about mobility can be sensitive. The OMF and MDS community have created a number of resources to help cities, mobility providers, and software companies handle vehicle data safely: + +* [MDS Privacy Guide for Cities](https://github.com/openmobilityfoundation/governance/raw/main/documents/OMF-MDS-Privacy-Guide-for-Cities.pdf) - guide that covers essential privacy topics and best practices +* [The Privacy Principles for Mobility Data](https://www.mobilitydataprivacyprinciples.org/) - principles endorsed by the OMF and other mobility organizations to guide the mobility ecosystem in the responsible use of data and the protection of individual privacy +* [Mobility Data State of Practice](https://github.com/openmobilityfoundation/privacy-committee/blob/main/products/state-of-the-practice.md) - real-world examples related to the handling and protection of MDS and other types of mobility data +* [Understanding the Data in MDS](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/Understanding-the-Data-in-MDS) - technical document outlining what data is (and is not) in MDS +* [Use Case Database](https://www.openmobilityfoundation.org/whats-possible-with-mds/) - a starting point for understanding how MDS can be used, and what parts of MDS is required to meet those use cases +* [Policy Requirements](https://github.com/openmobilityfoundation/mobility-data-specification/tree/main/policy#requirement) - built into MDS, allowing agencies to specify only the endpoints and fields needed for program regulation +* Using MDS Under GDPR (link coming soon) - how to use MDS in the context of GDPR in Europe + +The OMF’s [Privacy, Security, and Transparency Committee](https://github.com/openmobilityfoundation/privacy-committee#welcome-to-the-privacy-security-and-transparency-committee) creates many of these resources, and advises the OMF on principles and practices that ensure the secure handling of mobility data. The committee – which is composed of both private and public sector OMF members – also holds regular public meetings, which provide additional resources and an opportunity to discuss issues related to privacy and mobility data. Learn more [here](https://github.com/openmobilityfoundation/privacy-committee#welcome-to-the-privacy-security-and-transparency-committee). + +[Top][toc] + # Use Cases How cities use MDS depends on a variety of factors: their transportation goals, existing services and infrastructure, and the unique needs of their communities. Cities are using MDS to create policy, enforce rules, manage hundreds of devices, and ensure the safe operation of vehicles in the public right of way. Some examples of how cities are using MDS in practice are: @@ -173,7 +192,7 @@ How cities use MDS depends on a variety of factors: their transportation goals, A list of use cases is useful to show what's possible with MDS, to list what other cities are accomplishing with the data, to see many use cases up front for privacy considerations, and to use for policy discussions and policy language. More details and examples can be seen on the [OMF website](https://www.openmobilityfoundation.org/whats-possible-with-mds/) and our [Wiki Database](https://github.com/openmobilityfoundation/governance/wiki/MDS-Use-Cases). -Please [let us know](https://www.openmobilityfoundation.org/get-in-touch/) if you have recommended updates or use cases to add. +Please [let us know](https://docs.google.com/forms/d/e/1FAIpQLScrMPgeb1TKMYCjjKsJh3y1TPTJO8HR_y1NByrf1rDmTEJS7Q/viewform) if you have recommended updates or use cases to add. [Top][toc] diff --git a/ReleaseNotes.md b/ReleaseNotes.md index ad2f6bf5..d93c3d45 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,3 +1,45 @@ +## 1.2.0 + +> Released: 2021-11-04 + +> [Release Plan](https://github.com/openmobilityfoundation/governance/wiki/Release-1.2.0) + +The 1.2.0 minor release adds digital program Requirements, new options for policies, better GPS telemetry data, and moves Provider Vehicles out of beta. + +### CHANGES + +See the closed PRs tagged with [Milestone 1.2.0](https://github.com/openmobilityfoundation/mobility-data-specification/pulls?q=is%3Apr+is%3Aclosed+milestone%3A1.2.0) and [Issues](https://github.com/openmobilityfoundation/mobility-data-specification/issues?q=is%3Aissue+milestone%3A1.2.0+is%3Aclosed) for a full list of changes. + +**_General MDS_** + +- [Richer telemetry data](https://github.com/openmobilityfoundation/mobility-data-specification/issues/589), including [616](https://github.com/openmobilityfoundation/mobility-data-specification/issues/616), [73](https://github.com/openmobilityfoundation/mobility-data-specification/pull/73), [51](https://github.com/openmobilityfoundation/mobility-data-specification/pull/51) +- [Add cargo_bicycle vehicle type](https://github.com/openmobilityfoundation/mobility-data-specification/pull/698) + +**_Policy_** + +- [Program Requirements](https://github.com/openmobilityfoundation/mobility-data-specification/issues/646) - For agencies to describe program requirements digitally to allow providers and the public to see what MDS and GBFS versions, APIs, endpoints, and fields are required, and communicate available MDS agency information to providers. + - [Ability to express data sharing requirements in Policy](https://github.com/openmobilityfoundation/mobility-data-specification/issues/608) + - [Method to Exclude some Provider Fields from Response](https://github.com/openmobilityfoundation/mobility-data-specification/issues/507) + - [Retrieve operational zones from operators](https://github.com/openmobilityfoundation/mobility-data-specification/issues/639) + - [Make Trip 'route' field optional for privacy](https://github.com/openmobilityfoundation/mobility-data-specification/issues/504) + +- [Multiple options added to Policy](https://github.com/openmobilityfoundation/mobility-data-specification/pull/658) + - [Add rate options to other rules types](https://github.com/openmobilityfoundation/mobility-data-specification/issues/633) + - [Support parking fees by duration](https://github.com/openmobilityfoundation/mobility-data-specification/issues/631) + - [Min and max clarity on Rules](https://github.com/openmobilityfoundation/mobility-data-specification/issues/689) + - [Add a "rate applies when" field to Rules](https://github.com/openmobilityfoundation/mobility-data-specification/issues/666) + +**_Provider_** + +- [Vehicles out of beta](https://github.com/openmobilityfoundation/mobility-data-specification/issues/637) +- [Clarify use cases between MDS Vehicles and GBFS](https://github.com/openmobilityfoundation/mobility-data-specification/issues/641) + +_Minor Updates_ + +- [Clarify single object response on policy/geography](https://github.com/openmobilityfoundation/mobility-data-specification/issues/599) +- [Schema updates](https://github.com/openmobilityfoundation/mobility-data-specification/issues/693), including [645](https://github.com/openmobilityfoundation/mobility-data-specification/issues/645), [687](https://github.com/openmobilityfoundation/mobility-data-specification/issues/687), [683](https://github.com/openmobilityfoundation/mobility-data-specification/issues/683) +- Add VeoRide, Boaz Bikes, and update Superpedestrian provider IDs + ## 1.1.1 > Released: 2021-09-24 diff --git a/agencies.csv b/agencies.csv new file mode 100644 index 00000000..efcd112d --- /dev/null +++ b/agencies.csv @@ -0,0 +1,21 @@ +agency_country_iso_code,agency_state,agency_city,agency_name,agency_id,department_url,requirement_url +CA,British Columbia,Kelowna,City of Kelowna,31e836fc-72da-4b2e-b553-8227d77a9b7a,https://www.kelowna.ca/roads-transportation/active-transportation/cycling/bikeshare-permit-program, +CO,Cundinamarca,Bogotá,Bogotá,85eac875-ab70-469f-8a85-cc5ef22e78d0,https://www.movilidadbogota.gov.co/web/, +DE,Baden-Württemberg,Ulm,Stadt Ulm,68f28fb8-d177-43f4-b7e8-a286fe0ddca0,https://www.ulm.de/de-de, +US,CA,Long Beach,City of Long Beach,188ed65d-a81e-40b9-b0b2-aeb85436610a,https://www.longbeach.gov/goactivelb/, +US,CA,Los Angeles,Los Angeles,d82d8584-dfa6-4396-93f0-5a36288b9eb1,https://ladot.lacity.org/projects/transportation-services/shared-mobility/micromobility, +US,CA,San Francisco,San Francisco,8e03693b-0153-446c-8bc0-c31f8a5a7ed8,https://www.sfmta.com/projects/powered-scooter-share-permit-and-pilot-program, +US,CA,San Jose,San Jose,801cb4b8-8725-449c-bdb9-7e9ac0e10b5b,https://www.sanjoseca.gov/your-government/departments-offices/transportation/micro-mobility, +US,CA,Santa Monica,Santa Monica,e4e7c0f6-c6aa-4919-a4de-565e4fe9fa57,https://www.smgov.net/Departments/PCD/Transportation/Shared-Mobility-Services/, +US,CO,Denver,Denver,c3d07b63-f602-4837-9525-7285f5ef286b,https://www.denvergov.org/content/denvergov/en/transportation-infrastructure/programs-services/dockless-mobility.html, +US,DC,Washington,District of Columbia,8aca1cf2-ffa5-46d8-b747-20cf00a7c7f1,https://ddot.dc.gov/page/dockless-vehicle-permits-district, +US,FL,Miami,City of Miami,5b36be11-7d5d-45ab-9c89-105cf6aa8645,https://www.miamigov.com/Services/Transportation/Miami-Scooter-Pilot-Program, +US,IL,Chicago,Chicago,d2ed9de6-2d2d-477c-a843-7d150d2310ed,https://www.chicago.gov/city/en/depts/cdot/supp_info/escooter-share-pilot-project.html, +US,KY,Louisville,Louisville Metro,44bc31a7-464b-4ed9-b52e-8e74630826bd,https://louisvilleky.gov/government/public-works/dockless-find-and-ride-vehicles, +US,MI,Detroit,Detroit,5814742e-78ba-4ac1-a628-c414ecc45448,https://detroitmi.gov/departments/department-public-works/complete-streets/scooters, +US,MN,Minneapolis,Minneapolis,88303227-48d6-4088-a690-65b4dcf381f7,http://www.minneapolismn.gov/publicworks/trans/WCMSP-212816, +US,OR,Portland,Portland,7d600eb6-f967-40ea-a212-33917f9b48ae,https://www.portlandoregon.gov/transportation/, +US,PA,Philadelphia,Philadelphia,fa2d0c5a-a716-473c-808b-be3b23e022ee,http://www.phillyotis.com/portfolio-item/dockless-bike-share-pilot/, +US,PA,Pittsburgh,Pittsburgh,f3c50422-7e3e-4efe-88c7-99da3b36c24d,https://pittsburghpa.gov/domi/bikeplan, +US,TX,Austin,City of Austin,296220ae-c90a-4383-9a97-0bc6cf1adf18,https://austintexas.gov/department/shared-mobility-services, +US,WA,Seattle,City of Seattle,9acf6e41-f145-49ad-be7f-d910e978fc36,https://www.seattle.gov/transportation/projects-and-programs/programs/bike-program/bike-share, diff --git a/agency/README.md b/agency/README.md index e1afb8fe..4f22cc93 100644 --- a/agency/README.md +++ b/agency/README.md @@ -184,9 +184,9 @@ Body Params: | ----------- | ----------------------------- | -------- | -------------------------------------------------------------------------------- | | `vehicle_state` | Enum | Required | see [Vehicle States][vehicle-states] | | `event_types` | Enum[] | Required | see [Vehicle Events][vehicle-events] | -| `timestamp` | [timestamp][ts] | Required | Date of last event update | +| `timestamp` | [timestamp][ts] | Required | Date of last event update | | `telemetry` | [Telemetry](#telemetry-data) | Required | Single point of telemetry. | -| `event_geographies` | UUID[] | Optional | **[Beta feature](/general-information.md#beta-features):** *Yes (as of 1.1.0)*. Array of Geography UUIDs consisting of every Geography that contains the location of the event. See [Geography Driven Events][geography-driven-events]. Required if `telemetry` is not present. | +| `event_geographies` | UUID[] | Optional | **[Beta feature](/general-information.md#beta-features):** *Yes (as of 1.1.0)*. Array of Geography UUIDs consisting of every Geography that contains the location of the event. See [Geography Driven Events][geography-driven-events]. Required if `telemetry` is not present. | | `trip_id` | UUID | Optional | UUID provided by Operator to uniquely identify the trip. Required if `event_types` contains `trip_start`, `trip_end`, `trip_cancel`, `trip_enter_jurisdiction`, or `trip_leave_jurisdiction` | 201 Success Response: @@ -223,7 +223,7 @@ Body Params: | Field | Type | Field Description | | ---------- | ------------------------------ | ------------------------------------------------------------------------------------------------------- | | `success` | Integer | Number of successfully written telemetry data points. | -| `total` | Integer | Ttotal number of provided points. | +| `total` | Integer | Total number of provided points. | | `failures` | [Telemetry](#telemetry-data)[] | Array of failed telemetry for zero or more vehicles (empty if all successful). | 400 Failure Response: @@ -244,14 +244,14 @@ A standard point of vehicle telemetry. References to latitude and longitude impl | Field | Type | Required/Optional | Field Description | | -------------- | -------------- | --------------------- | ------------------------------------------------------------ | | `device_id` | UUID | Required | ID used in [Register](#vehicle---register) | -| `timestamp` | [timestamp][ts] | Required | Date/time that event occurred. Based on GPS or GNSS clock | +| `timestamp` | [timestamp][ts]| Required | Date/time that event occurred. Based on GPS or GNSS clock | | `gps` | Object | Required | Telemetry position data | | `gps.lat` | Double | Required | Latitude of the location | | `gps.lng` | Double | Required | Longitude of the location | | `gps.altitude` | Double | Required if Available | Altitude above mean sea level in meters | | `gps.heading` | Double | Required if Available | Degrees - clockwise starting at 0 degrees at true North | -| `gps.speed` | Float | Required if Available | Speed in meters / sec | -| `gps.accuracy` | Float | Required if Available | Accuracy in meters | +| `gps.speed` | Float | Required if Available | Estimated speed in meters / sec as reported by the GPS chipset | +| `gps.accuracy` | Float | Required if Available | Horizontal accuracy, in meters | | `gps.hdop` | Float | Required if Available | Horizontal GPS or GNSS accuracy value (see [hdop][hdop]) | | `gps.satellites` | Integer | Required if Available | Number of GPS or GNSS satellites | `charge` | Float | Required if Applicable | Percent battery charge of vehicle, expressed between 0 and 1 | @@ -265,7 +265,7 @@ The `/stops` endpoint allows an agency to register city-managed Stops, or a prov **Endpoint:** `/stops` **Method:** `POST` -**[Beta feature][beta]:** Yes (as of 1.0.0) +**[Beta feature][beta]:** Yes (as of 1.0.0). [Leave feedback](https://github.com/openmobilityfoundation/mobility-data-specification/issues/638) **Request Body**: An array of [Stops][stops] 201 Success Response: @@ -287,7 +287,7 @@ _No content returned on success._ **Endpoint:** `/stops` **Method:** `PUT` -**[Beta feature][beta]:** Yes (as of 1.0.0) +**[Beta feature][beta]:** Yes (as of 1.0.0). [Leave feedback](https://github.com/openmobilityfoundation/mobility-data-specification/issues/638) **Request Body**: An array of subsets of [Stop][stops] information, where the permitted subset fields are defined as: | Field | Required/Optional | Description | @@ -313,7 +313,7 @@ _No content returned if no vehicle matching `stop_id` is found._ **Endpoint:** `/stops/:stop_id` **Method:** `GET` -**[Beta feature][beta]:** Yes (as of 1.0.0) +**[Beta feature][beta]:** Yes (as of 1.0.0). [Leave feedback](https://github.com/openmobilityfoundation/mobility-data-specification/issues/638) **Payload:** `{ "stops": [] }`, an array of [Stops][stops] Path Params: diff --git a/agency/get_stops.json b/agency/get_stops.json index f40271cd..a966091c 100644 --- a/agency/get_stops.json +++ b/agency/get_stops.json @@ -156,7 +156,7 @@ "type": "number", "description": "Integer milliseconds since Unix epoch", "multipleOf": 1.0, - "minimum": 0 + "minimum": 1514764800000 }, "uuid": { "$id": "#/definitions/uuid", @@ -186,6 +186,11 @@ "type": "integer", "minimum": 0 }, + "cargo_bicycle": { + "$id": "#/definitions/vehicle_type_counts/properties/cargo_bicycle", + "type": "integer", + "minimum": 0 + }, "car": { "$id": "#/definitions/vehicle_type_counts/properties/car", "type": "integer", @@ -225,6 +230,16 @@ "Feature" ] }, + "id": { + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, "properties": { "type": "object", "required": [ @@ -236,6 +251,30 @@ }, "stop_id": { "$ref": "#/definitions/uuid" + }, + "altitude": { + "type": "number", + "description": "Altitude above mean sea level in meters" + }, + "heading": { + "type": "number", + "description": "Degrees - clockwise starting at 0 degrees at true North" + }, + "speed": { + "type": "number", + "description": "Estimated speed in meters / sec as reported by the GPS chipset" + }, + "accuracy": { + "type": "number", + "description": "Horizontal accuracy, in meters" + }, + "hdop": { + "type": "number", + "description": "Horizontal GPS or GNSS accuracy value" + }, + "satellites": { + "type": "integer", + "description": "Number of GPS or GNSS satellites" } } }, diff --git a/agency/get_vehicle.json b/agency/get_vehicle.json index ff843457..d03929f6 100644 --- a/agency/get_vehicle.json +++ b/agency/get_vehicle.json @@ -30,7 +30,7 @@ "type": "number", "description": "Integer milliseconds since Unix epoch", "multipleOf": 1.0, - "minimum": 0 + "minimum": 1514764800000 }, "vehicle_type": { "$id": "#/definitions/vehicle_type", @@ -38,6 +38,7 @@ "description": "The type of vehicle", "enum": [ "bicycle", + "cargo_bicycle", "car", "scooter", "moped", @@ -280,6 +281,7 @@ "contains": { "enum": [ "agency_pick_up", + "comms_restored", "compliance_pick_up", "decommissioned", "located", diff --git a/agency/post_stops.json b/agency/post_stops.json index 75dd4662..570973b9 100644 --- a/agency/post_stops.json +++ b/agency/post_stops.json @@ -156,7 +156,7 @@ "type": "number", "description": "Integer milliseconds since Unix epoch", "multipleOf": 1.0, - "minimum": 0 + "minimum": 1514764800000 }, "uuid": { "$id": "#/definitions/uuid", @@ -186,6 +186,11 @@ "type": "integer", "minimum": 0 }, + "cargo_bicycle": { + "$id": "#/definitions/vehicle_type_counts/properties/cargo_bicycle", + "type": "integer", + "minimum": 0 + }, "car": { "$id": "#/definitions/vehicle_type_counts/properties/car", "type": "integer", @@ -225,6 +230,16 @@ "Feature" ] }, + "id": { + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, "properties": { "type": "object", "required": [ @@ -236,6 +251,30 @@ }, "stop_id": { "$ref": "#/definitions/uuid" + }, + "altitude": { + "type": "number", + "description": "Altitude above mean sea level in meters" + }, + "heading": { + "type": "number", + "description": "Degrees - clockwise starting at 0 degrees at true North" + }, + "speed": { + "type": "number", + "description": "Estimated speed in meters / sec as reported by the GPS chipset" + }, + "accuracy": { + "type": "number", + "description": "Horizontal accuracy, in meters" + }, + "hdop": { + "type": "number", + "description": "Horizontal GPS or GNSS accuracy value" + }, + "satellites": { + "type": "integer", + "description": "Number of GPS or GNSS satellites" } } }, diff --git a/agency/post_vehicle.json b/agency/post_vehicle.json index 2d1ec9e3..21d14ec2 100644 --- a/agency/post_vehicle.json +++ b/agency/post_vehicle.json @@ -31,6 +31,7 @@ "description": "The type of vehicle", "enum": [ "bicycle", + "cargo_bicycle", "car", "scooter", "moped", diff --git a/agency/post_vehicle_event.json b/agency/post_vehicle_event.json index 4ee2011e..284bc222 100644 --- a/agency/post_vehicle_event.json +++ b/agency/post_vehicle_event.json @@ -4,10 +4,27 @@ "title": "The MDS Agency Schema, POST vehicle status body", "type": "object", "definitions": { - "telemetry": { - "$id": "#/definitions/telemetry", + "timestamp": { + "$id": "#/definitions/timestamp", + "type": "number", + "description": "Integer milliseconds since Unix epoch", + "multipleOf": 1.0, + "minimum": 1514764800000 + }, + "uuid": { + "$id": "#/definitions/uuid", + "type": "string", + "description": "A UUID used to uniquely identifty an object", + "default": "", + "examples": [ + "3c9604d6-b5ee-11e8-96f8-529269fb1459" + ], + "pattern": "^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$" + }, + "vehicle_telemetry": { + "$id": "#/definitions/vehicle_telemetry", "type": "object", - "description": "A vehicle telemetry datum", + "description": "A telemetry datum for a specific vehicle at a specific time", "required": [ "device_id", "timestamp", @@ -47,15 +64,15 @@ }, "heading": { "type": "number", - "description": "Degrees clockwise from true north" + "description": "Degrees - clockwise starting at 0 degrees at true North" }, "speed": { "type": "number", - "description": "Speed in meters/sec" + "description": "Estimated speed in meters / sec as reported by the GPS chipset" }, "accuracy": { "type": "number", - "description": "Accuracy in meters" + "description": "Horizontal accuracy, in meters" }, "hdop": { "type": "number", @@ -69,29 +86,12 @@ }, "charge": { "type": "number", - "description": "Fraction of charge of the vehicle (required if applicable", + "description": "Fraction of charge of the vehicle (required if applicable)", "minimum": 0, "maximum": 1 } } }, - "timestamp": { - "$id": "#/definitions/timestamp", - "type": "number", - "description": "Integer milliseconds since Unix epoch", - "multipleOf": 1.0, - "minimum": 0 - }, - "uuid": { - "$id": "#/definitions/uuid", - "type": "string", - "description": "A UUID used to uniquely identifty an object", - "default": "", - "examples": [ - "3c9604d6-b5ee-11e8-96f8-529269fb1459" - ], - "pattern": "^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$" - }, "vehicle_state": { "$id": "#/definitions/vehicle_state", "type": "string", @@ -167,7 +167,7 @@ "$ref": "#/definitions/timestamp" }, "telemetry": { - "$ref": "#/definitions/telemetry" + "$ref": "#/definitions/vehicle_telemetry" }, "trip_id": { "$ref": "#/definitions/uuid" @@ -267,6 +267,7 @@ "contains": { "enum": [ "agency_pick_up", + "comms_restored", "compliance_pick_up", "decommissioned", "located", diff --git a/agency/post_vehicle_telemetry.json b/agency/post_vehicle_telemetry.json index 36040b8b..021c9d8e 100644 --- a/agency/post_vehicle_telemetry.json +++ b/agency/post_vehicle_telemetry.json @@ -4,10 +4,27 @@ "title": "The MDS Agency Schema, POST vehicle telemetry body", "type": "object", "definitions": { - "telemetry": { - "$id": "#/definitions/telemetry", + "timestamp": { + "$id": "#/definitions/timestamp", + "type": "number", + "description": "Integer milliseconds since Unix epoch", + "multipleOf": 1.0, + "minimum": 1514764800000 + }, + "uuid": { + "$id": "#/definitions/uuid", + "type": "string", + "description": "A UUID used to uniquely identifty an object", + "default": "", + "examples": [ + "3c9604d6-b5ee-11e8-96f8-529269fb1459" + ], + "pattern": "^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$" + }, + "vehicle_telemetry": { + "$id": "#/definitions/vehicle_telemetry", "type": "object", - "description": "A vehicle telemetry datum", + "description": "A telemetry datum for a specific vehicle at a specific time", "required": [ "device_id", "timestamp", @@ -47,15 +64,15 @@ }, "heading": { "type": "number", - "description": "Degrees clockwise from true north" + "description": "Degrees - clockwise starting at 0 degrees at true North" }, "speed": { "type": "number", - "description": "Speed in meters/sec" + "description": "Estimated speed in meters / sec as reported by the GPS chipset" }, "accuracy": { "type": "number", - "description": "Accuracy in meters" + "description": "Horizontal accuracy, in meters" }, "hdop": { "type": "number", @@ -69,28 +86,11 @@ }, "charge": { "type": "number", - "description": "Fraction of charge of the vehicle (required if applicable", + "description": "Fraction of charge of the vehicle (required if applicable)", "minimum": 0, "maximum": 1 } } - }, - "timestamp": { - "$id": "#/definitions/timestamp", - "type": "number", - "description": "Integer milliseconds since Unix epoch", - "multipleOf": 1.0, - "minimum": 0 - }, - "uuid": { - "$id": "#/definitions/uuid", - "type": "string", - "description": "A UUID used to uniquely identifty an object", - "default": "", - "examples": [ - "3c9604d6-b5ee-11e8-96f8-529269fb1459" - ], - "pattern": "^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$" } }, "required": [ @@ -100,7 +100,7 @@ "data": { "type": "array", "items": { - "$ref": "#/definitions/telemetry" + "$ref": "#/definitions/vehicle_telemetry" } } }, diff --git a/agency/put_stops.json b/agency/put_stops.json index 8e6f7639..5f510cdb 100644 --- a/agency/put_stops.json +++ b/agency/put_stops.json @@ -47,6 +47,11 @@ "type": "integer", "minimum": 0 }, + "cargo_bicycle": { + "$id": "#/definitions/vehicle_type_counts/properties/cargo_bicycle", + "type": "integer", + "minimum": 0 + }, "car": { "$id": "#/definitions/vehicle_type_counts/properties/car", "type": "integer", diff --git a/general-information.md b/general-information.md index c1042616..20fd7589 100644 --- a/general-information.md +++ b/general-information.md @@ -9,6 +9,7 @@ This document contains specifications that are shared between the various MDS AP * [Definitions](#definitions) * [Devices](#devices) * [Geographic Data][geo] + * [Geographic Telemetry Data](#geographic-telemetry-data) * [Stop-based Geographic Data](#stop-based-geographic-data) * [Intersection Operation](#intersection-operation) * [Geography-Driven Events](#geography-driven-events) @@ -34,11 +35,11 @@ This document contains specifications that are shared between the various MDS AP In some cases, features within MDS may be marked as "beta." These are typically recently added endpoints or fields. Because beta features are new, they may not yet be fully mature and proven in real-world operation. The design of beta features may have undiscovered gaps, ambiguities, or inconsistencies. Implementations of those features are typically also quite new and are more likely to contain bugs or other flaws. Beta features are likely to evolve more rapidly than other parts of the specification. -Despite this, MDS users are highly encouraged to use beta features. New features can only become proven and trusted through implementation, use, and the learning that comes with it. Users should be thoughtful about the role of beta features in their operations. Users of beta features are strongly encouraged to share their experiences, learnings, and challenges with the broader MDS community via GitHub issues or pull requests. This will inform the refinements that transform beta features into fully proven, stable parts of the specification. +Despite this, MDS users are highly encouraged to use beta features. New features can only become proven and trusted through implementation, use, and the learning that comes with it. Users should be thoughtful about the role of beta features in their operations. Users of beta features are strongly encouraged to share their experiences, learnings, and challenges with the broader MDS community via GitHub issues or pull requests. This will inform the refinements that transform beta features into fully proven, stable parts of the specification. You may leave feedback on the appropriate open [feedback issue](https://github.com/openmobilityfoundation/mobility-data-specification/issues?q=is%3Aissue+is%3Aopen+label%3Abeta) tagged with the `beta` label. Beta features may be suitable for enabling some new tools and analysis, but may not be appropriate for mission-critical applications or regulatory decisions where certainty and reliability are essential. In subsequent releases existing beta features may include breaking changes, even in a minor release. Note that [schemas](/schema) may not be defined for some beta features until they are promoted out of beta. -Working Groups and their Steering Committees are expected to review beta designated features with each release cycle and determine whether the feature has reached the level of stability and maturity needed to remove the beta designation. In a case where a beta feature fails to reach substantial adoption after an extended time, Working Group Steering Committees should discuss whether or not the feature should remain in the specification. +Working Groups and their Steering Committees are expected to review beta designated features and [feedback](https://github.com/openmobilityfoundation/mobility-data-specification/issues?q=is%3Aissue+is%3Aopen+label%3Abeta) with each release cycle and determine whether the feature has reached the level of stability and maturity needed to remove the beta designation. In a case where a beta feature fails to reach substantial adoption after an extended time, Working Group Steering Committees should discuss whether or not the feature should remain in the specification. [Top][toc] @@ -57,6 +58,7 @@ Defining terminology and abbreviations used throughout MDS. * **API** - Application Programming Interface - A function or set of functions that allow one software application to access or communicate with features of a different software application or service. * **API Endpoint** - A point at which an API connects with a software application or service. * **DOT** - Department of Transportation, usually a city-run agency. +* **Jurisdiction** - An agency’s area of legal authority to manage and regulate a mobility program in the real world. Note there is also an MDS API called [Jurisdiction](/jurisdiction), which is a way to digitally represent this. * **PROW** - Public Right of Way - the physical infrastructure reserved for transportation purposes, examples include sidewalks, curbs, bike lanes, transit lanes and stations, traffic lanes and signals, and public parking. [Top][toc] @@ -73,14 +75,30 @@ Additionally, `device_id` must remain constant for the device's lifetime of serv References to geographic datatypes (Point, MultiPolygon, etc.) imply coordinates encoded in the [WGS 84 (EPSG:4326)][wgs84] standard GPS or GNSS projection expressed as [Decimal Degrees][decimal-degrees]. When points are used, you may assume a 20 meter buffer around the point when needed. -Whenever an individual location coordinate measurement is presented, it must be -represented as a GeoJSON [`Feature`][geojson-feature] object with a corresponding [`timestamp`][ts] property and [`Point`][geojson-point] geometry: +### Geographic Telemetry Data + +Whenever a vehicle location coordinate measurement is presented, it must be represented as a GeoJSON [`Feature`][geojson-feature] object with a corresponding `properties` object with the following properties: + + +| Field | Type | Required/Optional | Field Description | +| -------------- | --------------- | --------------------- | ------------------------------------------------------------ | +| `timestamp` | [timestamp][ts] | Required | Date/time that event occurred. Based on GPS or GNSS clock | +| `altitude` | Double | Required if Available | Altitude above mean sea level in meters | +| `heading` | Double | Required if Available | Degrees - clockwise starting at 0 degrees at true North | +| `speed` | Float | Required if Available | Estimated speed in meters / sec as reported by the GPS chipset | +| `accuracy` | Float | Required if Available | Horizontal accuracy, in meters | +| `hdop` | Float | Required if Available | Horizontal GPS or GNSS accuracy value (see [hdop][hdop]) | +| `satellites` | Integer | Required if Available | Number of GPS or GNSS satellites | + +Example of a vehicle location GeoJSON [`Feature`][geojson-feature] object: ```json { "type": "Feature", "properties": { - "timestamp": 1529968782421 + "timestamp": 1529968782421, + "accuracy": 10, + "speed": 1.21 }, "geometry": { "type": "Point", @@ -125,9 +143,11 @@ For the purposes of this specification, the intersection of two geographic datat ## Geography-Driven Events -**[Beta feature](/general-information.md#beta-features):** *Yes (as of 1.1.0)* +**[Beta feature](/general-information.md#beta-features):** *Yes (as of 1.1.0)*. [Leave feedback](https://github.com/openmobilityfoundation/mobility-data-specification/issues/670) + +Geography-Driven Events (GDE) is a new MDS feature for Agencies to perform complete Policy compliance monitoring without precise location data. Geography-Driven Events describe individual vehicles in realtime – not just aggregate data. However, rather than receiving the exact location of a vehicle, Agencies receive information about the vehicle's current geographic region. The regions used for Geography-Driven Events correspond to the Geographies in an Agency's current Policy. In this way, the data-shared using Geography-Driven Events is matched to an Agency's particular regulatory needs. -Geography-Driven Events is a new MDS feature for Agencies to perform complete Policy compliance monitoring without precise location data. Geography-Driven Events describe individual vehicles in realtime – not just aggregate data. However, rather than receiving the exact location of a vehicle, Agencies receive information about the vehicle's current geographic region. The regions used for Geography-Driven Events correspond to the Geographies in an Agency's current Policy. In this way, the data-shared using Geography-Driven Events is matched to an Agency's particular regulatory needs. +See [this example](/policy/examples/requirements.md#geography-driven-events) for how to implement GDE using [Policy Requirements](/policy#requirement). Here's how it works in practice: @@ -143,15 +163,15 @@ Here's how it works in practice: *Agency adds rule disallowing parking on waterfront path, begins receiving data on events within area.* -Agencies that wish to use Geography-Driven Events do so by requiring a new `event_geographies` field in status events. When an Agency is using Geography-Driven Events, Providers must emit a new `changed_geographies` status event whenever a vehicle in a trip enters or leaves a Geography managed by a Policy. +Agencies that wish to use Geography-Driven Events do so by requiring a new `event_geographies` field in status events. When an Agency is using Geography-Driven Events, Providers must emit a new `changed_geographies` status event whenever a vehicle in a trip enters or leaves a Geography managed by a Policy. -During the Beta period for this feature, location and telemtry data remain required fields. This allows Aggencies to test Geography-Driven Events, measuring its accuracy and efficacy against regulatory systems based on precise location data. After the beta period, if Geography-Driven Events is deemed by OMF to be accurate and effective, the specification will evolve to allow cities to use Geography-Driven Events in lieu of location or telemtry data. +During the Beta period for this feature, location and telemetry data remain required fields. This allows Agencies to test Geography-Driven Events, measuring its accuracy and efficacy against regulatory systems based on precise location data. After the beta period, if Geography-Driven Events is deemed by the OMF to be accurate and effective, the specification will evolve to allow cities to use Geography-Driven Events in lieu of location or telemetry data. [Top][toc] ## Optional Authentication -Authorization of the Policy and Geography APIs is no longer required and will be deprecated in next major release with these endpoints becoming optionally private instead of optionally public. An agency may optionally decide to make both the Policy and Geography endpoints unauthenticated and public. This allows transparency for the public to see how the city is regulating, holds the city accountable for their policy decisions, and reduces the technical burden on providers to use these endpoints. A side benefit is that this allows third parties to ingest this information into their applications and services for public benefit. +Authorization of the Policy and Geography APIs is no longer required and will be deprecated in next major release with these endpoints (plus Jursidictions) becoming 'optionally private' instead of 'optionally public'. An agency may optionally decide to make the Policy and Geography endpoints, as well as Jursidictions, unauthenticated and public. This allows transparency for the public to see how the city is regulating, holds the city accountable for their policy decisions, and reduces the technical burden on providers to use these endpoints. A side benefit is that this allows third parties to ingest this information into their applications and services for public benefit. Note if implementing the beta feature [Geography Driven Events](/general-information.md#geography-driven-events), both Policy and Geography must be public. @@ -208,29 +228,29 @@ All String fields, such as `vehicle_id`, are limited to a maximum of 255 charact Stops describe vehicle trip start and end locations in a pre-designated physical place. They can vary from docking stations with or without charging, corrals with lock-to railings, or suggested parking areas marked with spray paint. Stops are used in both [Provider](/provider#stops) (including routes and event locations) and [Agency](/agency#stops) (including telemetry data). -| Field | Type | Required/Optional | Description | -|------------------------|-------------------------------------------------------------|-------------------|----------------------------------------------------------------------------------------------| -| stop_id | UUID | Required | Unique ID for stop | -| name | String | Required | Name of stop | -| last_reported | Timestamp | Required | Date/Time that the stop was last updated | -| location | GeoJSON [Point Feature](#stop-based-geographic-data) | Required | Simple centerpoint location of the Stop. The use of the optional `geography_id` is recommended to provide more detail. | -| status | [Stop Status](#stop-status) | Required | Object representing the status of the Stop. See [Stop Status](#stop-status). | -| capacity | {vehicle_type: number} | Required | Number of total places per vehicle_type | -| num_vehicles_available | {vehicle_type: number} | Required | How many vehicles are available per vehicle_type at this stop? | -| num_vehicles_disabled | {vehicle_type: number} | Required | How many vehicles are unavailable/reserved per vehicle_type at this stop? | -| provider_id | UUID | Optional | UUID for the Provider managing this stop. Null/undefined if managed by an Agency. See MDS [provider list](/providers.csv). | -| geography_id | UUID | Optional | Pointer to the [Geography](/geography) that represents the Stop geospatially via Polygon or MultiPolygon. | -| region_id | string | Optional | ID of the region where station is located, see [GBFS Station Information][gbfs-station-info] | -| short_name | String | Optional | Abbreviated stop name | -| address | String | Optional | Postal address (useful for directions) | -| post_code | String | Optional | Postal code (e.g. `10036`) | -| rental_methods | [Enum[]][gbfs-station-info] | Optional | List of payment methods accepted at stop, see [GBFS Rental Methods][gbfs-station-info] | -| cross_street | String | Optional | Cross street of where the station is located. | -| num_places_available | {vehicle_type: number} | Optional | How many places are free to be populated with vehicles at this stop? | -| num_places_disabled | {vehicle_type: number} | Optional | How many places are disabled and unable to accept vehicles at this stop? | -| parent_stop | UUID | Optional | Describe a basic hierarchy of stops (e.g.a stop inside of a greater stop) | -| devices | UUID[] | Optional | List of device_ids for vehicles which are currently at this stop | -| image_url | URL | Optional | Link to an image, photo, or diagram of the stop. Could be used by providers to help riders find or use the stop. | +| Field | Type | Required/Optional | Description | +|------------------------|-------------------------------------------------------|-------------------|-------------| +| stop_id | UUID | Required | Unique ID for stop | +| name | String | Required | Name of stop | +| last_reported | Timestamp | Required | Date/Time that the stop was last updated | +| location | GeoJSON [Point Feature](#stop-based-geographic-data) | Required | Simple centerpoint location of the Stop. The use of the optional `geography_id` is recommended to provide more detail. | +| status | [Stop Status](#stop-status) | Required | Object representing the status of the Stop. See [Stop Status](#stop-status). | +| capacity | {vehicle_type: number} | Required | Number of total places per vehicle_type | +| num_vehicles_available | {vehicle_type: number} | Required | How many vehicles are available per vehicle_type at this stop? | +| num_vehicles_disabled | {vehicle_type: number} | Required | How many vehicles are unavailable/reserved per vehicle_type at this stop? | +| provider_id | UUID | Optional | UUID for the Provider managing this stop. Null/undefined if managed by an Agency. See MDS [provider list](/providers.csv). | +| geography_id | UUID | Optional | Pointer to the [Geography](/geography) that represents the Stop geospatially via Polygon or MultiPolygon. | +| region_id | string | Optional | ID of the region where station is located, see [GBFS Station Information][gbfs-station-info] | +| short_name | String | Optional | Abbreviated stop name | +| address | String | Optional | Postal address (useful for directions) | +| post_code | String | Optional | Postal code (e.g. `10036`) | +| rental_methods | [Enum[]][gbfs-station-info] | Optional | List of payment methods accepted at stop, see [GBFS Rental Methods][gbfs-station-info] | +| cross_street | String | Optional | Cross street of where the station is located. | +| num_places_available | {vehicle_type: number} | Optional | How many places are free to be populated with vehicles at this stop? | +| num_places_disabled | {vehicle_type: number} | Optional | How many places are disabled and unable to accept vehicles at this stop? | +| parent_stop | UUID | Optional | Describe a basic hierarchy of stops (e.g.a stop inside of a greater stop) | +| devices | UUID[] | Optional | List of device_ids for vehicles which are currently at this stop | +| image_url | URL | Optional | Link to an image, photo, or diagram of the stop. Could be used by providers to help riders find or use the stop. | ### Stop Status @@ -269,7 +289,7 @@ A `timestamp` refers to integer milliseconds since Unix epoch. Object identifiers are described via Universally Unique Identifiers [(UUIDs)](https://en.wikipedia.org/wiki/Universally_unique_identifier). For example, the `device_id` field used to uniquely identify a vehicle is a UUID. -MDS uses Version 1 UUIDs. +MDS uses Version 1 UUIDs by default. Version 4 UUIDs may be used where noted. [Top][toc] @@ -279,24 +299,24 @@ This table describes the list of vehicle conditions that may be used by regulato In a multi-jurisdiction environment, the status of a vehicle is per-jurisdiction. For example, a vehicle may be in the `on_trip` status for a county that contains five cities, and also in the `on_trip` status for one of those cities, but `elsewhere` for the other four cities. In such a condition, generally a Provider would send the device data to the over-arching jurisdiction (the county) and the vehicle state with respect to each city would be determined by the Agency managing the jurisdictions. -| `vehicle_state` | In PROW? | Description | -| --- | --- | --- | -| `removed` | no | Examples include: at the Provider's warehouse, in a Provider's truck, or destroyed and in a landfill. | -| `available` | yes | Available for rental via the Provider's app. In PROW. | -| `non_operational` | yes | Not available for rent. Examples include: vehicle has low battery, or currently outside legal operating hours. | -| `reserved` | yes | Reserved via Provider's app, waiting to be picked up by a rider. | -| `on_trip` | yes | In possession of renter. May or may not be in motion. | -| `elsewhere` | no | Outside of regulator's jurisdiction, and thus not subject to cap-counts or other regulations. Example: a vehicle that started a trip in L.A. has transitioned to Santa Monica. | +| `vehicle_state` | In PROW? | Description | +| ----------------- | -------- | ----------- | +| `removed` | no | Examples include: at the Provider's warehouse, in a Provider's truck, or destroyed and in a landfill. | +| `available` | yes | Available for rental via the Provider's app. In PROW. | +| `non_operational` | yes | Not available for rent. Examples include: vehicle has low battery, or currently outside legal operating hours. | +| `reserved` | yes | Reserved via Provider's app, waiting to be picked up by a rider. | +| `on_trip` | yes | In possession of renter. May or may not be in motion. | +| `elsewhere` | no | Outside of regulator's jurisdiction, and thus not subject to cap-counts or other regulations. Example: a vehicle that started a trip in L.A. has transitioned to Santa Monica. | | `unknown` | unknown | Provider has lost contact with the vehicle and its disposition is unknown. Examples include: taken into a private residence, thrown in river. | [Top][toc] ### Event Types -Event types are the possible transitions bewteen some vehicle states. +Event types are the possible transitions between some vehicle states. | `event_type` | Description | -|---|---| +|--------------|-------------| | `agency_drop_off` | Drop off by the agency | | `agency_pick_up` | Pick up by the agency | | `battery_charged` | Battery charged | @@ -350,8 +370,8 @@ Note that to handle out-of-order events, the validity of the prior-state shall n Vehicles can enter the `unknown` state to and from any other state with the following event types: any state can go to `unknown` with event type `comms_lost`, `missing`, or `unspecified`, and `unknown` can go to any state with event type `comms_restored` of `unspecified`. -| Valid prior `vehicle_state` values | `vehicle_state` | `event_type` | Description | -| --- | --- | --- | --- | +| Valid prior `vehicle_state` values | `vehicle_state` | `event_type` | Description | +| ---------------------------------- | --------------- | ------------ | ----------- | | `non_operational` | `available` | `battery_charged` | The vehicle became available because its battery is now charged. | | `non_operational` | `available` | `on_hours` | The vehicle has entered operating hours (per the regulator or per the provider) | | `removed`, `unknown` | `available` | `provider_drop_off` | The vehicle was placed in the PROW by the provider | @@ -409,12 +429,13 @@ The *State Machine Diagram* shows how `vehicle_state` and `event_type` relate to The list of allowed `vehicle_type` values in MDS. Aligning with [GBFS vehicle types form factors](https://github.com/NABSA/gbfs/blob/master/gbfs.md#vehicle_typesjson-added-in-v21-rc). | `vehicle_type` | Description | -|--------------| --- | -| bicycle | Anything with pedals, including recumbents; can include powered assist | -| car | Any automobile | -| scooter | Any motorized mobility device intended for one rider | -| moped | A motorcycle/bicycle hybrid that can be powered or pedaled | -| other | A device that does not fit in the other categories | +|----------------| ----------- | +| bicycle | A two-wheeled mobility device intended for personal transportation that can be operated via pedals, with or without a motorized assist (includes e-bikes, recumbents, and tandems) | +| cargo_bicycle | A two- or three-wheeled bicycle intended for transporting larger, heavier cargo than a standard bicycle (such as goods or passengers), with or without motorized assist (includes bakfiets/front-loaders, cargo trikes, and long-tails) | +| car | A passenger car or similar light-duty vehicle | +| scooter | A standing or seated fully-motorized mobility device intended for one rider, capable of travel at low or moderate speeds, and suited for operation in infrastructure shared with motorized bicycles | +| moped | A seated fully-motorized mobility device capable of travel at moderate or high speeds and suited for operation in general urban traffic | +| other | A device that does not fit in the other categories | [Top][toc] @@ -429,7 +450,7 @@ The version parameter specifies the dot-separated combination of major and minor Clients must specify the version they are targeting through the `Accept` header. For example: ```http -Accept: application/vnd.mds+json;version=0.3 +Accept: application/vnd.mds+json;version=1.2.0 ``` Since versioning was not available from the start, the following APIs provide a fallback version if the `Accept` header is not set as specified above: @@ -444,6 +465,7 @@ If an unsupported or invalid version is requested, the API must respond with a s [agency]: /agency/README.md [decimal-degrees]: https://en.wikipedia.org/wiki/Decimal_degrees +[hdop]: https://en.wikipedia.org/wiki/Dilution_of_precision_(navigation) [gbfs-station-info]: https://github.com/NABSA/gbfs/blob/master/gbfs.md#station_informationjson [gbfs-station-status]: https://github.com/NABSA/gbfs/blob/master/gbfs.md#station_statusjson [general-stops]: /general-information.md#stops diff --git a/geography/README.md b/geography/README.md index a3b14f12..2699de0d 100644 --- a/geography/README.md +++ b/geography/README.md @@ -11,12 +11,12 @@ Geographical data will be stored as GeoJSON and read from either `geographies.js ## Table of Contents * [General Information](#general-information) - * [Versioning](#versioning) - * [Transition from Policy](#transition-from-policy) + * [Versioning](#versioning) + * [Transition from Policy](#transition-from-policy) * [Distribution](#distribution) - * [Flat Files](#flat-files) - * [Response Format](#response-format) - * [Authorization](#authorization) + * [Flat Files](#flat-files) + * [Response Format](#response-format) + * [Authorization](#authorization) * [Schema](#schema) * [Geography Fields](#geography-fields) * [Previous Geographies](#previous-geographies) @@ -45,7 +45,7 @@ Versioning must be implemented as specified in the [Versioning section][versioni To ensure this Geography API is not creating a breaking change for the 1.1.0 release, it's expected that the data contained in the [`/geographies`](/policy#geography) endpoint in the Policy API is identical to this Geography API. This will ensure that when a Geography ID is used anywhere in this release, the data could be retrieved from either location. -This temporary requirement is to ensure backwards compatibility, but the overall intent is to remove the /policy/geographies endpoint at the next major MDS release. +This temporary requirement is to ensure backwards compatibility, but the overall intent is to remove the /policy/geographies endpoint at the next major MDS release. [Top][toc] @@ -65,7 +65,7 @@ Geographies should be re-fetched at an agreed upon interval between providers an To use a flat file, geographies shall be represented in one (1) file equivalent to the /geographies endpoint: -- `geographies.json` +* `geographies.json` The files shall be structured like the output of the [REST endpoints](#rest-endpoints) above. @@ -89,7 +89,7 @@ Authorization is not required. An agency may decide to make this endpoint unauth ## Schema -Link to schema will be defined and added in a future release. +See the [Endpoints](#endpoints) below for links to their specific JSON Schema documents. [Top][toc] @@ -115,6 +115,8 @@ Obsoleting or otherwise changing a geography is accomplished by publishing a new This field is optional can be omitted by the publishing Agency. +[Top][toc] + ### Geography Type Type of geography. These specific types are recommendations based on ones commonly defined by agencies. Others may be created by the Agency as needed, or the optional `geography_type` field may be omitted. @@ -148,16 +150,17 @@ Type of geography. These specific types are recommendations based on ones common Note: to use flat files rather than REST endpoints, Geography objects should be stored in `geographies.json`. The `geographies.json` file will look like the output of `GET /geographies`. Example `geographies.json` -```json + +```jsonc { - "version": "1.1.0", + "version": "1.2.0", "updated": "1570035222868", "geographies": [ { - // GeoJSON 1 + // Geography 1 }, { - // GeoJSON 2 + // Geography 2 } ] } @@ -173,26 +176,26 @@ The Geography Author API consists of the following endpoints: ### Geography -**Endpoint**: `/geographies/{geography_id}` - -**Method**: `GET` +**Endpoint**: `/geographies/{geography_id}` +**Method**: `GET` +**Schema:** [`geography` schema](./geography.json) -Path Params: +#### Query Parameters | Name | Type | Required/Optional | Description | | ------------- | ---- | --- | --------------------------------------------------- | -| geography_id | UUID | Required | Unique identifier for a single specific Geography | +| `geography_id` | UUID | Required | Unique identifier for a single specific Geography | -Returns: Details of a single Geography based on a UUID. +Returns: Details of a single Geography based on a UUID. Response body: ```js { - "version": '1.1.0', + "version": '1.2.0', "geography": { "geography_id": UUID, - "geography_type": Enum, + "geography_type": string, "name": string, "description": string, "published_date": timestamp, @@ -204,31 +207,27 @@ Response body: ``` Response codes: -- 200 - success -- 401 - unauthorized -- 404 - no geography found -- 403 - user is attempting to read an unpublished geography, but only has the `geographies:read:published` scope. + +* 200 - success +* 401 - unauthorized +* 404 - no geography found +* 403 - user is attempting to read an unpublished geography, but only has the `geographies:read:published` scope. [Top][toc] ### Geographies -**Endpoint**: `/geographies` - -**Method**: `GET` - -Path Params: - -| Name | Type | Required/Optional | Description | -| ------------ | --------- | --- | ---------------------------------------------- | -| `summary` | string | Optional | Return geographies, including the GeoJSON in each geography object | +**Endpoint**: `/geographies` +**Method**: `GET` +**Schema:** [`geographies` schema](./geographies.json) Returns: All geography objects Response body: -```js + +```jsonc { - "version": "1.1.0", + "version": "1.2.0", "updated": "1570035222868", "geographies": [ { @@ -242,8 +241,9 @@ Response body: ``` Response codes: -- 200 - success -- 401 - unauthorized + +* 200 - success +* 401 - unauthorized [Top][toc] diff --git a/geography/examples/README.md b/geography/examples/README.md index 5b9c2036..dee77f62 100644 --- a/geography/examples/README.md +++ b/geography/examples/README.md @@ -32,7 +32,7 @@ Shows the muncipal boundaries of a regulating entity, which may be larger than t ```json { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "e00535dd-d8ff-4b1b-920d-34e7404d0208", "geography_type": "municipal_boundary", @@ -60,7 +60,7 @@ Boundaries of a city's permitted operating area for provider vehicles. ```json { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "8ad39dc3-005b-4348-9d61-c830c54c161b", "geography_type": "operating_area", @@ -89,7 +89,7 @@ Boundaries of one of 9 areas in a city where vehicles can be distibuted and rebl ```json { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "70a91abc-0d9f-43a9-8e6a-763142dc6c94", "geography_type": "distribution_zone", @@ -119,7 +119,7 @@ Boundaries of areas in a city where vehicles are not allowed be ridden by riders ```json { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "fc277865-79d3-4f0e-8459-53e9a647db99", "geography_type": "slow_ride_zone", @@ -144,7 +144,7 @@ Boundaries of areas in a city where vehicles are to be ridden at a slower top sp ```json { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "8ad39dc3-005b-4348-9d61-c830c54c161b", "geography_type": "operating_area", @@ -174,7 +174,7 @@ Designated stoping areas for vehicles. In this example the recommended parking l ```json { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "d1328cdb-92fe-4267-85e0-a9fe5653268e", "geography_type": "stop", diff --git a/geography/examples/distribution-zone-8.json b/geography/examples/distribution-zone-8.json index 3da278a4..8d4203a3 100644 --- a/geography/examples/distribution-zone-8.json +++ b/geography/examples/distribution-zone-8.json @@ -1,5 +1,5 @@ { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "70a91abc-0d9f-43a9-8e6a-763142dc6c94", "geography_type": "distribution_zone", diff --git a/geography/examples/geographies.json b/geography/examples/geographies.json index 854d2484..67a1020d 100644 --- a/geography/examples/geographies.json +++ b/geography/examples/geographies.json @@ -1,9 +1,9 @@ { - "version": "1.1.0", + "version": "1.2.0", "updated": "1570035222868", "geographies": [ { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "e00535dd-d8ff-4b1b-920d-34e7404d0208", "geography_type": "municipal_boundary", @@ -208,7 +208,7 @@ } }, { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "8ad39dc3-005b-4348-9d61-c830c54c161b", "geography_type": "operating_area", @@ -15450,7 +15450,7 @@ } }, { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "70a91abc-0d9f-43a9-8e6a-763142dc6c94", "geography_type": "distribution_zone", @@ -17290,7 +17290,7 @@ } }, { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "e00535dd-d8ff-4b1b-920d-34e7404d0208", "geography_type": "no_ride_zone", @@ -17844,7 +17844,7 @@ } }, { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "fc277865-79d3-4f0e-8459-53e9a647db99", "geography_type": "slow_ride_zone", @@ -19855,7 +19855,7 @@ } }, { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "d1328cdb-92fe-4267-85e0-a9fe5653268e", "geography_type": "stop", @@ -20001,4 +20001,4 @@ } } ] -} \ No newline at end of file +} diff --git a/geography/examples/municipal-boundary.json b/geography/examples/municipal-boundary.json index 29240ada..bc45f7f3 100644 --- a/geography/examples/municipal-boundary.json +++ b/geography/examples/municipal-boundary.json @@ -1,5 +1,5 @@ { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "e00535dd-d8ff-4b1b-920d-34e7404d0208", "geography_type": "municipal_boundary", @@ -202,4 +202,4 @@ ] } } -} \ No newline at end of file +} diff --git a/geography/examples/no-ride-zone.json b/geography/examples/no-ride-zone.json index d1821d90..601bfc7c 100644 --- a/geography/examples/no-ride-zone.json +++ b/geography/examples/no-ride-zone.json @@ -1,5 +1,5 @@ { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "e00535dd-d8ff-4b1b-920d-34e7404d0208", "geography_type": "no_ride_zone", @@ -551,4 +551,4 @@ ] } } -} \ No newline at end of file +} diff --git a/geography/examples/operating-area.json b/geography/examples/operating-area.json index 4de24220..5493c179 100644 --- a/geography/examples/operating-area.json +++ b/geography/examples/operating-area.json @@ -1,5 +1,5 @@ { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "8ad39dc3-005b-4348-9d61-c830c54c161b", "geography_type": "operating_area", diff --git a/geography/examples/slow-ride-zone.json b/geography/examples/slow-ride-zone.json index 278a13e4..f665d2e2 100644 --- a/geography/examples/slow-ride-zone.json +++ b/geography/examples/slow-ride-zone.json @@ -1,5 +1,5 @@ { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "fc277865-79d3-4f0e-8459-53e9a647db99", "geography_type": "slow_ride_zone", @@ -2008,4 +2008,4 @@ ] } } -} \ No newline at end of file +} diff --git a/geography/examples/stop.json b/geography/examples/stop.json index bea9c9f3..fc0b1464 100644 --- a/geography/examples/stop.json +++ b/geography/examples/stop.json @@ -1,5 +1,5 @@ { - "version": "1.0.0", + "version": "1.2.0", "geography": { "geography_id": "d1328cdb-92fe-4267-85e0-a9fe5653268e", "geography_type": "stop", @@ -143,4 +143,4 @@ ] } } -} \ No newline at end of file +} diff --git a/geography/geographies.json b/geography/geographies.json new file mode 100644 index 00000000..d1ebbc76 --- /dev/null +++ b/geography/geographies.json @@ -0,0 +1,163 @@ +{ + "$id": "https://raw.githubusercontent.com/openmobilityfoundation/mobility-data-specification/main/geography/geographies.json", + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "The MDS Geographies Schema", + "type": "object", + "definitions": { + "geography": { + "$id": "#/definitions/geography", + "type": "object", + "title": "The geography object schema", + "additionalProperties": false, + "required": [ + "name", + "geography_id", + "geography_json", + "published_date" + ], + "properties": { + "name": { + "$id": "#/definitions/geography/properties/name", + "$ref": "#/definitions/string", + "description": "Name of geography" + }, + "description": { + "$id": "#/definitions/geography/properties/description", + "$ref": "#/definitions/string", + "description": "Description of geography" + }, + "geography_type": { + "$id": "#/definitions/geography/properties/geography_type", + "type": "string", + "description": "The type of geography" + }, + "geography_id": { + "$id": "#/definitions/geography/properties/geography_id", + "$ref": "#/definitions/uuid", + "description": "Unique ID of geography" + }, + "geography_json": { + "$id": "#/definitions/geography/properties/geography_json", + "$ref": "https://geojson.org/schema/FeatureCollection.json", + "description": "The GeoJSON FeatureCollection that defines the geographical coordinates" + }, + "effective_date": { + "$id": "#/definitions/geography/properties/effective_date", + "$ref": "#/definitions/null_timestamp", + "description": "The date at which a Geography is considered 'live'. Must be at or after published_date." + }, + "published_date": { + "$id": "#/definitions/geography/properties/published_date", + "$ref": "#/definitions/timestamp", + "description": "Timestamp at which the geography was published i.e. made immutable" + }, + "retire_date": { + "$id": "#/definitions/geography/properties/end_date", + "$ref": "#/definitions/null_timestamp", + "description": "Time that the geography is slated to retire. Once the retire date is passed, new policies can no longer reference it and old policies referencing it should be updated. Retired geographies should continue to be returned in the geographies list. Must be after effective_date." + }, + "prev_geographies": { + "$id": "#/definitions/geography/properties/prev_geographies", + "$ref": "#/definitions/null_uuid_array", + "description": "Unique IDs of prior geographies replaced by this one", + "uniqueItems": true + } + } + }, + "string": { + "$id": "#/definitions/string", + "type": "string", + "description": "A length-limited string type", + "maxLength": 255, + "default": "", + "examples": [ + "ABC123" + ], + "pattern": "^(.*)$" + }, + "timestamp": { + "$id": "#/definitions/timestamp", + "type": "number", + "description": "Integer milliseconds since Unix epoch", + "multipleOf": 1.0, + "minimum": 1514764800000 + }, + "uuid": { + "$id": "#/definitions/uuid", + "type": "string", + "description": "A UUID used to uniquely identifty an object", + "default": "", + "examples": [ + "3c9604d6-b5ee-11e8-96f8-529269fb1459" + ], + "pattern": "^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$" + }, + "version": { + "$id": "#/definitions/version", + "type": "string", + "description": "The version of MDS this data represents", + "examples": [ + "1.2.0" + ], + "pattern": "^1\\.2\\.[0-9]+$" + }, + "uuid_array": { + "$id": "#/definitions/uuid_array", + "type": "array", + "description": "Array of UUID", + "items": { + "$id": "#/definitions/uuid_array/items", + "$ref": "#/definitions/uuid" + } + }, + "null_timestamp": { + "$id": "#/definitions/null_timestamp", + "type": [ + "number", + "null" + ], + "description": "Integer milliseconds since Unix epoch", + "multipleOf": 1.0, + "minimum": 1514764800000 + }, + "null_uuid_array": { + "$id": "#/definitions/null_uuid_array", + "type": [ + "array", + "null" + ], + "description": "Array of UUID", + "items": { + "$id": "#/definitions/uuid_array/items", + "$ref": "#/definitions/uuid" + } + } + }, + "required": [ + "geographies", + "updated", + "version" + ], + "properties": { + "geographies": { + "$id": "#/properties/geographies", + "type": "array", + "title": "The array of policy objects in this payload", + "items": { + "$id": "#/properties/geographies/items", + "$ref": "#/definitions/geography" + } + }, + "updated": { + "$id": "#/properties/updated", + "$ref": "#/definitions/timestamp", + "description": "The timestamp when geographies was last updated" + }, + "version": { + "$id": "#/properties/version", + "$ref": "#/definitions/version", + "description": "The version of MDS that the geographies represents" + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/geography/geography.json b/geography/geography.json new file mode 100644 index 00000000..661263f8 --- /dev/null +++ b/geography/geography.json @@ -0,0 +1,154 @@ +{ + "$id": "https://raw.githubusercontent.com/openmobilityfoundation/mobility-data-specification/main/geography/geography.json", + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "The MDS Geography Schema", + "type": "object", + "definitions": { + "geography": { + "$id": "#/definitions/geography", + "type": "object", + "title": "The geography object schema", + "additionalProperties": false, + "required": [ + "name", + "geography_id", + "geography_json", + "published_date" + ], + "properties": { + "name": { + "$id": "#/definitions/geography/properties/name", + "$ref": "#/definitions/string", + "description": "Name of geography" + }, + "description": { + "$id": "#/definitions/geography/properties/description", + "$ref": "#/definitions/string", + "description": "Description of geography" + }, + "geography_type": { + "$id": "#/definitions/geography/properties/geography_type", + "type": "string", + "description": "The type of geography" + }, + "geography_id": { + "$id": "#/definitions/geography/properties/geography_id", + "$ref": "#/definitions/uuid", + "description": "Unique ID of geography" + }, + "geography_json": { + "$id": "#/definitions/geography/properties/geography_json", + "$ref": "https://geojson.org/schema/FeatureCollection.json", + "description": "The GeoJSON FeatureCollection that defines the geographical coordinates" + }, + "effective_date": { + "$id": "#/definitions/geography/properties/effective_date", + "$ref": "#/definitions/null_timestamp", + "description": "The date at which a Geography is considered 'live'. Must be at or after published_date." + }, + "published_date": { + "$id": "#/definitions/geography/properties/published_date", + "$ref": "#/definitions/timestamp", + "description": "Timestamp at which the geography was published i.e. made immutable" + }, + "retire_date": { + "$id": "#/definitions/geography/properties/end_date", + "$ref": "#/definitions/null_timestamp", + "description": "Time that the geography is slated to retire. Once the retire date is passed, new policies can no longer reference it and old policies referencing it should be updated. Retired geographies should continue to be returned in the geographies list. Must be after effective_date." + }, + "prev_geographies": { + "$id": "#/definitions/geography/properties/prev_geographies", + "$ref": "#/definitions/null_uuid_array", + "description": "Unique IDs of prior geographies replaced by this one", + "uniqueItems": true + } + } + }, + "string": { + "$id": "#/definitions/string", + "type": "string", + "description": "A length-limited string type", + "maxLength": 255, + "default": "", + "examples": [ + "ABC123" + ], + "pattern": "^(.*)$" + }, + "timestamp": { + "$id": "#/definitions/timestamp", + "type": "number", + "description": "Integer milliseconds since Unix epoch", + "multipleOf": 1.0, + "minimum": 1514764800000 + }, + "uuid": { + "$id": "#/definitions/uuid", + "type": "string", + "description": "A UUID used to uniquely identifty an object", + "default": "", + "examples": [ + "3c9604d6-b5ee-11e8-96f8-529269fb1459" + ], + "pattern": "^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$" + }, + "version": { + "$id": "#/definitions/version", + "type": "string", + "description": "The version of MDS this data represents", + "examples": [ + "1.2.0" + ], + "pattern": "^1\\.2\\.[0-9]+$" + }, + "uuid_array": { + "$id": "#/definitions/uuid_array", + "type": "array", + "description": "Array of UUID", + "items": { + "$id": "#/definitions/uuid_array/items", + "$ref": "#/definitions/uuid" + } + }, + "null_timestamp": { + "$id": "#/definitions/null_timestamp", + "type": [ + "number", + "null" + ], + "description": "Integer milliseconds since Unix epoch", + "multipleOf": 1.0, + "minimum": 1514764800000 + }, + "null_uuid_array": { + "$id": "#/definitions/null_uuid_array", + "type": [ + "array", + "null" + ], + "description": "Array of UUID", + "items": { + "$id": "#/definitions/uuid_array/items", + "$ref": "#/definitions/uuid" + } + } + }, + "required": [ + "geography", + "version" + ], + "properties": { + "geography": { + "$id": "#/properties/geography", + "$ref": "#/definitions/geography", + "description": "The geography in this payload", + "additionalProperties": false + }, + "version": { + "$id": "#/properties/version", + "$ref": "#/definitions/version", + "description": "The version of MDS that the geography represents" + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/geography/get_geography.json b/geography/get_geography.json deleted file mode 100644 index 83a5e2f6..00000000 --- a/geography/get_geography.json +++ /dev/null @@ -1,1038 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "The MDS Geography Author Schema, GET /geographies/{id} payload", - "definitions": { - "GeoJSON.Feature": { - "description": "A feature object which contains a geometry and associated properties.\nhttps://tools.ietf.org/html/rfc7946#section-3.2", - "properties": { - "bbox": { - "anyOf": [ - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 4, - "type": "array" - }, - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 6, - "type": "array" - } - ], - "description": "Bounding box of the coordinate range of the object's Geometries, Features, or Feature Collections.\nThe value of the bbox member is an array of length 2*n where n is the number of dimensions\nrepresented in the contained geometries, with all axes of the most southwesterly point\nfollowed by all axes of the more northeasterly point.\nThe axes order of a bbox follows the axes order of geometries.\nhttps://tools.ietf.org/html/rfc7946#section-5" - }, - "geometry": { - "anyOf": [ - { - "$ref": "#/definitions/GeoJSON.Point" - }, - { - "$ref": "#/definitions/GeoJSON.MultiPoint" - }, - { - "$ref": "#/definitions/GeoJSON.LineString" - }, - { - "$ref": "#/definitions/GeoJSON.MultiLineString" - }, - { - "$ref": "#/definitions/GeoJSON.Polygon" - }, - { - "$ref": "#/definitions/GeoJSON.MultiPolygon" - }, - { - "$ref": "#/definitions/GeoJSON.GeometryCollection" - } - ], - "description": "The feature's geometry" - }, - "id": { - "description": "A value that uniquely identifies this feature in a\nhttps://tools.ietf.org/html/rfc7946#section-3.2.", - "type": [ - "string", - "number" - ] - }, - "properties": { - "additionalProperties": {}, - "description": "Properties associated with this feature.", - "type": "object" - }, - "type": { - "description": "Specifies the type of GeoJSON object.", - "enum": [ - "Feature" - ], - "type": "string" - } - }, - "type": "object" - }, - "GeoJSON.FeatureCollection": { - "description": "A collection of feature objects.\n https://tools.ietf.org/html/rfc7946#section-3.3", - "properties": { - "bbox": { - "anyOf": [ - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 4, - "type": "array" - }, - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 6, - "type": "array" - } - ], - "description": "Bounding box of the coordinate range of the object's Geometries, Features, or Feature Collections.\nThe value of the bbox member is an array of length 2*n where n is the number of dimensions\nrepresented in the contained geometries, with all axes of the most southwesterly point\nfollowed by all axes of the more northeasterly point.\nThe axes order of a bbox follows the axes order of geometries.\nhttps://tools.ietf.org/html/rfc7946#section-5" - }, - "features": { - "items": { - "$ref": "#/definitions/GeoJSON.Feature" - }, - "type": "array" - }, - "type": { - "description": "Specifies the type of GeoJSON object.", - "enum": [ - "FeatureCollection" - ], - "type": "string" - } - }, - "type": "object" - }, - "GeoJSON.GeometryCollection": { - "description": "Geometry Collection\nhttps://tools.ietf.org/html/rfc7946#section-3.1.8", - "properties": { - "bbox": { - "anyOf": [ - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 4, - "type": "array" - }, - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 6, - "type": "array" - } - ], - "description": "Bounding box of the coordinate range of the object's Geometries, Features, or Feature Collections.\nThe value of the bbox member is an array of length 2*n where n is the number of dimensions\nrepresented in the contained geometries, with all axes of the most southwesterly point\nfollowed by all axes of the more northeasterly point.\nThe axes order of a bbox follows the axes order of geometries.\nhttps://tools.ietf.org/html/rfc7946#section-5" - }, - "geometries": { - "items": { - "anyOf": [ - { - "$ref": "#/definitions/GeoJSON.Point" - }, - { - "$ref": "#/definitions/GeoJSON.MultiPoint" - }, - { - "$ref": "#/definitions/GeoJSON.LineString" - }, - { - "$ref": "#/definitions/GeoJSON.MultiLineString" - }, - { - "$ref": "#/definitions/GeoJSON.Polygon" - }, - { - "$ref": "#/definitions/GeoJSON.MultiPolygon" - }, - { - "$ref": "#/definitions/GeoJSON.GeometryCollection" - } - ] - }, - "type": "array" - }, - "type": { - "description": "Specifies the type of GeoJSON object.", - "enum": [ - "GeometryCollection" - ], - "type": "string" - } - }, - "type": "object" - }, - "GeoJSON.LineString": { - "description": "LineString geometry object.\nhttps://tools.ietf.org/html/rfc7946#section-3.1.4", - "properties": { - "bbox": { - "anyOf": [ - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 4, - "type": "array" - }, - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 6, - "type": "array" - } - ], - "description": "Bounding box of the coordinate range of the object's Geometries, Features, or Feature Collections.\nThe value of the bbox member is an array of length 2*n where n is the number of dimensions\nrepresented in the contained geometries, with all axes of the most southwesterly point\nfollowed by all axes of the more northeasterly point.\nThe axes order of a bbox follows the axes order of geometries.\nhttps://tools.ietf.org/html/rfc7946#section-5" - }, - "coordinates": { - "items": { - "items": { - "type": "number" - }, - "type": "array" - }, - "type": "array" - }, - "type": { - "description": "Specifies the type of GeoJSON object.", - "enum": [ - "LineString" - ], - "type": "string" - } - }, - "type": "object" - }, - "GeoJSON.MultiLineString": { - "description": "MultiLineString geometry object.\nhttps://tools.ietf.org/html/rfc7946#section-3.1.5", - "properties": { - "bbox": { - "anyOf": [ - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 4, - "type": "array" - }, - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 6, - "type": "array" - } - ], - "description": "Bounding box of the coordinate range of the object's Geometries, Features, or Feature Collections.\nThe value of the bbox member is an array of length 2*n where n is the number of dimensions\nrepresented in the contained geometries, with all axes of the most southwesterly point\nfollowed by all axes of the more northeasterly point.\nThe axes order of a bbox follows the axes order of geometries.\nhttps://tools.ietf.org/html/rfc7946#section-5" - }, - "coordinates": { - "items": { - "items": { - "items": { - "type": "number" - }, - "type": "array" - }, - "type": "array" - }, - "type": "array" - }, - "type": { - "description": "Specifies the type of GeoJSON object.", - "enum": [ - "MultiLineString" - ], - "type": "string" - } - }, - "type": "object" - }, - "GeoJSON.MultiPoint": { - "description": "MultiPoint geometry object.\n https://tools.ietf.org/html/rfc7946#section-3.1.3", - "properties": { - "bbox": { - "anyOf": [ - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 4, - "type": "array" - }, - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 6, - "type": "array" - } - ], - "description": "Bounding box of the coordinate range of the object's Geometries, Features, or Feature Collections.\nThe value of the bbox member is an array of length 2*n where n is the number of dimensions\nrepresented in the contained geometries, with all axes of the most southwesterly point\nfollowed by all axes of the more northeasterly point.\nThe axes order of a bbox follows the axes order of geometries.\nhttps://tools.ietf.org/html/rfc7946#section-5" - }, - "coordinates": { - "items": { - "items": { - "type": "number" - }, - "type": "array" - }, - "type": "array" - }, - "type": { - "description": "Specifies the type of GeoJSON object.", - "enum": [ - "MultiPoint" - ], - "type": "string" - } - }, - "type": "object" - }, - "GeoJSON.MultiPolygon": { - "description": "MultiPolygon geometry object.\nhttps://tools.ietf.org/html/rfc7946#section-3.1.7", - "properties": { - "bbox": { - "anyOf": [ - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 4, - "type": "array" - }, - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 6, - "type": "array" - } - ], - "description": "Bounding box of the coordinate range of the object's Geometries, Features, or Feature Collections.\nThe value of the bbox member is an array of length 2*n where n is the number of dimensions\nrepresented in the contained geometries, with all axes of the most southwesterly point\nfollowed by all axes of the more northeasterly point.\nThe axes order of a bbox follows the axes order of geometries.\nhttps://tools.ietf.org/html/rfc7946#section-5" - }, - "coordinates": { - "items": { - "items": { - "items": { - "items": { - "type": "number" - }, - "type": "array" - }, - "type": "array" - }, - "type": "array" - }, - "type": "array" - }, - "type": { - "description": "Specifies the type of GeoJSON object.", - "enum": [ - "MultiPolygon" - ], - "type": "string" - } - }, - "type": "object" - }, - "GeoJSON.Point": { - "description": "Point geometry object.\nhttps://tools.ietf.org/html/rfc7946#section-3.1.2", - "properties": { - "bbox": { - "anyOf": [ - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 4, - "type": "array" - }, - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 6, - "type": "array" - } - ], - "description": "Bounding box of the coordinate range of the object's Geometries, Features, or Feature Collections.\nThe value of the bbox member is an array of length 2*n where n is the number of dimensions\nrepresented in the contained geometries, with all axes of the most southwesterly point\nfollowed by all axes of the more northeasterly point.\nThe axes order of a bbox follows the axes order of geometries.\nhttps://tools.ietf.org/html/rfc7946#section-5" - }, - "coordinates": { - "description": "A Position is an array of coordinates.\nhttps://tools.ietf.org/html/rfc7946#section-3.1.1\nArray should contain between two and three elements.\nThe previous GeoJSON specification allowed more elements (e.g., which could be used to represent M values),\nbut the current specification only allows X, Y, and (optionally) Z to be defined.", - "items": { - "type": "number" - }, - "type": "array" - }, - "type": { - "description": "Specifies the type of GeoJSON object.", - "enum": [ - "Point" - ], - "type": "string" - } - }, - "type": "object" - }, - "GeoJSON.Polygon": { - "description": "Polygon geometry object.\nhttps://tools.ietf.org/html/rfc7946#section-3.1.6", - "properties": { - "bbox": { - "anyOf": [ - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 4, - "type": "array" - }, - { - "additionalItems": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ] - }, - "items": [ - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - }, - { - "type": "number" - } - ], - "minItems": 6, - "type": "array" - } - ], - "description": "Bounding box of the coordinate range of the object's Geometries, Features, or Feature Collections.\nThe value of the bbox member is an array of length 2*n where n is the number of dimensions\nrepresented in the contained geometries, with all axes of the most southwesterly point\nfollowed by all axes of the more northeasterly point.\nThe axes order of a bbox follows the axes order of geometries.\nhttps://tools.ietf.org/html/rfc7946#section-5" - }, - "coordinates": { - "items": { - "items": { - "items": { - "type": "number" - }, - "type": "array" - }, - "type": "array" - }, - "type": "array" - }, - "type": { - "description": "Specifies the type of GeoJSON object.", - "enum": [ - "Polygon" - ], - "type": "string" - } - }, - "type": "object" - } - }, - "properties": { - "geography_id": { - "examples": [ - "3c9604d6-b5ee-11e8-96f8-529269fb1459" - ], - "format": "uuid", - "pattern": "^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$", - "type": "string" - }, - "geography_json": { - "$ref": "#/definitions/GeoJSON.FeatureCollection" - } - }, - "type": "object" -} diff --git a/jurisdiction/README.md b/jurisdiction/README.md index 911c3f2f..ea9ab373 100644 --- a/jurisdiction/README.md +++ b/jurisdiction/README.md @@ -2,9 +2,7 @@ MDS Jurisdiction Icon -This specification details the purpose, use cases, and schema for Jurisdictions, a beta feature. Jurisdictions are an optional service that, if implemented, must be authenticated, and served by a coordinated group of agencies. - -The Jursidiction API is to be served by agencies, and it can optionally be made public. It serves a different purpose from the [Geography](/geography) API, though it does reference areas within Geography by ID. +This specification details the purpose, use cases, and schema for Jurisdictions. Jurisdictions are an optional service that are served by a coordinated group of agencies. Jurisdictions can be '[optionally authenticated](/general-information.md#optional-authentication)', making the endpoints unauthenticated and public. In the future this will shift to 'optionally private', where Jursidictions will be public by default. Note that it serves a different purpose from the [Geography](/geography) API, though it does reference areas within Geography by ID. ## Table of Contents @@ -39,6 +37,8 @@ A jurisdiction is: The Jurisdictions API and all of its endpoints are marked as a [beta feature](https://github.com/openmobilityfoundation/mobility-data-specification/blob/feature-metrics/general-information.md#beta-features) starting in the 1.1.0 release. It has not been tested in real world scenarios, and may be adjusted in future releases. +**[Beta feature](https://github.com/openmobilityfoundation/mobility-data-specification/blob/feature-metrics/general-information.md#beta-features)**: _Yes (as of 1.1.0)_. [Leave feedback](https://github.com/openmobilityfoundation/mobility-data-specification/issues/673) + ## Use Cases ### 1. Defining boundaries and what the vehicle state `elsewhere` means @@ -162,6 +162,8 @@ The publishing Agency should establish and communicate to interested parties how The `updated` field in the payload wrapper should be set to the time of publishing a revision, so that it is simple to identify a changed file. +[Top](#table-of-contents) + ## Examples See the [Jurisdiction Examples](examples/README.md) for a sample `jurisdictions.json` file. diff --git a/jurisdiction/examples/README.md b/jurisdiction/examples/README.md index cbb8e976..947545d2 100644 --- a/jurisdiction/examples/README.md +++ b/jurisdiction/examples/README.md @@ -6,7 +6,7 @@ This file presents an example of how to format a flat file, should an agency pre ```js { - "version": "1.1.0", + "version": "1.2.0", "updated": "1570035222868", "end_date": "1570035222868", "jurisdictions": diff --git a/jurisdiction/examples/jurisdictions.json b/jurisdiction/examples/jurisdictions.json index 5f4e9d52..9e54b15d 100644 --- a/jurisdiction/examples/jurisdictions.json +++ b/jurisdiction/examples/jurisdictions.json @@ -1,9 +1,9 @@ { - "version": "1.1.0", + "version": "1.2.0", "updated": "1570035222868", "end_date": "1570035222868", - "jurisdictions": + "jurisdictions": [ { "jurisdiction_id": "240edf69-9f2b-457c-accf-e8156e78811f", diff --git a/metrics/README.md b/metrics/README.md index 9a4762b8..22e5d0cf 100644 --- a/metrics/README.md +++ b/metrics/README.md @@ -68,7 +68,7 @@ Metrics may be a supplement for other more granular MDS data, and may be used to ## Beta Feature -[Beta feature](https://github.com/openmobilityfoundation/mobility-data-specification/blob/feature-metrics/general-information.md#beta-features): Yes (as of 1.0.0) +**[Beta feature](https://github.com/openmobilityfoundation/mobility-data-specification/blob/feature-metrics/general-information.md#beta-features)**: _Yes (as of 1.0.0)_. [Leave feedback](https://github.com/openmobilityfoundation/mobility-data-specification/issues/671) The Metrics API and all of its endpoints are marked as a [beta feature](https://github.com/openmobilityfoundation/mobility-data-specification/blob/feature-metrics/general-information.md#beta-features). It has not been tested in real world scenarios, and may be adjusted in future releases. diff --git a/policy/README.md b/policy/README.md index 7de2bae6..65476f09 100644 --- a/policy/README.md +++ b/policy/README.md @@ -10,11 +10,14 @@ This specification describes the digital relationship between _mobility as a ser - [General Information](#general-information) - [Versioning](#versioning) - - [Update Frequency](#update-frequency) + - [Update Frequency](#update-frequency) - [Background](#background) - [Distribution](#distribution) - - [REST Endpoints](#rest-endpoints) - - [Flat Files](#flat-files) +- [REST Endpoints](#rest-endpoints) + - [Policies](#policies) + - [Geographies](#geographies) + - [Requirements](#requirements) +- [Flat Files](#flat-files) - [Schema](#schema) - [Policy](#policy) - [Rules](#rules) @@ -25,7 +28,17 @@ This specification describes the digital relationship between _mobility as a ser - [Messages](#messages) - [Value URL](#value-url) - [Order of Operations](#order-of-operations) - + - [Requirement](#requirement) + - [Update Frequency](#requirement-update-frequency) + - [Public Hosting](#public-hosting) + - [Version Tracking](#version-tracking) + - [Beta Limitations](#beta-limitations) + - [Format](#requirement-format) + - [Metadata](#requirement-metadata) + - [Programs](#requirement-programs) + - [Data Specs](#requirement-data-specs) + - [APIs](#requirement-apis) + ## General information The following information applies to all `policy` API endpoints. @@ -48,7 +61,7 @@ Versioning must be implemented as specified in the [Versioning section][versioni ## Background -The goal of this specification is to enable Agencies to create, revise, and publish machine-readable policies, as sets of rules for individual and collective device behavior exhibited by both _mobility as a service_ Providers and riders / users. [Examples](./examples/README.md) of policies include: +The goal of the Policy API specification is to enable agencies to create, revise, and publish machine-readable policies, as sets of rules for individual and collective device behavior exhibited by both _mobility as a service_ providers and riders / users. [Examples](./examples/README.md) of policies include: - City-wide and localized caps (e.g. "Minimum 500 and maximum 3000 scooters within city boundaries") - Exclusion zones (e.g. "No scooters are permitted in this district on weekends") @@ -57,7 +70,7 @@ The goal of this specification is to enable Agencies to create, revise, and publ - Idle-time and disabled-time limitations (e.g. "5 days idle while rentable, 12 hours idle while unrentable, per device") - Trip fees and subsidies (e.g. "A 25 cent fee applied when a trip ends downtown") -The machine-readable format allows Providers to obtain policies and compute compliance where it can be determined entirely by data obtained internally. +The machine-readable format allows Providers to obtain policies and compute compliance where it can be determined entirely by data obtained internally, and know what data is required from them and provided to them. **See the [Policy Examples](./examples/README.md) for ways these can be implemented.** @@ -82,7 +95,7 @@ Flat files have an optional `end_date` field that will apply to the file as a wh [Top][toc] -### REST Endpoints +## REST Endpoints Among other use-cases, configuring a REST API allows an Agency to: @@ -93,7 +106,7 @@ Among other use-cases, configuring a REST API allows an Agency to: Responses must set the `Content-Type` header, as specified in the [versioning][versioning] section. -#### Responses and Error Messages +### Responses and Error Messages The response to a client request must include a valid HTTP status code defined in the [IANA HTTP Status Code Registry][iana]. @@ -101,15 +114,16 @@ See the [Responses section][responses] for information on valid MDS response cod ### Authorization -Authorization is not required. An agency may decide to make this endpoint unauthenticated and public. See [Optional Authentication](/general-information.md#optional-authentication) for details. +Authorization is not required and agencies are encouraged to make these endpoints unauthenticated and public. See [Optional Authentication](/general-information.md#optional-authentication) for details. -#### Policies +### Policies -Endpoint: `/policies/{id}` -Method: `GET` -`data` Payload: `{ "policies": [] }`, an array of objects with the structure [outlined below](#policy). +**Endpoint**: `/policies/{id}` +**Method**: `GET` +**Schema:** [`policy` schema][json-schema] +**`data` Payload**: `{ "policies": [] }`, an array of objects with the structure [outlined below](#policy). -##### Query Parameters +#### Query Parameters | Name | Type | Required / Optional | Description | | ------------ | --------- | --- | ---------------------------------------------- | @@ -123,15 +137,16 @@ Policies will be returned in order of effective date (see schema below), with pa `provider_id` is an implicit parameter and will be encoded in the authentication mechanism, or a complete list of policies should be produced. If the Agency decides that Provider-specific policy documents should not be shared with other Providers (e.g. punitive policy in response to violations), an Agency should filter policy objects before serving them via this endpoint. -#### Geographies +### Geographies -**Note:** see the new [Geography API](/geography#transition-from-policy) to understand the transisiton away from this endpoint, and how to support both in the MDS 1.1.0 release. +**Deprecated:** see the new [Geography API](/geography#transition-from-policy) to understand the transistion away from this endpoint, and how to support both in MDS 1.x.0 releases. -Endpoint: `/geographies/{id}` -Method: `GET` -`data` Payload: `{ geographies: [] }`, an array of GeoJSON `Feature` objects that follow the schema [outlined here](#geography) or in [Geography](/geography#general-information). +**Endpoint**: `/geographies/{id}` +**Method**: `GET` +**Schema:** [`policy` schema][json-schema] +**`data` Payload**: `{ geographies: [] }`, an array of GeoJSON `Feature` objects that follow the schema [outlined here](#geography) or in [Geography](/geography#general-information). -##### Query Parameters +#### Query Parameters | Name | Type | Required / Optional | Description | | ------------ | --------- | --- | ---------------------------------------------- | @@ -139,8 +154,19 @@ Method: `GET` [Top][toc] +### Requirements + +**Endpoint**: `/requirements/` +**Method**: `GET` +**[Beta feature](/general-information.md#beta-features)**: *Yes (as of 1.2.0)*. [Leave feedback](https://github.com/openmobilityfoundation/mobility-data-specification/issues/682) +**Schema:** TBD when out of beta +**`data` Payload**: `{ requirements: [] }`, JSON objects that follow the schema [outlined here](#requirement). + +See [Policy Requirements Examples](/policy/examples/requirements.md) for how this can be implemented. -### Flat Files +[Top][toc] + +## Flat Files To use flat files, policies shall be represented in two (2) files: @@ -153,7 +179,7 @@ The publishing Agency should establish and communicate to providers how frequent The `updated` field in the payload wrapper should be set to the time of publishing a revision, so that it is simple to identify a changed file. -#### Example `policies.json` +### Example `policies.json` ```jsonc { @@ -175,7 +201,7 @@ The `updated` field in the payload wrapper should be set to the time of publishi The optional `end_date` field applies to all policies represented in the file. -#### Example `geographies.json` +### Example `geographies.json` ```jsonc { @@ -255,10 +281,13 @@ An individual `Rule` object is defined by the following fields: | `propulsion_types` | `propulsion_type[]` | Optional | Applicable vehicle [propulsion types][propulsion-types], default "all". | | `minimum` | integer | Optional | Minimum value, if applicable (default 0) | | `maximum` | integer | Optional | Maximum value, if applicable (default unlimited) | -| `rate_amount` | integer | Optional | The amount of a rate applied when this rule applies, if applicable (default zero). A positive integer rate amount represents a fee, while a negative integer represents a subsidy. Rate amounts are given in the `currency` defined in the [Policy](#policy). | +| `inclusive_minimum` | boolean | Optional | Whether the rule `minimum` is considered in-bounds (default `true`) | +| `inclusive_maximum` | boolean | Optional | Whether the rule `maximum` is considered in-bounds (default `true`) | +| `rate_amount` | integer | Optional | Amount of the rate (see [Rate Amounts](#rate-amounts)) | | `rate_recurrence` | enum | Optional | Recurrence of the rate (see [Rate Recurrences](#rate-recurrences)) | -| `start_time` | ISO 8601 time `hh:mm:ss` | Optional | Beginning time-of-day when the rule is in effect (default 00:00:00). | -| `end_time` | ISO 8601 time `hh:mm:ss` | Optional | Ending time-of-day when the rule is in effect (default 23:59:59). | +| `rate_applies_when` | enum | Optional | Specifies when a rate is applied to a rule (see [Rate Applies When](#rate-applies-when)) (defaults to `out_of_bounds`) | +| `start_time` | ISO 8601 time `hh:mm:ss` | Optional | Beginning time-of-day when the rule is in effect (default 00:00:00). | +| `end_time` | ISO 8601 time `hh:mm:ss` | Optional | Ending time-of-day when the rule is in effect (default 23:59:59). | | `days` | day[] | Optional | Days `["sun", "mon", "tue", "wed", "thu", "fri", "sat"]` when the rule is in effect (default all) | | `messages` | `{ String:String }` | Optional | Message to rider user, if desired, in various languages, keyed by language tag (see [Messages](#messages)) | | `value_url` | URL | Optional | URL to an API endpoint that can provide dynamic information for the measured value (see [Value URL](#value-url)) | @@ -270,7 +299,7 @@ An individual `Rule` object is defined by the following fields: | Name | Description | | ------- | ------------------------------------------------------------------------------------------------------------- | | `count` | Fleet counts based on regions. Rule `minimum`/`maximum` refers to number of devices in [Rule Units](#rule-units). | -| `time` | Individual limitations on time spent in one or more vehicle-states. Rule `minimum`/`maximum` refers to increments of time in [Rule Units](#rule-units). | +| `time` | Individual limitations or fees based upon time spent in one or more vehicle-states. Rule `minimum`/`maximum` refers to increments of time in [Rule Units](#rule-units). | | `speed` | Global or local speed limits. Rule `minimum`/`maximum` refers to speed in [Rule Units](#rule-units). | | `rate` | **[Beta feature](/general-information.md#beta-features):** *Yes (as of 1.0.0)*. Fees or subsidies based on regions and time spent in one or more vehicle-states. Rule `rate_amount` refers to the rate charged according to the [Rate Recurrences](#rate_recurrences) and the [currency requirements](/general-information.md#costs-and-currencies) in [Rule Units](#rule-units). *Prior to implementation agencies should consult with providers to discuss how the `rate` rule will be used. Most agencies do this as a matter of course, but it is particularly important to communicate in advance how frequently and in what ways rates might change over time.* | | `user` | Information for users, e.g. about helmet laws. Generally can't be enforced via events and telemetry. | @@ -296,6 +325,8 @@ An individual `Rule` object is defined by the following fields: ### Geography +**Deprecated:** see the new [Geography API](/geography#transition-from-policy) to understand the transistion away from this endpoint, and how to support both in a MDS 1.x.0 release. + | Name | Type | Required / Optional | Description | | ---------------- | --------- | --- | ----------------------------------------------------------------------------------- | | `name` | String | Required | Name of geography | @@ -308,18 +339,40 @@ An individual `Rule` object is defined by the following fields: [Top][toc] -### Rate Recurrences +### Rates +Rate-related properties can currently be specified on `rate` and `time` Rules. Note: A future MDS version will likely support rates for `count` and `speed` rules, but their behavior is currently undefined. -Rate recurrences specify when a rate is applied – either once, or periodically according to a `time_unit` specified using [Rule Units](#rule-units). A `time_unit` refers to a unit of time as measured in local time for the juristiction – a day begins at midnight local time, an hour begins at the top of the hour, etc. +**[Beta feature](/general-information.md#beta-features)**: *Yes (as of 1.0.0)*. [Leave feedback](https://github.com/openmobilityfoundation/mobility-data-specification/issues/674) -| Name | Description | -| --------- | ------------------- | -| `once` | Rate is applied once to vehicles entering a matching status from a non-matching status. | -| `each_time_unit` | During each `time_unit`, rate is applied once to vehicles entering or remaining in a matching status. Requires a `time_unit` to be specified using `rule_units`. | -| `per_complete_time_unit` | Rate is applied once per complete `time_unit` that vehicles remain in a matching status. Requires a `time_unit` to be specified using `rule_units`. | +#### Rate Amounts +The amount of a rate applied when this rule applies, if applicable (default zero). A positive integer rate amount represents a fee, while a negative integer represents a subsidy. Rate amounts are given in the `currency` defined in the [Policy](#policy). + +#### Rate Recurrences + +Rate recurrences specify how a rate is applied – either once, or periodically according to a `time_unit` specified using [Rule Units](#rule-units). A `time_unit` refers to a unit of time as measured in local time for the jurisdiction – a day begins at midnight local time, an hour begins at the top of the hour, etc. + +| Name | Description | +| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `once_on_match` | Rate is applied once when a vehicle transitions **into** a matching status from a non-matching status. | +| `once_on_unmatch` | Rate is applied once a vehicle transitions **out of** a matching status to a non-matching status. | +| `each_time_unit` | During each `time_unit`, rate is applied once to vehicles entering or remaining in a matching status. Requires a `time_unit` to be specified using `rule_units`. | +| `per_complete_time_unit` | Rate is applied once per complete `time_unit` that vehicles remain in a matching status. Requires a `time_unit` to be specified using `rule_units`. | [Top][toc] +#### Rate Applies When + +The `rate_applies_when` field specifies when a rate should be applied to an event or count, +e.g. is it when the event is within the Rule bounds or when it is outside? +It defaults to `out_of_bounds`. + +The `rate_applies_when` field may take the following values: + +| Name | Description | +| --------------- | ----------- | +| `in_bounds` | Rate applies when an event or count is within the rule `minimum` and `maximum` | +| `out_of_bounds` | Rate applies when an event or count is outside of the rule `minimum` and `maximum` | + ### Messages Some Policies as established by the Agency may benefit from rider communication. This optional field contains a map of languages to messages, to be shown to the user. @@ -365,12 +418,293 @@ If a vehicle is matched with a rule, then it _will not_ be considered in the sub The internal mechanics of ordering are up to the Policy editing and hosting software. +[Top][toc] + +### Requirement + +A public agency's Policy program Requirements endpoint enumerates all of the parts of MDS, GBFS, and other specifications that an agency requires from providers for certain programs, including APIs, endpoints, and optional fields, as well as information for providers about the APIs the agency is hosting. The program requirements are specific to the needs and use cases of each agency, and ensure there is clarity on what data is being asked for in operating policy documents from providers, reducing the burden on both. This also allows additional public transparency and accountability around data requirements from agencies, and encourages privacy by allowing agencies to ask for only the data they need. + +Requirements can also be used to define a scaled-down MDS implementation in situations where an agency has more limited regulatory goals, has legal limitations on the types of data they can collect, or wants to use a lightweight version of MDS for a pilot project or other experiment where aspects of a full MDS implementation would be irrelevant or unnecessary. + +See [Policy Requirements Examples](/policy/examples/requirements.md) for ideas on how this can be implemented. + +#### Public Hosting + +This endpoint is not authenticated (ie. public), and allows the discovery of other public endpoints within Geography, Policy, and Jurisdiction. The agency can host this as a file or dynamic endpoint on their servers, on a third party server, or the OMF can host on behalf of an agency in the [agency program requirements repo](https://github.com/openmobilityfoundation/agency-program-requirements). See this [hosting guidance document](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/Policy-Requirements-OMF-Hosting-Guidance) for more information. This requirements file can be [referenced directly](https://github.com/openmobilityfoundation/governance/blob/main/technical/OMF-MDS-Policy-Language-Guidance.md) in an agency's operating permit/policy document when discussing program data requirements, and [updated digitally as needed](#requirement-update-frequency). To be compliant with MDS you must obtain an `agency_id` and list your public URL in [agencies.csv](/agencies.csv), per our [guidance document](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/Adding-an-MDS-Agency-ID). + +#### Requirement Update Frequency + +The OMF recommends updating the Requirements feed no more than monthly, and you may specify your expected timeframe with the `max_update_interval` in the [metadata](#requirement-metadata) section so providers have some idea of how often to check the feed. More specifically the OMF recommends giving the following notice to providers: 1 month for optional field additions, 3 months for endpoint/API changes/additions, 3 months for new minor releases, and 4 months for major releases. You should also communicate these future changes ahead of time with the `start_date` field. Finally, the OMF recommends any changes need to be part of a discussion between agencies and affected providers. + +#### Version Tracking + +If you are upgrading to a new MDS version, it is recommended to create a new requirements file at a new URL, since field names and available options may have changed. To make this more obvious, the MDS version number could be part of your URL, e.g. "https://mds.cityname.gov/requirements/1.2.0". + +When requirements are updated within the same MDS version, in the [metadata](#requirement-metadata) section, increment the `file_version` value by one and update the `last_updated` timestamp. Though not required, you may choose to use the `start_date` and `end_date` fields in the [programs](#requirement-programs) section to keep retired requirements accessible. We also recommend hosting your requirements file in a location that has a publicly-accessible version history, like GitHub or Bitbucket, or keeping previous versions accessible with a versioned URL, e.g. "https://mds.cityname.gov/requirements/1.2.0/v3". + +#### Beta Limitations + +Note that while Requirements is in [beta](#Requirements) in this **minor**, non-breaking MDS 1.2.0 release, items listed as "required" or "disallowed" will be treated as a _request_ only by default (precluding intentional formal agency communications with providers) to prevent an _unintentional_ burden on providers. For the next **major**, breaking MDS 2.0.0 release, these items will be required or disallowed as documented. + +#### Requirement Format + +An agency's program [Requirements](#requirements) endpoint contains a number of distinct parts, namely [metadata](#requirement-metadata), [program definitions](#requirement-programs), and [data specs](#requirement-data-specs) (with sub sections on relevant [required APIs](#requirement-apis)). + +```jsonc +{ + "metadata": { + // metadata fields per the "Requirement Metadata" section + }, + "programs" [ + { + "description" : "[PROGRAM DESCRIPTION]", + "provider_ids": [ + // provider id array + ], + "vehicle_types": [ + // optional vehicle_type array + ], + "start_date": [timestamp], + "end_date": [timestamp], + "required_data_specs": [ + { + "data_spec_name": "[NAME OF DATA SPEC]", + "version": "[VERSION NUMBER]", + "required_apis": [ + { + "api_name" : "[API NAME]": { + // Data spec endpoints, urls, optional fields + } + ] + } + }, + // other data specs per the "Requirement Data Specs" section + ] + }, + // other MDS versions per the "Requriement MDS Version" section + } +} +``` + +| Name | Type | Required / Optional | Description | +| ---------------------------- | --------------- | -------- | ----------------------------------- | +| `metadata` | Array | Required | Array of [Requirement Metadata](#requirement-metadata) fields. | +| `programs` | Array | Required | Array of [Requirement Programs](#requirement-programs) data. | +| `required_data_specs` | Array | Required | Array of [Requirement Data Specs](#requirement-data-specs) data. | +| `required_apis` | Array | Required | Array of [Requirement APIs](#requirement-apis) data. | + +[Top][toc] + +#### Requirement Metadata + +Contains metadata applicable to the agency and at the top of its [Requirement](#requirement) data feed in the `metadata` section. + +```jsonc +{ + "metadata": { + "mds_release": "[TEXT]", + "file_version": "[INTEGER]", + "last_updated": "[TIMESTAMP]", + "max_update_interval": "[DURATION]", + "agency_id": "[UUID]", + "agency_name": "[TEXT]", + "agency_timezone": "[TIMEZONE]", + "agency_language": "[TEXT]", + "agency_currency": "[TEXT]", + "agency_website_url": "[URL]" + "url": "[URL]" + }, + "mds_versions" [ + // Requirement MDS Versions + ] +} +``` + +| Name | Type | Required / Optional | Description | +| ---------------------------- | --------------- | -------- | ----------------------------------- | +| `mds_release` | text | Required | Release of MDS that the **requirements data feed** aligns to, based on official MDS releases. E.g. "1.2.0" | +| `file_version` | integer | Required | Version of this file. Increment 1 with each modification. See [version tracking](#version-tracking) for details. E.g. "3" | +| `last_updated` | [timestamp][ts] | Required | When this file `version` was last updated. See [version tracking](#version-tracking) for details. E.g. "1611958740" | +| `max_update_interval` | duration | Required | The expected maximum frequency with which this file could be updated. [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). E.g. "P1M" | +| `agency_id` | UUID | Required | UUID of the agency this file applies to. Must come from [agencies.csv](/agencies.csv) file, per [guidance](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/Adding-an-MDS-Agency-ID). E.g. "737a9c62-c0cb-4c93-be43-271d21b784b5" | +| `agency_name` | text | Required | Name of the agency this file applies to. E.g. "Louisville Metro" | +| `agency_timezone` | timezone | Required | [TZ Database Name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) used for dates and times in Requirements and across all MDS endpoints. E.g. "America/New_York" | +| `agency_language` | text | Required | An [IETF BCP 47](https://www.rfc-editor.org/rfc/bcp/bcp47.txt) language code, used across all MDS endpoints. E.g. "en-US" | +| `agency_currency` | text | Required | Currency used for all monetary values across all MDS endpoints. E.g. "USD" | +| `agency_website_url` | URL | Required | URL of the agency's general transportation page. E.g. "https://www.cityname.gov/transportation/" | +| `url` | URL | Required | URL of this file. Must be added to [agencies.csv](/agencies.csv), per [guidance](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/Adding-an-MDS-Agency-ID). E.g. "https://mds.cityname.gov/requirements/1.2.0" | + +[Top][toc] + +#### Requirement Programs + +Contains information about an agency's programs, with links to policy documents, and a list of providers and data specs/APIs/endpoints/fields that the program applies to over a certain time frame in its [Requirement](#requirement) data feed in the `required_data_specs` section. + +Unique combinations for data specs, specific providers, vehicle types, policies, and dates (past, current, or future) can be defined. For example an agency can define MDS version 1.2.0 and GBFS 2.2 for Provider #1 in a pilot with beta endpoints and optional fields, MDS version 1.2.0 for other providers without beta features starting a month from now, and MDS version 1.1.0 for Provider #2 with docked bikeshare. + +```jsonc +// ... + "programs": [ + { + "description" : "[PROGRAM DESCRIPTION]", + "program_website_url": "[URL]", + "program_document_url": "[URL]", + "provider_ids": [ + "[PROVIDER UUID]", + // ... + ], + "vehicle_types": [ + "[vehicle_type]", + // ... + ], + "start_date": [timestamp], + "end_date": [timestamp], + "required_data_specs" [ + { + // Required Data Specs array + }, + // other data specs + ] + } + ] +// ... +``` + +| Name | Type | Required / Optional | Description | +| ---------------------------- | --------------- | -------- | ----------------------------------- | +| `description` | text | Required | Simple agency program description of this combination of MDS, providers, vehicles, and time frame. | +| `program_website_url` | URL | Required | URL of the agency's transportation policy page. E.g. "https://www.cityname.gov/transportation/shared-devices.htm" | +| `program_document_url` | URL | Optional | URL of the agency's operating permit rules that mention data requirements. E.g. "https://www.cityname.gov/mds_data_policy.pdf" | +| `provider_ids` | UUID[] | Required | Array of provider UUIDs that apply to this group the requirements | +| `vehicle_type` | Enum | Optional | Array of [Vehicle Types](../general-information.md#vehicle-types) that apply to this requirement. If absent it applies to all vehicle types. | +| `start_date` | [timestamp][ts] | Required | Beginning date/time of requirements | +| `end_date` | [timestamp][ts] | Required | End date/time of requirements. Can be null. Keep data at least one year past `end_date` before removing. | +| `required_data_specs` | Array | Required | Array of required [Data Specs](#requirement-data-specs) | + +[Top][toc] + +#### Requirement Data Specs + +For each combination of items in a program, you can specify the data specs, APIs, endpoints, and optional fields that are required per your agency's program policies. This is an array within the [Requirement MDS Versions](#requirement-mds-versions) `mds_apis` section in the [Requirement](#requirement) data feed. + +```jsonc +// ... + "required_data_specs": [ + { + "data_spec_name": "[DATA SPEC NAME]", + "version": "[VERSION NUMBER]", + "required_apis": [ + { + // Required APIs array + } + ], + "available_apis": [ + { + // Available APIs array + } + ] + }, + // other data specs + ] +// ... +``` + +| Name | Type | Required / Optional | Description | +| -------------------- | ------ | -------- | ----------------------------------- | +| `data_spec_name` | Enum | Required | Name of the data spec required. Supported values are: '[MDS](https://github.com/openmobilityfoundation/mobility-data-specification/tree/ms-requirements)', '[GBFS](https://github.com/NABSA/gbfs/tree/v2.2)'. Others like GOFS, GTFS, TOMP-API, etc can be tested by agencies and officially standardized here in the future -- leave your feedback on [this issue](https://github.com/openmobilityfoundation/mobility-data-specification/issues/682). | +| `version` | Text | Required | Version number of the data spec required. E.g. '1.2.0' | +| `required_apis` | Array | Conditionally Required | Name of the [Requirement APIs](#requirement-apis) that need to be served by providers. At least one API is required. APIs not listed will not be available to the agency. | +| `available_apis` | Array | Conditionally Required | Name of the [Requirement APIs](#requirement-apis) that are being served by agencies. Not applicable to GBFS. APIs not listed will not be available to the provider. | + +[Top][toc] + +#### Requirement APIs + +For each data specification, you can specify which APIs, endpoints, and fields are required from providers, and which are available from your agency. + +An agency may require providers to serve optional APIs, endpoints, and fields that are needed for your agency's program. This is a `required_apis` array within the [Requirement Data Specs](#requirement-data-specs) section in the [Requirement](#requirement) data feed. + +**Note: any APIs, endpoints, or fields that are _required_ by a data specification are not to be listed, and are still required. Only optional items are enumerated here. You may however list `disallowed_fields` to exclude required fields. Optional APIs or endpoints should _NOT_ be returned unless specified.** + +You may also show which APIs, endpoints, and fields your agency is serving to providers and the public. This is an `available_apis` array within the [Requirement Data Specs](#requirement-data-specs) section in the [Requirement](#requirement) data feed. + +```jsonc +// ... + "required_apis": [ + { + "api_name" : "[API NAME]", + "required_endpoints": [ + { + "endpoint_name" : "[ENDPOINT NAME]", + "required_fields": [ + "[FIELD NAME]", + // other field names + ], + "disallowed_fields": [ + "[FIELD NAME]", + // other field names + ] + }, + // other endpoints + ] + }, + // other APIs in same data spec + ], + "available_apis": [ + { + "api_name" : "[API NAME]", + "available_endpoints": [ + { + "endpoint_name" : "[ENDPOINT NAME]", + "url": "[ENDPOINT URL]", + "available_fields": [ + "[FIELD NAME]", + // other field names + ] + }, + // other endpoints + ] + } + ] +// ... +``` + +| Name | Type | Required/Optional | Description | +| -------------------- | ----- | -------- | ----------------------------------- | +| `api_name` | Text | Required | Name of the applicable API required. At least one API is required. APIs not listed will not be available to the agency. E.g. for MDS: 'provider', or 'agency'. For GBFS, this field is omitted since GBFS starts at the `endpoint` level. | +| `endpoint_name` | Text | Required | Name of the required endpoint under the API. At least one endpoint is required. E.g. for MDS 'provider': 'trips' | + +**Provider Endpoints** - Specific to the `required_apis` array + +| Name | Type | Required/Optional | Description | +| -------------------- | ----- | -------- | ----------------------------------- | +| `required_endpoints` | Array | Required | Array of optional endpoints required by the agency. At least one is required. Endpoints not listed will not be available to the agency. | +| `required_fields` | Array | Optional | Array of optional field names required by the agency. Can be omitted if no optional fields are required. Use dot notation for nested fields. See **special notes** below. | +| `disallowed_fields` | Array | Optional | Array of optional field names which must not be returned by in the endpoint, even if required in MDS. Use dot notation for nested fields. See **special notes** below. | + +**Agency Endpoints** - Specific to the `available_apis` array + +| Name | Type | Required/Optional | Description | +| -------------------- | ----- | -------- | ----------------------------------- | +| `available_endpoints`| Array | Required | Array of endpoints provided by the agency. At least one is required. Endpoints not listed will not be available to the provider. | +| `url` | URL | Optional | Location of API endpoint url. Required if the API is unauthenticated and public, optional if endpoint is authenticated and private. E.g. "https://mds.cityname.gov/geographies/geography/1.1.0" | +| `available_fields` | Array | Optional | Array of optional field names provided by the agency. Can be omitted if none are required. Use dot notation for nested fields. See **special notes** below. | + +**Special notes about `required_fields` and `disallowed_fields`.** + +- All fields marked 'Required' in MDS are still included by default and should not be enumerated in `required_fields`. They are not affected by the Requirements endpoint, unless explicitly listed in `disallowed_fields`. +- Fields in MDS marked 'Required if available' are still returned if available, and are not affected by the Requirements endpoint, unless explicitly listed in `disallowed_fields`. +- If a 'Required' or 'Required if available' or 'Optional' field in MDS is listed in `disallowed_fields`, those fields should not be returned by the provider in the endpoint. The field (and therefore its value) must be completely removed from the response. If used, [schema](/schema) validation may fail on missing required fields. +- To reference fields that are lower in a heirarchy, use [dot separated notation](https://docs.oracle.com/en/database/oracle/oracle-database/18/adjsn/simple-dot-notation-access-to-json-data.html#GUID-7249417B-A337-4854-8040-192D5CEFD576), where a dot between field names represents one nested level deeper into the data structure. E.g. 'gps.heading' or 'features.properties.rules.vehicle_type_id'. +- To require [Greography Driven Events](/general-information.md#geography-driven-events), simply include the `event_geographies` field for either the Agency or Provider API `api_name`. Per how GDEs work, `event_location` will then not be returned, and the `changed_geographies` vehicle state `event_type` will be used. +- [While in beta](#beta-limitations), items marked as required or disallowed will only be considered a 'request' by providers, unless agencies have communicated with providers outside of MDS. [Top][toc] [beta]: /general-information.md#beta [error-messages]: /general-information.md#error-messages [iana]: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml +[json-schema]: #json-schema [muni-boundary]: ../provider/README.md#municipality-boundary [propulsion-types]: /general-information.md#propulsion-types [responses]: /general-information.md#responses diff --git a/policy/examples/README.md b/policy/examples/README.md index 9fb59f93..99e38bc4 100644 --- a/policy/examples/README.md +++ b/policy/examples/README.md @@ -1,17 +1,21 @@ # Policy Examples -This file presents a series of example [Policy documents](../README.md#schema) for Agencies to use as templates. +This file presents a series of example [Policy documents](../README.md#policy) for Agencies to use as templates. ## Table of Contents -- [Prohibited Zone](#prohibited-zone) -- [Provider Cap](#provider-cap) -- [Idle Time](#idle-time) -- [Speed Limits](#speed-limits) -- [Per Trip Fees](#per-trip-fees) -- [Vehicle Right of Way Fees](#vehicle-right-of-way-fees) -- [Metered Parking Fees](#metered-parking-fees) -- [Required Parking](#required-parking) +- [Policy Examples](#policy-examples) + - [Table of Contents](#table-of-contents) + - [Prohibited Zone](#prohibited-zone) + - [Provider Cap](#provider-cap) + - [Idle Time](#idle-time) + - [Speed Limits](#speed-limits) + - [Per Trip Fees](#per-trip-fees) + - [Vehicle Right of Way Fees](#vehicle-right-of-way-fees) + - [Metered Parking Fees](#metered-parking-fees) + - [Required Parking](#required-parking) + - [Tiered Parking Fees Per Hour](#tiered-parking-fees-per-hour) + - [Tiered Parking Fees Total](#tiered-parking-fees-total) ## Prohibited Zone @@ -270,7 +274,7 @@ File: [`per-trip-fees.json`](per-trip-fees.json) "rule_type": "rate", "rule_units": "amount", "rate_amount": 25, - "rate_recurrence": "once", + "rate_recurrence": "once_on_match", "geographies": [ "b4bcc213-4888-48ce-a33d-4dd6c3384bda" ], @@ -445,5 +449,205 @@ File: [`required-parking.json`](required-parking.json) } ``` -[Top](#table-of-contents) +## Tiered Parking Fees Per Hour +This policy states parking fees as such: +- Parking for the first hour costs $2 +- Parking for the second hour costs $4 +- Parking every hour onwards costs $10 + +For example, say a vehicle is parked for 6.5 hours. It will be charged `$2 (0-1hr) + $4 (1-2hr) + $10 (2-3hr) + $10 (3-4hr) + $10 (4-5hr) + $10 (5-6hr) + $10 (6-6.5hr) = $56` + +This policy may be specified different ways using the `rate_applies_when` field. +Both examples are shown here. + +### With default `rate_applies_when = "out_of_bounds"` + +By default the `rate_applies_when` field has the value `out_of_bounds`, +meaning the rate should take effect when an event is outside the bounds +of a rule's `minimum` and `maximum` values. + +File: [`tiered-parking-fees-per-hour.json`](tiered-parking-fees-per-hour.json) + +```json +{ + "name": "Tiered Dwell Time Example", + "description": "First hour $2, second hour $4, every hour onwards $10", + "policy_id": "2800cd0a-7827-4110-9713-b9e5bf29e9a1", + "start_date": 1558389669540, + "publish_date": 1558389669540, + "end_date": null, + "prev_policies": null, + "provider_ids": [], + "currency": "USD", + "rules": [ + { + "name": "> 2 hours", + "rule_id": "9cd1768c-ab9e-484c-93f8-72a7078aa7b9", + "rule_type": "time", + "rule_units": "hours", + "geographies": ["0c77c813-bece-4e8a-84fd-f99af777d198"], + "statuses": { "available": [], "non_operational": [] }, + "vehicle_types": ["bicycle", "scooter"], + "maximum": 2, + "rate_amount": 1000, + "rate_recurrence": "each_time_unit" + }, + { + "name": "1-2 Hours", + "rule_id": "edd6a195-bb30-4eb5-a2cc-44e5a18798a2", + "rule_type": "time", + "rule_units": "hours", + "geographies": ["0c77c813-bece-4e8a-84fd-f99af777d198"], + "statuses": { "available": [], "non_operational": [] }, + "vehicle_types": ["bicycle", "scooter"], + "maximum": 1, + "rate_amount": 400, + "rate_recurrence": "each_time_unit" + }, + { + "name": "0-1 Hour", + "rule_id": "6b6fe61b-dbe5-4367-8e35-84fb14d23c54", + "rule_type": "time", + "rule_units": "hours", + "geographies": ["0c77c813-bece-4e8a-84fd-f99af777d198"], + "statuses": { "available": [], "non_operational": [] }, + "vehicle_types": ["bicycle", "scooter"], + "maximum": 0, + "rate_amount": 200, + "rate_recurrence": "each_time_unit" + } + ] +} +``` + +### With `rate_applies_when = "in_bounds"` + +When the `rate_applies_when` field has the value `in_bounds`, +the rate takes effect when an event is within a rule's `minimum` and +`maximum` values. Note that this also uses the `inclusive_minimum` and +`inclusive_maximum` fields to create non-overlapping ranges for the rules. + +File: [`tiered-parking-fees-per-hour-in-bounds.json`](tiered-parking-fees-per-hour-in-bounds.json) + +```json +{ + "name": "Tiered Dwell Time Example", + "description": "First hour $2, second hour $4, every hour onwards $10", + "policy_id": "2800cd0a-7827-4110-9713-b9e5bf29e9a1", + "start_date": 1558389669540, + "publish_date": 1558389669540, + "end_date": null, + "prev_policies": null, + "provider_ids": [], + "currency": "USD", + "rules": [ + { + "name": "0-1 Hour", + "rule_id": "6b6fe61b-dbe5-4367-8e35-84fb14d23c54", + "rule_type": "time", + "rule_units": "hours", + "geographies": ["0c77c813-bece-4e8a-84fd-f99af777d198"], + "statuses": { "available": [], "non_operational": [] }, + "vehicle_types": ["bicycle", "scooter"], + "maximum": 1, + "inclusive_maximum": false, + "rate_applies_when": "in_bounds", + "rate_amount": 200, + "rate_recurrence": "each_time_unit" + }, + { + "name": "1-2 Hours", + "rule_id": "edd6a195-bb30-4eb5-a2cc-44e5a18798a2", + "rule_type": "time", + "rule_units": "hours", + "geographies": ["0c77c813-bece-4e8a-84fd-f99af777d198"], + "statuses": { "available": [], "non_operational": [] }, + "vehicle_types": ["bicycle", "scooter"], + "minimum": 1, + "maximum": 2, + "inclusive_minimum": true, + "inclusive_maximum": false, + "rate_applies_when": "in_bounds", + "rate_amount": 400, + "rate_recurrence": "each_time_unit" + }, + { + "name": "> 2 hours", + "rule_id": "9cd1768c-ab9e-484c-93f8-72a7078aa7b9", + "rule_type": "time", + "rule_units": "hours", + "geographies": ["0c77c813-bece-4e8a-84fd-f99af777d198"], + "statuses": { "available": [], "non_operational": [] }, + "vehicle_types": ["bicycle", "scooter"], + "minimum": 2, + "inclusive_minimum": true, + "rate_applies_when": "in_bounds", + "rate_amount": 1000, + "rate_recurrence": "each_time_unit" + } + ] +} +``` + +## Tiered Parking Fees Total +This policy states parking fees as such: +- If parked for less than an hour, $2 on exit +- If parked for less than 2 hours, $4 on exit +- If parked for any duration longer than 2 hours, $10 on exit + +For example, if a vehicle is parked for 6.5 hours, it will be charged $10 on exit. +File: [`tiered-parking-fees-total.json`](tiered-parking-fees-total.json) + +```json +{ + "name": "Tiered Dwell Time Example", + "description": "If parked for <1hr $2 upon exit, if parked for 1-2 hours $4 upon exit, if parked for longer than 2 hours $10 upon exit", + "policy_id": "2800cd0a-7827-4110-9713-b9e5bf29e9a1", + "start_date": 1558389669540, + "publish_date": 1558389669540, + "end_date": null, + "prev_policies": null, + "provider_ids": [], + "currency": "USD", + "rules": [ + { + "name": "> 2 hours", + "rule_id": "9cd1768c-ab9e-484c-93f8-72a7078aa7b9", + "rule_type": "time", + "rule_units": "hours", + "geographies": ["0c77c813-bece-4e8a-84fd-f99af777d198"], + "statuses": { "available": [], "non_operational": [] }, + "vehicle_types": ["bicycle", "scooter"], + "maximum": 2, + "rate_amount": 1000, + "rate_recurrence": "once_on_unmatch" + }, + { + "name": "1-2 Hours", + "rule_id": "edd6a195-bb30-4eb5-a2cc-44e5a18798a2", + "rule_type": "time", + "rule_units": "hours", + "geographies": ["0c77c813-bece-4e8a-84fd-f99af777d198"], + "statuses": { "available": [], "non_operational": [] }, + "vehicle_types": ["bicycle", "scooter"], + "maximum": 1, + "rate_amount": 400, + "rate_recurrence": "once_on_unmatch" + }, + { + "name": "0-1 Hour", + "rule_id": "6b6fe61b-dbe5-4367-8e35-84fb14d23c54", + "rule_type": "time", + "rule_units": "hours", + "geographies": ["0c77c813-bece-4e8a-84fd-f99af777d198"], + "statuses": { "available": [], "non_operational": [] }, + "vehicle_types": ["bicycle", "scooter"], + "maximum": 0, + "rate_amount": 200, + "rate_recurrence": "once_on_unmatch" + } + ] +} +``` +[Top](#table-of-contents) diff --git a/policy/examples/idle-time.json b/policy/examples/idle-time.json index 08ca9722..bbaf0b74 100644 --- a/policy/examples/idle-time.json +++ b/policy/examples/idle-time.json @@ -1,8 +1,8 @@ { "updated": 0, - "version": "1.0.0", + "version": "1.2.0", "data": { - "policy": [ + "policies": [ { "policy_id": "a2c9a65f-fd85-463e-9564-fc95ea473f7d", "name": "Idle Times", diff --git a/policy/examples/metered-parking-fees.json b/policy/examples/metered-parking-fees.json index 102940ec..a12a07a0 100644 --- a/policy/examples/metered-parking-fees.json +++ b/policy/examples/metered-parking-fees.json @@ -1,8 +1,8 @@ { "updated": 0, - "version": "1.0.0", + "version": "1.2.0", "data": { - "policy": [ + "policies": [ { "policy_id": "6a3dd008-836a-11ea-bc55-0242ac130003", "published_date": 1586736000000, diff --git a/policy/examples/per-trip-fees.json b/policy/examples/per-trip-fees.json index de08b2f3..d48de4cc 100644 --- a/policy/examples/per-trip-fees.json +++ b/policy/examples/per-trip-fees.json @@ -1,8 +1,8 @@ { "updated": 0, - "version": "1.0.0", + "version": "1.2.0", "data": { - "policy": [ + "policies": [ { "policy_id": "d2567b3c-3071-48a6-bbeb-3424721dbd12", "published_date": 1586736000000, diff --git a/policy/examples/prohibited-zone.json b/policy/examples/prohibited-zone.json index 7fae90b6..27dc080c 100644 --- a/policy/examples/prohibited-zone.json +++ b/policy/examples/prohibited-zone.json @@ -1,8 +1,8 @@ { "updated": 0, - "version": "1.0.0", + "version": "1.2.0", "data": { - "policy": [ + "policies": [ { "policy_id": "39a653be-7180-4188-b1a6-cae33c280341", "name": "Prohibited Dockless Zones", diff --git a/policy/examples/provider-cap.json b/policy/examples/provider-cap.json index af2d1168..d1006049 100644 --- a/policy/examples/provider-cap.json +++ b/policy/examples/provider-cap.json @@ -1,8 +1,8 @@ { "updated": 0, - "version": "1.0.0", + "version": "1.2.0", "data": { - "policy": [ + "policies": [ { "name": "Test City Mobility Caps: Company X", "description": "Mobility caps as described in the One-Year Permit", diff --git a/policy/examples/required-parking.json b/policy/examples/required-parking.json index 79a14e5e..76f13468 100644 --- a/policy/examples/required-parking.json +++ b/policy/examples/required-parking.json @@ -1,8 +1,8 @@ { "updated": 0, - "version": "1.0.0", + "version": "1.2.0", "data": { - "policy": [ + "policies": [ { "policy_id": "99f7a469-6e3a-4981-9313-c2f6c0bbd5ce", "name": "Test City Mobility Hubs", diff --git a/policy/examples/requirements.md b/policy/examples/requirements.md new file mode 100644 index 00000000..4df23334 --- /dev/null +++ b/policy/examples/requirements.md @@ -0,0 +1,717 @@ +# Policy Requirement Examples + +This file presents a series of example [Requirements](../README.md#requirement) documents for Agencies to use as templates. + +## Table of Contents + +- [Policy and Geography](#policy-and-geography) +- [Vehicles Only](#vehicles-only) +- [Trips Only](#trips-only) +- [Trips with No Routes, Vehicles IDs, or Dates](#trips-with-no-routes-vehicle-ids-or-dates) +- [Provider and Other APIs](#provider-and-other-apis) +- [Agency](#agency) +- [Geography Driven Events](#geography-driven-events) +- [GBFS Only](#gbfs-only) + +## Policy and Geography + +Version 1.2.0 of MDS Policy and Geography for agencies to publish rules/fees/incentives and operating/equity/no-ride/slow speed/parking areas to all providers, and require GBFS's geofencing_zones. + +```json +{ + "metadata": { + "mds_release": "1.2.0", + "file_version": "4", + "last_updated": "1611729218", + "max_update_interval": "P1M", + "agency_id": "737a9c62-c0cb-4c93-be43-271d21b784b5", + "agency_name": "Louisville Metro", + "agency_timezone": "America/New_York", + "agency_language": "en-US", + "agency_currency": "USD", + "agency_website_url": "https://www.cityname.gov/transportation/", + "url": "https://mds.cityname.gov/policy/requirements/1.2.0" + }, + "programs": [ + { + "description": "City Micromobility Program Policy Rules", + "program_website_url": "https://www.cityname.gov/transportation/shared-devices.html", + "program_document_url": "https://www.cityname.gov/mds_data_policy.pdf", + "provider_ids": [ + "70aa475d-1fcd-4504-b69c-2eeb2107f7be", + "2411d395-04f2-47c9-ab66-d09e9e3c3251", + "04ab5c86-ab6f-4abc-b866-e4c92da39a3e", + "bd530feb-936f-40eb-ae04-ce931de216e1", + "a8c54e3e-fe67-4c5a-90a6-4a1d2c2808da" + ], + "start_date": 1611958740, + "end_date": null, + "required_data_specs": [ + { + "data_spec_name": "MDS", + "version": "1.2.0", + "available_apis": [ + { + "api_name": "policy", + "available_endpoints": [ + { + "endpoint_name": "policies", + "url": "https://mds.cityname.com/policy/policies/1.2.0" + } + ] + }, + { + "api_name": "geography", + "available_endpoints": [ + { + "endpoint_name": "geographies", + "url": "https://mds.cityname.com/geography/geographies/1.2.0", + "available_fields": [ + "geography_type", + "description" + ] + } + ] + } + ] + }, + { + "data_spec_name": "GBFS", + "version": "2.2", + "required_apis": [ + { + "required_endpoints": [ + { + "endpoint_name": "geofencing_zones.json", + "required_fields": [ + "features.properties.rules.vehicle_type_id" + ] + } + ] + } + ] + } + ] + } + ] +} +``` + +[Top](#table-of-contents) + +## Vehicles Only + +Version 1.1.0 for one provider with scooters, and 1.0.0 for another provider for bicycles, requiring only the Provider `/vehicles` endpoint and no optional fields, as an authenticated [alternative to GBFS](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/MDS-Vehicles) for internal use, while still requiring GBFS 2.1 for the public. + +```json +{ + "metadata": { + "mds_release": "1.2.0", + "file_version": "2", + "last_updated": "1611958740", + "max_update_interval": "P1M", + "agency_id": "737a9c62-c0cb-4c93-be43-271d21b784b5", + "agency_name": "Louisville Metro", + "agency_timezone": "America/New_York", + "agency_language": "en-US", + "agency_currency": "USD", + "agency_website_url": "https://www.cityname.gov/transportation/", + "url": "https://mds.cityname.gov/policy/requirements/1.2.0" + }, + "programs": [ + { + "description": "City Scooter Monitoring Program 2021", + "program_website_url": "https://www.cityname.gov/transportation/shared-devices.html", + "program_document_url": "https://www.cityname.gov/mds_data_policy.pdf", + "provider_ids": [ + "70aa475d-1fcd-4504-b69c-2eeb2107f7be" + ], + "vehicle_types": [ + "scooter" + ], + "start_date": 1611958740, + "end_date": null, + "required_data_specs": [ + { + "data_spec_name": "MDS", + "version": "1.2.0", + "required_apis": [ + { + "api_name": "provider", + "required_endpoints": [ + { + "endpoint_name": "vehicles" + } + ] + } + ] + }, + { + "data_spec_name": "GBFS", + "version": "2.1" + } + ] + }, + { + "description": "City Bikeshare Monitoring Program 2021", + "program_website_url": "https://www.cityname.gov/transportation/bikeshare.html", + "program_document_url": "https://www.cityname.gov/mds_data_policy.pdf", + "provider_ids": [ + "2411d395-04f2-47c9-ab66-d09e9e3c3251" + ], + "vehicle_types": [ + "bicycle" + ], + "start_date": 1611958740, + "end_date": null, + "required_data_specs": [ + { + "data_spec_name": "MDS", + "version": "1.2.0", + "required_apis": [ + { + "api_name": "provider", + "required_endpoints": [ + { + "endpoint_name": "vehicles" + } + ] + } + ] + }, + { + "data_spec_name": "GBFS", + "version": "2.1" + } + ] + } + ] +} +``` + +[Top](#table-of-contents) + +## Trips Only + +Version 1.1.0 for 2 providers requiring only historic Provider `/trips` with the optional `parking_verificaiton_url` field, linked to a specific MDS Policy. + +```json +{ + "metadata": { + "mds_release": "1.2.0", + "file_version": "3", + "last_updated": "1611958740", + "max_update_interval": "P1M", + "agency_id": "737a9c62-c0cb-4c93-be43-271d21b784b5", + "agency_name": "Louisville Metro", + "agency_timezone": "America/New_York", + "agency_language": "en-US", + "agency_currency": "USD", + "agency_website_url": "https://www.cityname.gov/transportation/", + "url": "https://mds.cityname.gov/policy/requirements/1.2.0" + }, + "programs": [ + { + "description": "City Vehicle Program Pilot 2021", + "program_website_url": "https://www.cityname.gov/transportation/shared-devices.html", + "program_document_url": "https://www.cityname.gov/mds_data_policy.pdf", + "provider_ids": [ + "70aa475d-1fcd-4504-b69c-2eeb2107f7be", + "2411d395-04f2-47c9-ab66-d09e9e3c3251" + ], + "start_date": 1611958740, + "end_date": 1611970539, + "required_data_specs": [ + { + "data_spec_name": "MDS", + "version": "1.2.0", + "required_apis": [ + { + "api_name": "provider", + "required_endpoints": [ + { + "endpoint_name": "trips", + "required_fields": [ + "parking_verification_url" + ] + } + ] + } + ] + }, + { + "data_spec_name": "GBFS", + "version": "2.2" + } + ] + } + ] +} +``` + +[Top](#table-of-contents) + +## Trips with No Routes, Vehicle IDs, or Dates + +Version 1.1.0 for 2 providers asking for only historic [Provider `/trips`](/provider#trips) with the typically required `device_id`, `vehicle_id`, `start_time`, `end_time`, and `route` array data, and the optional `parking_verification_url` photo URL, not returned in the endpoint. + +```json +{ + "metadata": { + "mds_release": "1.2.0", + "file_version": "3", + "last_updated": "1611958740", + "max_update_interval": "P1M", + "agency_id": "737a9c62-c0cb-4c93-be43-271d21b784b5", + "agency_name": "Louisville Metro", + "agency_timezone": "America/New_York", + "agency_language": "en-US", + "agency_currency": "USD", + "agency_website_url": "https://www.cityname.gov/transportation/", + "url": "https://mds.cityname.gov/policy/requirements/1.2.0" + }, + "programs": [ + { + "description": "City Vehicle Program Pilot Research for 2021", + "program_website_url": "https://www.cityname.gov/transportation/shared-devices.html", + "program_document_url": "https://www.cityname.gov/mds_data_policy.pdf", + "provider_ids": [ + "70aa475d-1fcd-4504-b69c-2eeb2107f7be", + "2411d395-04f2-47c9-ab66-d09e9e3c3251" + ], + "start_date": 1611958740, + "end_date": 1611970539, + "required_data_specs": [ + { + "data_spec_name": "MDS", + "version": "1.2.0", + "required_apis": [ + { + "api_name": "provider", + "required_endpoints": [ + { + "endpoint_name": "trips", + "disallowed_fields": [ + "route", + "device_id", + "vehicle_id", + "start_time", + "end_time", + "parking_verification_url" + ] + } + ] + } + ] + }, + { + "data_spec_name": "GBFS", + "version": "2.2" + } + ] + } + ] +} +``` + +[Top](#table-of-contents) + +## Provider and Other APIs + +Version 1.1.0 or 0.4.1 for 3 providers with many APIs and endpoints. + +Note: by specifying geography, policy, and jurisdiction here with a URL, the agency is in effect saying that they have created and are hosting these, and they are available for use if public. + +```json +{ + "metadata": { + "mds_release": "1.2.0", + "file_version": "3", + "last_updated": "1611958740", + "max_update_interval": "P1M", + "agency_id": "737a9c62-c0cb-4c93-be43-271d21b784b5", + "agency_name": "Louisville Metro", + "agency_timezone": "America/New_York", + "agency_language": "en-US", + "agency_currency": "USD", + "agency_website_url": "https://www.cityname.gov/transportation/", + "url": "https://mds.cityname.gov/policy/requirements/1.2.0" + }, + "programs": [ + { + "description": "City Shared Device Program and Policies 2021", + "program_website_url": "https://www.cityname.gov/transportation/shared-devices.html", + "program_document_url": "https://www.cityname.gov/mds_data_policy.pdf", + "provider_ids": [ + "70aa475d-1fcd-4504-b69c-2eeb2107f7be", + "2411d395-04f2-47c9-ab66-d09e9e3c3251", + "420e6e94-55a6-4946-b6b3-4398fe22e912" + ], + "start_date": 1611958740, + "end_date": 1611970539, + "required_data_specs": [ + { + "data_spec_name": "MDS", + "version": "1.2.0", + "required_apis": [ + { + "api_name": "provider", + "required_endpoints": [ + { + "endpoint_name": "trips", + "required_fields": [ + "parking_verification_url", + "standard_cost", + "actual_cost" + ] + }, + { + "endpoint_name": "status_changes", + "required_fields": [ + "event_geographies", + "trip_id" + ] + }, + { + "endpoint_name": "reports" + }, + { + "endpoint_name": "events", + "required_fields": [ + "trip_id", + "associated_ticket" + ] + }, + { + "endpoint_name": "stops", + "required_fields": [ + "geography_id", + "address", + "devices", + "image_url" + ] + }, + { + "endpoint_name": "vehicles", + "required_fields": [ + "current_location" + ] + } + ] + } + ], + "available_apis": [ + { + "api_name": "policy", + "available_endpoints": [ + { + "endpoint_name": "policies", + "url": "https://mds.cityname.gov/policy/policies/1.1.0" + } + ] + }, + { + "api_name": "geography", + "available_endpoints": [ + { + "endpoint_name": "geographies", + "url": "https://mds.cityname.gov/geography/geographies/1.1.0" + } + ] + }, + { + "api_name": "jurisdiction", + "available_endpoints": [ + { + "endpoint_name": "trips", + "url": "https://mds.cityname.gov/jurisdiction/jurisdictions/1.1.0" + } + ] + }, + { + "api_name": "metrics" + } + ] + }, + { + "data_spec_name": "GBFS", + "version": "2.2" + } + ] + }, + { + "description": "City Docked Device Program 2021", + "program_website_url": "https://www.cityname.gov/transportation/shared-devices.html", + "program_document_url": "https://www.cityname.gov/mds_data_policy.pdf", + "version": "0.4.1", + "provider_ids": [ + "70aa475d-1fcd-4504-b69c-2eeb2107f7be", + "2411d395-04f2-47c9-ab66-d09e9e3c3251", + "420e6e94-55a6-4946-b6b3-4398fe22e912" + ], + "start_date": 1611958740, + "end_date": 1611970539, + "required_data_specs": [ + { + "data_spec_name": "MDS", + "version": "0.4.1", + "required_apis": [ + { + "api_name": "provider", + "required_endpoints": [ + { + "endpoint_name": "trips", + "required_fields": [ + "parking_verification_url", + "standard_cost", + "actual_cost" + ] + }, + { + "endpoint_name": "status_changes" + }, + { + "endpoint_name": "events" + }, + { + "endpoint_name": "stops", + "required_fields": [ + "geography_id", + "address", + "devices", + "image_url" + ] + }, + { + "endpoint_name": "vehicles" + } + ] + } + ] + }, + { + "data_spec_name": "GBFS", + "version": "2.1" + } + ] + } + ] +} +``` + +[Top](#table-of-contents) + +## Agency + +Version 1.1.0 for 3 providers and serving Agency only linking to a defined MDS Policy. + +```json +{ + "metadata": { + "mds_release": "1.2.0", + "file_version": "2", + "last_updated": "1611958740", + "max_update_interval": "P1M", + "agency_id": "737a9c62-c0cb-4c93-be43-271d21b784b5", + "agency_name": "Louisville Metro", + "agency_timezone": "America/New_York", + "agency_language": "en-US", + "agency_currency": "USD", + "agency_website_url": "https://www.cityname.gov/transportation/", + "url": "https://mds.cityname.gov/policy/requirements/1.2.0" + }, + "programs": [ + { + "description": "City Shared Device Management Program 2021-2022", + "program_website_url": "https://www.cityname.gov/transportation/shared-devices.html", + "program_document_url": "https://www.cityname.gov/mds_data_policy.pdf", + "provider_ids": [ + "70aa475d-1fcd-4504-b69c-2eeb2107f7be", + "2411d395-04f2-47c9-ab66-d09e9e3c3251", + "420e6e94-55a6-4946-b6b3-4398fe22e912" + ], + "start_date": 1611958740, + "end_date": 1611970539, + "required_data_specs": [ + { + "data_spec_name": "MDS", + "version": "1.2.0", + "required_apis": [ + { + "api_name": "agency", + "required_endpoints": [ + { + "endpoint_name": "vehicles" + }, + { + "endpoint_name": "vehicle_register", + "required_fields": [ + "year", + "mfg", + "model" + ] + }, + { + "endpoint_name": "vehicle_update" + }, + { + "endpoint_name": "vehicle_event", + "required_fields": [ + "event_geographies", + "trip_id" + ] + }, + { + "endpoint_name": "vehicle_telemetry" + }, + { + "endpoint_name": "stops", + "required_fields": [ + "status", + "num_spots_disabled" + ] + } + ] + } + ] + }, + { + "data_spec_name": "GBFS", + "version": "2.2" + } + ] + } + ] +} +``` + +[Top](#table-of-contents) + +## Geography Driven Events + +Version 1.1.0 for 2 providers requiring Provider `/status_changes` with the minimum required for beta feature [Geography Driven Events](/general-information.md#geography-driven-events). + +```json +{ + "metadata": { + "mds_release": "1.2.0", + "file_version": "1", + "last_updated": "1611958740", + "max_update_interval": "P1M", + "agency_id": "737a9c62-c0cb-4c93-be43-271d21b784b5", + "agency_name": "Portland Metro", + "agency_timezone": "America/Los_Angeles", + "agency_language": "en-US", + "agency_currency": "USD", + "agency_website_url": "https://www.cityname.gov/transportation/", + "url": "https://mds.cityname.gov/policy/requirements/1.2.0" + }, + "programs": [ + { + "description": "City Shared Vehicle Program", + "program_website_url": "https://www.cityname.gov/transportation/shared-devices.html", + "program_document_url": "https://www.cityname.gov/mds_data_policy.pdf", + "provider_ids": [ + "70aa475d-1fcd-4504-b69c-2eeb2107f7be", + "2411d395-04f2-47c9-ab66-d09e9e3c3251" + ], + "start_date": 1611958740, + "end_date": 1611970539, + "required_data_specs": [ + { + "data_spec_name": "MDS", + "version": "1.2.0", + "required_apis": [ + { + "api_name": "provider", + "required_endpoints": [ + { + "endpoint_name": "status_changes", + "required_fields": [ + "event_geographies" + ] + } + ] + } + ] + }, + { + "data_spec_name": "GBFS", + "version": "2.1" + } + ] + } + ] +} +``` + +[Top](#table-of-contents) + +## GBFS Only + +Since Requirements allows the GBFS versions and optional endpoints and fields to be defined, an agency could use it to only require public GBFS feeds, and not require MDS at all. + +```json +{ + "metadata": { + "mds_release": "1.2.0", + "file_version": "2", + "last_updated": "1611958740", + "max_update_interval": "P1M", + "agency_id": "737a9c62-c0cb-4c93-be43-271d21b784b5", + "agency_name": "Louisville Metro", + "agency_timezone": "America/New_York", + "agency_language": "en-US", + "agency_currency": "USD", + "agency_website_url": "https://www.cityname.gov/transportation/", + "url": "https://mds.cityname.gov/policy/requirements/1.2.0" + }, + "programs": [ + { + "description": "City Scooter Public Data Feeds 2021", + "program_website_url": "https://www.cityname.gov/transportation/shared-devices.html", + "program_document_url": "https://www.cityname.gov/data_policy.pdf", + "provider_ids": [ + "70aa475d-1fcd-4504-b69c-2eeb2107f7be", + "2411d395-04f2-47c9-ab66-d09e9e3c3251" + ], + "start_date": 1611958740, + "end_date": null, + "required_data_specs": [ + { + "data_spec_name": "GBFS", + "version": "2.2", + "required_apis": [ + { + "required_endpoints": [ + { + "endpoint_name": "geofencing_zones.json", + "required_fields": [ + "features.properties.name", + "features.properties.start", + "features.properties.end", + "features.properties.rules.vehicle_type_id" + ] + }, + { + "endpoint_name": "system_calendar.json" + }, + { + "endpoint_name": "system_pricing_plans.json", + "required_fields": [ + "per_km_pricing", + "per_km_pricing", + "surge_pricing" + ] + } + ] + } + ] + } + ] + } + ] +} +``` + +[Top](#table-of-contents) diff --git a/policy/examples/speed-limits.json b/policy/examples/speed-limits.json index 9643466a..d14dd2ef 100644 --- a/policy/examples/speed-limits.json +++ b/policy/examples/speed-limits.json @@ -1,8 +1,8 @@ { "updated": 0, - "version": "1.0.0", + "version": "1.2.0", "data": { - "policy": [ + "policies": [ { "policy_id": "95645117-fd85-463e-a2c9-fc95ea47463e", "name": "Speed Limits", diff --git a/policy/examples/tiered-parking-fees-per-hour-in-bounds.json b/policy/examples/tiered-parking-fees-per-hour-in-bounds.json new file mode 100644 index 00000000..3a2489c8 --- /dev/null +++ b/policy/examples/tiered-parking-fees-per-hour-in-bounds.json @@ -0,0 +1,89 @@ +{ + "updated": 0, + "version": "1.2.0", + "data": { + "policies": [ + { + "name": "Tiered Dwell Time Example", + "description": "First hour $2, second hour $4, every hour onwards $10", + "policy_id": "2800cd0a-7827-4110-9713-b9e5bf29e9a1", + "start_date": 1558389669540, + "publish_date": 1558389669540, + "end_date": null, + "prev_policies": null, + "provider_ids": [], + "currency": "USD", + "rules": [ + { + "name": "0-1 Hour", + "rule_id": "6b6fe61b-dbe5-4367-8e35-84fb14d23c54", + "rule_type": "time", + "rule_units": "hours", + "geographies": [ + "0c77c813-bece-4e8a-84fd-f99af777d198" + ], + "statuses": { + "available": [], + "non_operational": [] + }, + "vehicle_types": [ + "bicycle", + "scooter" + ], + "maximum": 1, + "inclusive_maximum": false, + "rate_applies_when": "in_bounds", + "rate_amount": 200, + "rate_recurrence": "each_time_unit" + }, + { + "name": "1-2 Hours", + "rule_id": "edd6a195-bb30-4eb5-a2cc-44e5a18798a2", + "rule_type": "time", + "rule_units": "hours", + "geographies": [ + "0c77c813-bece-4e8a-84fd-f99af777d198" + ], + "statuses": { + "available": [], + "non_operational": [] + }, + "vehicle_types": [ + "bicycle", + "scooter" + ], + "minimum": 1, + "maximum": 2, + "inclusive_minimum": true, + "inclusive_maximum": false, + "rate_applies_when": "in_bounds", + "rate_amount": 400, + "rate_recurrence": "each_time_unit" + }, + { + "name": "> 2 hours", + "rule_id": "9cd1768c-ab9e-484c-93f8-72a7078aa7b9", + "rule_type": "time", + "rule_units": "hours", + "geographies": [ + "0c77c813-bece-4e8a-84fd-f99af777d198" + ], + "statuses": { + "available": [], + "non_operational": [] + }, + "vehicle_types": [ + "bicycle", + "scooter" + ], + "minimum": 2, + "inclusive_minimum": true, + "rate_applies_when": "in_bounds", + "rate_amount": 1000, + "rate_recurrence": "each_time_unit" + } + ] + } + ] + } +} diff --git a/policy/examples/tiered-parking-fees-per-hour.json b/policy/examples/tiered-parking-fees-per-hour.json new file mode 100644 index 00000000..aa973921 --- /dev/null +++ b/policy/examples/tiered-parking-fees-per-hour.json @@ -0,0 +1,81 @@ +{ + "updated": 0, + "version": "1.2.0", + "data": { + "policies": [ + { + "name": "Tiered Dwell Time Example", + "description": "First hour $2, second hour $4, every hour onwards $10", + "policy_id": "2800cd0a-7827-4110-9713-b9e5bf29e9a1", + "start_date": 1558389669540, + "publish_date": 1558389669540, + "end_date": null, + "prev_policies": null, + "provider_ids": [], + "currency": "USD", + "rules": [ + { + "name": "> 2 hours", + "rule_id": "9cd1768c-ab9e-484c-93f8-72a7078aa7b9", + "rule_type": "time", + "rule_units": "hours", + "geographies": [ + "0c77c813-bece-4e8a-84fd-f99af777d198" + ], + "statuses": { + "available": [], + "non_operational": [] + }, + "vehicle_types": [ + "bicycle", + "scooter" + ], + "maximum": 2, + "rate_amount": 1000, + "rate_recurrence": "each_time_unit" + }, + { + "name": "1-2 Hours", + "rule_id": "edd6a195-bb30-4eb5-a2cc-44e5a18798a2", + "rule_type": "time", + "rule_units": "hours", + "geographies": [ + "0c77c813-bece-4e8a-84fd-f99af777d198" + ], + "statuses": { + "available": [], + "non_operational": [] + }, + "vehicle_types": [ + "bicycle", + "scooter" + ], + "maximum": 1, + "rate_amount": 400, + "rate_recurrence": "each_time_unit" + }, + { + "name": "0-1 Hour", + "rule_id": "6b6fe61b-dbe5-4367-8e35-84fb14d23c54", + "rule_type": "time", + "rule_units": "hours", + "geographies": [ + "0c77c813-bece-4e8a-84fd-f99af777d198" + ], + "statuses": { + "available": [], + "non_operational": [] + }, + "vehicle_types": [ + "bicycle", + "scooter" + ], + "maximum": 0, + "rate_amount": 200, + "rate_recurrence": "each_time_unit" + } + ] + } + ] + } +} diff --git a/policy/examples/tiered-parking-fees-total.json b/policy/examples/tiered-parking-fees-total.json new file mode 100644 index 00000000..dac70774 --- /dev/null +++ b/policy/examples/tiered-parking-fees-total.json @@ -0,0 +1,81 @@ +{ + "updated": 0, + "version": "1.2.0", + "data": { + "policies": [ + { + "name": "Tiered Dwell Time Example", + "description": "If parked for <1hr $2 upon exit, if parked for 1-2 hours $4 upon exit, if parked for longer than 2 hours $10 upon exit", + "policy_id": "2800cd0a-7827-4110-9713-b9e5bf29e9a1", + "start_date": 1558389669540, + "publish_date": 1558389669540, + "end_date": null, + "prev_policies": null, + "provider_ids": [], + "currency": "USD", + "rules": [ + { + "name": "> 2 hours", + "rule_id": "9cd1768c-ab9e-484c-93f8-72a7078aa7b9", + "rule_type": "time", + "rule_units": "hours", + "geographies": [ + "0c77c813-bece-4e8a-84fd-f99af777d198" + ], + "statuses": { + "available": [], + "non_operational": [] + }, + "vehicle_types": [ + "bicycle", + "scooter" + ], + "maximum": 2, + "rate_amount": 1000, + "rate_recurrence": "once_on_unmatch" + }, + { + "name": "1-2 Hours", + "rule_id": "edd6a195-bb30-4eb5-a2cc-44e5a18798a2", + "rule_type": "time", + "rule_units": "hours", + "geographies": [ + "0c77c813-bece-4e8a-84fd-f99af777d198" + ], + "statuses": { + "available": [], + "non_operational": [] + }, + "vehicle_types": [ + "bicycle", + "scooter" + ], + "maximum": 1, + "rate_amount": 400, + "rate_recurrence": "once_on_unmatch" + }, + { + "name": "0-1 Hour", + "rule_id": "6b6fe61b-dbe5-4367-8e35-84fb14d23c54", + "rule_type": "time", + "rule_units": "hours", + "geographies": [ + "0c77c813-bece-4e8a-84fd-f99af777d198" + ], + "statuses": { + "available": [], + "non_operational": [] + }, + "vehicle_types": [ + "bicycle", + "scooter" + ], + "maximum": 0, + "rate_amount": 200, + "rate_recurrence": "once_on_unmatch" + } + ] + } + ] + } +} diff --git a/policy/examples/vehicle-row-fees.json b/policy/examples/vehicle-row-fees.json index 56f20ad8..ec6ea351 100644 --- a/policy/examples/vehicle-row-fees.json +++ b/policy/examples/vehicle-row-fees.json @@ -1,8 +1,8 @@ { "updated": 0, - "version": "1.0.0", + "version": "1.2.0", "data": { - "policy": [ + "policies": [ { "policy_id": "4137a47c-836a-11ea-bc55-0242ac130003", "published_date": 1586736000000, diff --git a/policy/policy.json b/policy/policy.json index 6afe52e2..b937f00e 100644 --- a/policy/policy.json +++ b/policy/policy.json @@ -176,6 +176,22 @@ ], "description": "Maximum value, if applicable (default unlimited)" }, + "inclusive_minimum": { + "$id": "#/definitions/rule/properties/inclusive_minimum", + "type": [ + "null", + "boolean" + ], + "description": "Whether the rule minimum is considered in-bounds (default true)" + }, + "inclusive_maximum": { + "$id": "#/definitions/rule/properties/inclusive_maximum", + "type": [ + "null", + "boolean" + ], + "description": "Whether the rule maximum is considered in-bounds (default true)" + }, "rate_amount": { "$id": "#/definitions/rule/properties/rate_amount", "type": [ @@ -190,13 +206,26 @@ "string", "null" ], - "description": "Specify when a rate is applied \u00e2\u20ac\u201c either once, or periodically according to a time unit specified using rule_units", + "description": "Specify how a rate is applied \u00e2\u20ac\u201c either once, or periodically according to a time unit specified using rule_units", "enum": [ - "once", + "once_on_match", + "once_on_unmatch", "each_time_unit", "per_complete_time_unit" ] }, + "rate_applies_when": { + "$id": "#/definitions/rule/properties/rate_applies_when", + "type": [ + "string", + "null" + ], + "description": "Specify when a rate is applicable to an event or count: when it's within rule bounds or when it is not", + "enum": [ + "in_bounds", + "out_of_bounds" + ] + }, "start_time": { "$id": "#/definitions/rule/properties/start_time", "$ref": "#/definitions/null_iso_time", @@ -377,7 +406,7 @@ "type": "number", "description": "Integer milliseconds since Unix epoch", "multipleOf": 1.0, - "minimum": 0 + "minimum": 1514764800000 }, "uuid": { "$id": "#/definitions/uuid", @@ -451,6 +480,7 @@ "description": "The type of vehicle", "enum": [ "bicycle", + "cargo_bicycle", "car", "scooter", "moped", @@ -462,9 +492,9 @@ "type": "string", "description": "The version of MDS this data represents", "examples": [ - "1.1.0" + "1.2.0" ], - "pattern": "^1\\.1\\.[0-9]+$" + "pattern": "^1\\.2\\.[0-9]+$" }, "days": { "$id": "#/definitions/days", @@ -584,15 +614,15 @@ "type": "object", "description": "The data records in this payload", "required": [ - "policy" + "policies" ], "properties": { - "policy": { - "$id": "#/properties/data/properties/policy", + "policies": { + "$id": "#/properties/data/properties/policies", "type": "array", - "title": "The policy payload", + "title": "The array of policy objects in this payload", "items": { - "$id": "#/properties/data/properties/policy/items", + "$id": "#/properties/data/properties/policies/items", "$ref": "#/definitions/policy" } } diff --git a/provider/README.md b/provider/README.md index a0eeea21..f854b65e 100644 --- a/provider/README.md +++ b/provider/README.md @@ -185,21 +185,40 @@ The `/trips` API should allow querying trips with the following query parameters | --------------- | ------ | --------------- | | `end_time` | `YYYY-MM-DDTHH`, an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) extended datetime representing an UTC hour between 00 and 23. | All trips with an end time occurring within the hour. For example, requesting `end_time=2019-10-01T07` returns all trips where `2019-10-01T07:00:00 <= trip.end_time < 2019-10-01T08:00:00` UTC. | -If the provider was operational during the requested hour the provider shall return -a 200 response, even if there are no trips to report (in which case -the response will contain an empty list of trips). -If the requested hour occurs in a time period in which the provider was not operational -or the hour is not yet in the past `/trips` shall return a `404 Not Found` error. - Without an `end_time` query parameter, `/trips` shall return a `400 Bad Request` error. +### Trips - Responses + +The API's response will depend on the hour queried and the status of data +processing for that hour: + +* For hours that are not yet in the past the API shall return a `404 Not Found` + response. +* For hours in which the provider was not operating the API shall return a + `404 Not Found` response. +* For hours that are in the past but for which data is not yet available + the API shall return a `102 Processing` response. +* For all other hours the API shall return a `200 OK` response with a fully + populated body, even for hours that contain no trips to report. + If the hour has no trips to report the response shall contain an empty + array of trips: + + ```json + { + "version": "x.y.z", + "data": { + "trips": [] + } + } + ``` + For the near-ish real time use cases, please use the [events][events] endpoint. [Top][toc] ### Routes -To represent a route, MDS `provider` APIs must create a GeoJSON [`FeatureCollection`][geojson-feature-collection], which includes every [observed point][geo] in the route, even those which occur outside the [municipality boundary][muni-boundary]. +To represent a route, MDS `provider` APIs must create a GeoJSON [`FeatureCollection`][geojson-feature-collection], which includes every [observed point][point-geo] in the route, even those which occur outside the [municipality boundary][muni-boundary]. Routes must include at least 2 points: the start point and end point. Routes must include all possible GPS or GNSS samples collected by a Provider. Providers may round the latitude and longitude to the level of precision representing the maximum accuracy of the specific measurement. For example, [a-GPS][agps] is accurate to 5 decimal places, [differential GPS][dgps] is generally accurate to 6 decimal places. Providers may round those readings to the appropriate number for their systems. @@ -271,7 +290,7 @@ Unless stated otherwise by the municipality, this endpoint must return only thos | `event_types` | Enum[] | Required | [Vehicle event(s)][vehicle-events] for state change, allowable values determined by `vehicle_state` | | `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 | See also [Stop-based Geographic Data][stop-based-geo]. | +| `event_location` | GeoJSON [Point Feature][point-geo] | Required | See also [Stop-based Geographic Data][stop-based-geo]. | | `event_geographies` | UUID[] | Optional | **[Beta feature](/general-information.md#beta-features):** *Yes (as of 1.1.0)*. Array of Geography UUIDs consisting of every Geography that contains the location of the status change. See [Geography Driven Events][geography-driven-events]. Required if `event_location` is not present. | | `battery_pct` | Float | Required if Applicable | Percent battery charge of device, expressed between 0 and 1 | | `trip_id` | UUID | Required if Applicable | Trip UUID (foreign key to Trips API), required if `event_types` contains `trip_start`, `trip_end`, `trip_cancel`, `trip_enter_jurisdiction`, or `trip_leave_jurisdiction` | @@ -287,14 +306,33 @@ The `/status_changes` API should allow querying status changes with the followin | --------------- | ------ | --------------- | | `event_time` | `YYYY-MM-DDTHH`, an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) extended datetime representing an UTC hour between 00 and 23. | All status changes with an event time occurring within the hour. For example, requesting `event_time=2019-10-01T07` returns all status changes where `2019-10-01T07:00:00 <= status_change.event_time < 2019-10-01T08:00:00` UTC. | -If the provider was operational during the requested hour the provider shall return -a 200 response, even if there are no status changes to report (in which case -the response will contain an empty list of status changes). -If the requested hour occurs in a time period in which the provider was not operational -or the hour is not yet in the past `/status_changes` shall return a `404 Not Found` error. - Without an `event_time` query parameter, `/status_changes` shall return a `400 Bad Request` error. +### Status Changes - Responses + +The API's response will depend on the hour queried and the status of data +processing for that hour: + +* For hours that are not yet in the past the API shall return a `404 Not Found` + response. +* For hours in which the provider was not operating the API shall return a + `404 Not Found` response. +* For hours that are in the past but for which data is not yet available + the API shall return a `102 Processing` response. +* For all other hours the API shall return a `200 OK` response with a fully + populated body, even for hours that contain no status changes to report. + If the hour has no status changes to report the response shall contain an + empty array of status changes: + + ```json + { + "version": "x.y.z", + "data": { + "status_changes": [] + } + } + ``` + [Top][toc] ## Reports @@ -307,7 +345,7 @@ The authenticated reports are monthly, historic flat files that may be pre-gener **Endpoint:** `/reports` **Method:** `GET` -**[Beta feature][beta]:** Yes (as of 1.1.0) +**[Beta feature][beta]:** Yes (as of 1.1.0). [Leave feedback](https://github.com/openmobilityfoundation/mobility-data-specification/issues/672) **Schema:** TBD when out of beta **`data` Filename:** monthly file named by year and month, e.g. `/reports/YYYY-MM.csv` **`data` Payload:** monthly CSV files with the following structure: @@ -492,7 +530,8 @@ In addition to the standard [Provider payload wrapper](#response-format), respon **Endpoint:** `/stops/:stop_id` **Method:** `GET` -**[Beta feature][beta]:** Yes (as of 1.0.0) +**[Beta feature][beta]:** Yes (as of 1.0.0). [Leave feedback](https://github.com/openmobilityfoundation/mobility-data-specification/issues/638) +**Schema:** [`stops` schema][stops-schema] **`data` Payload:** `{ "stops": [] }`, an array of [Stops][stops] In the case that a `stop_id` query parameter is specified, the `stops` array returned will only have one entry. In the case that no `stop_id` query parameter is specified, all stops will be returned. @@ -501,9 +540,11 @@ In the case that a `stop_id` query parameter is specified, the `stops` array ret ### Vehicles -The `/vehicles` endpoint returns the current status of vehicles on the [PROW](/general-information.md#definitions). Only vehicles that are currently in available, unavailable, or reserved states should be returned in this payload. Data in this endpoint should reconcile with data from the `/status_changes` enpdoint. +The `/vehicles` is a near-realtime endpoint and returns the current status of vehicles in an agency's [Jurisdiction](/general-information.md#definitions) and/or area of agency responsibility. All vehicles that are currently in any [`vehicle_state`](/general-information.md#vehicle-states) should be returned in this payload. Since all states are returned, care should be taken to filter out states not in the [PROW](/general-information.md#definitions) if doing vehicle counts. For the states `elsewhere` and `removed` which include vehicles not in the [PROW](/general-information.md#definitions) but provide some operational clarity for agencies, these must only persist in the feed for 90 minutes before being removed. + +Data in this endpoint should reconcile with data from the historic [`/status_changes`](/provider#status-changes) enpdoint, though `/status_changes` is the source of truth if there are discrepancies. -As with other MDS APIs, `/vehicles` is intended for use by regulators, not by the general public. `/vehicles` can be deployed by providers as a standalone MDS endpoint for agencies without requiring the use of other endpoints, due to the [modularity](/README.md#modularity) of MDS. See our [MDS Vehicles Guide](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/MDS-Vehicles) for how this compares to GBFS `/free_bike_status`. Note that using authenticated `/vehicles` does not replace the role of a public [GBFS][gbfs] feed in enabling consumer-facing applications. +As with other MDS APIs, `/vehicles` is intended for use by regulators, not by the general public. `/vehicles` can be deployed by providers as a standalone MDS endpoint for agencies without requiring the use of other endpoints, due to the [modularity](/README.md#modularity) of MDS. See our [MDS Vehicles Guide](https://github.com/openmobilityfoundation/mobility-data-specification/wiki/MDS-Vehicles) for how this compares to GBFS `/free_bike_status`. Note that using authenticated `/vehicles` does not replace the role of a public [GBFS][gbfs] feed in enabling consumer-facing applications. If a provider is using both `/vehicles` and GBFS endpoints, the `/vehicles` endpoint should be considered source of truth regarding an agency's compliance checks. In addition to the standard [Provider payload wrapper](#response-format), responses from this endpoint should contain the last update timestamp and amount of time until the next update in accordance with the [Data Latency Requirements][data-latency]: @@ -520,7 +561,7 @@ In addition to the standard [Provider payload wrapper](#response-format), respon **Endpoint:** `/vehicles` **Method:** `GET` -**[Beta feature][beta]:** Yes (as of 0.4.1) +**[Beta feature][beta]:** No (as of 1.2.0) **Schema:** [`vehicles` schema][vehicles-schema] **`data` Payload:** `{ "vehicles": [] }`, an array of objects with the following structure @@ -535,8 +576,8 @@ In addition to the standard [Provider payload wrapper](#response-format), respon | `last_event_time` | [timestamp][ts] | Required | Date/time when last state change occurred. See [Event Times][event-times] | | `last_vehicle_state` | Enum | Required | [Vehicle state][vehicle-states] of most recent state change. | | `last_event_types` | Enum[] | Required | [Vehicle event(s)][vehicle-events] of most recent state change, allowable values determined by `last_vehicle_state`. | -| `last_event_location` | GeoJSON [Point Feature][geo]| Required | Location of vehicle's last event. See also [Stop-based Geographic Data][stop-based-geo]. | -| `current_location` | GeoJSON [Point Feature][geo] | Required if Applicable | Current location of vehicle if different from last event, and the vehicle is not currently on a trip. See also [Stop-based Geographic Data][stop-based-geo]. | +| `last_event_location` | GeoJSON [Point Feature][point-geo]| Required | Location of vehicle's last event. See also [Stop-based Geographic Data][stop-based-geo]. | +| `current_location` | GeoJSON [Point Feature][point-geo] | Required if Applicable | Current location of vehicle if different from last event, and the vehicle is not currently on a trip. See also [Stop-based Geographic Data][stop-based-geo]. | | `battery_pct` | Float | Required if Applicable | Percent battery charge of device, expressed between 0 and 1 | [Top][toc] @@ -552,7 +593,6 @@ In addition to the standard [Provider payload wrapper](#response-format), respon [event-times]: #event-times [gbfs]: https://github.com/NABSA/gbfs [general-information]: /general-information.md -[geo]: /general-information.md#geographic-data [geography-driven-events]: /general-information.md#geography-driven-events [geojson-feature-collection]: https://tools.ietf.org/html/rfc7946#section-3.3 [iana]: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml @@ -561,6 +601,7 @@ In addition to the standard [Provider payload wrapper](#response-format), respon [json-api-pagination]: http://jsonapi.org/format/#fetching-pagination [json-schema]: https://json-schema.org [muni-boundary]: #municipality-boundary +[point-geo]: /general-information.md#geographic-telemetry-data [propulsion-types]: /general-information.md#propulsion-types [responses]: /general-information.md#responses [status]: #status-changes diff --git a/provider/events.json b/provider/events.json index 13a0aae3..b97fe883 100644 --- a/provider/events.json +++ b/provider/events.json @@ -20,7 +20,7 @@ "type": "number", "description": "Integer milliseconds since Unix epoch", "multipleOf": 1.0, - "minimum": 0 + "minimum": 1514764800000 }, "uuid": { "$id": "#/definitions/uuid", @@ -37,41 +37,76 @@ "type": "string", "description": "The version of MDS this data represents", "examples": [ - "1.1.0" + "1.2.0" ], - "pattern": "^1\\.1\\.[0-9]+$" + "pattern": "^1\\.2\\.[0-9]+$" }, - "Point": { - "$id": "#/definitions/Point", - "title": "GeoJSON Point", + "MDS_Feature_Point": { + "$id": "#/definitions/MDS_Feature_Point", + "title": "MDS GeoJSON Feature Point", "type": "object", "required": [ "type", - "coordinates" + "properties", + "geometry" ], "properties": { "type": { "type": "string", "enum": [ - "Point" + "Feature" ] }, - "coordinates": { - "type": "array", - "minItems": 2, - "items": [ + "id": { + "oneOf": [ { - "type": "number", - "minimum": -180.0, - "maximum": 180.0 + "type": "number" }, { - "type": "number", - "minimum": -90.0, - "maximum": 90.0 + "type": "string" } + ] + }, + "properties": { + "type": "object", + "required": [ + "timestamp" ], - "maxItems": 2 + "properties": { + "timestamp": { + "$ref": "#/definitions/timestamp" + }, + "stop_id": { + "$ref": "#/definitions/uuid" + }, + "altitude": { + "type": "number", + "description": "Altitude above mean sea level in meters" + }, + "heading": { + "type": "number", + "description": "Degrees - clockwise starting at 0 degrees at true North" + }, + "speed": { + "type": "number", + "description": "Estimated speed in meters / sec as reported by the GPS chipset" + }, + "accuracy": { + "type": "number", + "description": "Horizontal accuracy, in meters" + }, + "hdop": { + "type": "number", + "description": "Horizontal GPS or GNSS accuracy value" + }, + "satellites": { + "type": "integer", + "description": "Number of GPS or GNSS satellites" + } + } + }, + "geometry": { + "$ref": "#/definitions/Point" }, "bbox": { "type": "array", @@ -82,38 +117,37 @@ } } }, - "MDS_Feature_Point": { - "$id": "#/definitions/MDS_Feature_Point", - "title": "MDS GeoJSON Feature Point", + "Point": { + "$id": "#/definitions/Point", + "title": "GeoJSON Point", "type": "object", "required": [ "type", - "properties", - "geometry" + "coordinates" ], "properties": { "type": { "type": "string", "enum": [ - "Feature" + "Point" ] }, - "properties": { - "type": "object", - "required": [ - "timestamp" - ], - "properties": { - "timestamp": { - "$ref": "#/definitions/timestamp" + "coordinates": { + "type": "array", + "minItems": 2, + "items": [ + { + "type": "number", + "minimum": -180.0, + "maximum": 180.0 }, - "stop_id": { - "$ref": "#/definitions/uuid" + { + "type": "number", + "minimum": -90.0, + "maximum": 90.0 } - } - }, - "geometry": { - "$ref": "#/definitions/Point" + ], + "maxItems": 2 }, "bbox": { "type": "array", @@ -151,6 +185,7 @@ "description": "The type of vehicle", "enum": [ "bicycle", + "cargo_bicycle", "car", "scooter", "moped", @@ -482,6 +517,7 @@ "contains": { "enum": [ "agency_pick_up", + "comms_restored", "compliance_pick_up", "decommissioned", "located", diff --git a/provider/status_changes.json b/provider/status_changes.json index c9da9584..68faca22 100644 --- a/provider/status_changes.json +++ b/provider/status_changes.json @@ -20,7 +20,7 @@ "type": "number", "description": "Integer milliseconds since Unix epoch", "multipleOf": 1.0, - "minimum": 0 + "minimum": 1514764800000 }, "uuid": { "$id": "#/definitions/uuid", @@ -37,41 +37,76 @@ "type": "string", "description": "The version of MDS this data represents", "examples": [ - "1.1.0" + "1.2.0" ], - "pattern": "^1\\.1\\.[0-9]+$" + "pattern": "^1\\.2\\.[0-9]+$" }, - "Point": { - "$id": "#/definitions/Point", - "title": "GeoJSON Point", + "MDS_Feature_Point": { + "$id": "#/definitions/MDS_Feature_Point", + "title": "MDS GeoJSON Feature Point", "type": "object", "required": [ "type", - "coordinates" + "properties", + "geometry" ], "properties": { "type": { "type": "string", "enum": [ - "Point" + "Feature" ] }, - "coordinates": { - "type": "array", - "minItems": 2, - "items": [ + "id": { + "oneOf": [ { - "type": "number", - "minimum": -180.0, - "maximum": 180.0 + "type": "number" }, { - "type": "number", - "minimum": -90.0, - "maximum": 90.0 + "type": "string" } + ] + }, + "properties": { + "type": "object", + "required": [ + "timestamp" ], - "maxItems": 2 + "properties": { + "timestamp": { + "$ref": "#/definitions/timestamp" + }, + "stop_id": { + "$ref": "#/definitions/uuid" + }, + "altitude": { + "type": "number", + "description": "Altitude above mean sea level in meters" + }, + "heading": { + "type": "number", + "description": "Degrees - clockwise starting at 0 degrees at true North" + }, + "speed": { + "type": "number", + "description": "Estimated speed in meters / sec as reported by the GPS chipset" + }, + "accuracy": { + "type": "number", + "description": "Horizontal accuracy, in meters" + }, + "hdop": { + "type": "number", + "description": "Horizontal GPS or GNSS accuracy value" + }, + "satellites": { + "type": "integer", + "description": "Number of GPS or GNSS satellites" + } + } + }, + "geometry": { + "$ref": "#/definitions/Point" }, "bbox": { "type": "array", @@ -82,38 +117,37 @@ } } }, - "MDS_Feature_Point": { - "$id": "#/definitions/MDS_Feature_Point", - "title": "MDS GeoJSON Feature Point", + "Point": { + "$id": "#/definitions/Point", + "title": "GeoJSON Point", "type": "object", "required": [ "type", - "properties", - "geometry" + "coordinates" ], "properties": { "type": { "type": "string", "enum": [ - "Feature" + "Point" ] }, - "properties": { - "type": "object", - "required": [ - "timestamp" - ], - "properties": { - "timestamp": { - "$ref": "#/definitions/timestamp" + "coordinates": { + "type": "array", + "minItems": 2, + "items": [ + { + "type": "number", + "minimum": -180.0, + "maximum": 180.0 }, - "stop_id": { - "$ref": "#/definitions/uuid" + { + "type": "number", + "minimum": -90.0, + "maximum": 90.0 } - } - }, - "geometry": { - "$ref": "#/definitions/Point" + ], + "maxItems": 2 }, "bbox": { "type": "array", @@ -151,6 +185,7 @@ "description": "The type of vehicle", "enum": [ "bicycle", + "cargo_bicycle", "car", "scooter", "moped", diff --git a/provider/stops.json b/provider/stops.json index d99c99ba..5f913970 100644 --- a/provider/stops.json +++ b/provider/stops.json @@ -20,7 +20,7 @@ "type": "number", "description": "Integer milliseconds since Unix epoch", "multipleOf": 1.0, - "minimum": 0 + "minimum": 1514764800000 }, "uuid": { "$id": "#/definitions/uuid", @@ -37,41 +37,76 @@ "type": "string", "description": "The version of MDS this data represents", "examples": [ - "1.1.0" + "1.2.0" ], - "pattern": "^1\\.1\\.[0-9]+$" + "pattern": "^1\\.2\\.[0-9]+$" }, - "Point": { - "$id": "#/definitions/Point", - "title": "GeoJSON Point", + "MDS_Feature_Point": { + "$id": "#/definitions/MDS_Feature_Point", + "title": "MDS GeoJSON Feature Point", "type": "object", "required": [ "type", - "coordinates" + "properties", + "geometry" ], "properties": { "type": { "type": "string", "enum": [ - "Point" + "Feature" ] }, - "coordinates": { - "type": "array", - "minItems": 2, - "items": [ + "id": { + "oneOf": [ { - "type": "number", - "minimum": -180.0, - "maximum": 180.0 + "type": "number" }, { - "type": "number", - "minimum": -90.0, - "maximum": 90.0 + "type": "string" } + ] + }, + "properties": { + "type": "object", + "required": [ + "timestamp" ], - "maxItems": 2 + "properties": { + "timestamp": { + "$ref": "#/definitions/timestamp" + }, + "stop_id": { + "$ref": "#/definitions/uuid" + }, + "altitude": { + "type": "number", + "description": "Altitude above mean sea level in meters" + }, + "heading": { + "type": "number", + "description": "Degrees - clockwise starting at 0 degrees at true North" + }, + "speed": { + "type": "number", + "description": "Estimated speed in meters / sec as reported by the GPS chipset" + }, + "accuracy": { + "type": "number", + "description": "Horizontal accuracy, in meters" + }, + "hdop": { + "type": "number", + "description": "Horizontal GPS or GNSS accuracy value" + }, + "satellites": { + "type": "integer", + "description": "Number of GPS or GNSS satellites" + } + } + }, + "geometry": { + "$ref": "#/definitions/Point" }, "bbox": { "type": "array", @@ -82,38 +117,37 @@ } } }, - "MDS_Feature_Point": { - "$id": "#/definitions/MDS_Feature_Point", - "title": "MDS GeoJSON Feature Point", + "Point": { + "$id": "#/definitions/Point", + "title": "GeoJSON Point", "type": "object", "required": [ "type", - "properties", - "geometry" + "coordinates" ], "properties": { "type": { "type": "string", "enum": [ - "Feature" + "Point" ] }, - "properties": { - "type": "object", - "required": [ - "timestamp" - ], - "properties": { - "timestamp": { - "$ref": "#/definitions/timestamp" + "coordinates": { + "type": "array", + "minItems": 2, + "items": [ + { + "type": "number", + "minimum": -180.0, + "maximum": 180.0 }, - "stop_id": { - "$ref": "#/definitions/uuid" + { + "type": "number", + "minimum": -90.0, + "maximum": 90.0 } - } - }, - "geometry": { - "$ref": "#/definitions/Point" + ], + "maxItems": 2 }, "bbox": { "type": "array", @@ -285,6 +319,11 @@ "type": "integer", "minimum": 0 }, + "cargo_bicycle": { + "$id": "#/definitions/vehicle_type_counts/properties/cargo_bicycle", + "type": "integer", + "minimum": 0 + }, "car": { "$id": "#/definitions/vehicle_type_counts/properties/car", "type": "integer", diff --git a/provider/trips.json b/provider/trips.json index be2a15c7..f89c8748 100644 --- a/provider/trips.json +++ b/provider/trips.json @@ -20,7 +20,7 @@ "type": "number", "description": "Integer milliseconds since Unix epoch", "multipleOf": 1.0, - "minimum": 0 + "minimum": 1514764800000 }, "uuid": { "$id": "#/definitions/uuid", @@ -37,41 +37,76 @@ "type": "string", "description": "The version of MDS this data represents", "examples": [ - "1.1.0" + "1.2.0" ], - "pattern": "^1\\.1\\.[0-9]+$" + "pattern": "^1\\.2\\.[0-9]+$" }, - "Point": { - "$id": "#/definitions/Point", - "title": "GeoJSON Point", + "MDS_Feature_Point": { + "$id": "#/definitions/MDS_Feature_Point", + "title": "MDS GeoJSON Feature Point", "type": "object", "required": [ "type", - "coordinates" + "properties", + "geometry" ], "properties": { "type": { "type": "string", "enum": [ - "Point" + "Feature" ] }, - "coordinates": { - "type": "array", - "minItems": 2, - "items": [ + "id": { + "oneOf": [ { - "type": "number", - "minimum": -180.0, - "maximum": 180.0 + "type": "number" }, { - "type": "number", - "minimum": -90.0, - "maximum": 90.0 + "type": "string" } + ] + }, + "properties": { + "type": "object", + "required": [ + "timestamp" ], - "maxItems": 2 + "properties": { + "timestamp": { + "$ref": "#/definitions/timestamp" + }, + "stop_id": { + "$ref": "#/definitions/uuid" + }, + "altitude": { + "type": "number", + "description": "Altitude above mean sea level in meters" + }, + "heading": { + "type": "number", + "description": "Degrees - clockwise starting at 0 degrees at true North" + }, + "speed": { + "type": "number", + "description": "Estimated speed in meters / sec as reported by the GPS chipset" + }, + "accuracy": { + "type": "number", + "description": "Horizontal accuracy, in meters" + }, + "hdop": { + "type": "number", + "description": "Horizontal GPS or GNSS accuracy value" + }, + "satellites": { + "type": "integer", + "description": "Number of GPS or GNSS satellites" + } + } + }, + "geometry": { + "$ref": "#/definitions/Point" }, "bbox": { "type": "array", @@ -82,38 +117,37 @@ } } }, - "MDS_Feature_Point": { - "$id": "#/definitions/MDS_Feature_Point", - "title": "MDS GeoJSON Feature Point", + "Point": { + "$id": "#/definitions/Point", + "title": "GeoJSON Point", "type": "object", "required": [ "type", - "properties", - "geometry" + "coordinates" ], "properties": { "type": { "type": "string", "enum": [ - "Feature" + "Point" ] }, - "properties": { - "type": "object", - "required": [ - "timestamp" - ], - "properties": { - "timestamp": { - "$ref": "#/definitions/timestamp" + "coordinates": { + "type": "array", + "minItems": 2, + "items": [ + { + "type": "number", + "minimum": -180.0, + "maximum": 180.0 }, - "stop_id": { - "$ref": "#/definitions/uuid" + { + "type": "number", + "minimum": -90.0, + "maximum": 90.0 } - } - }, - "geometry": { - "$ref": "#/definitions/Point" + ], + "maxItems": 2 }, "bbox": { "type": "array", @@ -197,6 +231,7 @@ "description": "The type of vehicle", "enum": [ "bicycle", + "cargo_bicycle", "car", "scooter", "moped", diff --git a/provider/vehicles.json b/provider/vehicles.json index d8921d88..6bda6bc3 100644 --- a/provider/vehicles.json +++ b/provider/vehicles.json @@ -20,7 +20,7 @@ "type": "number", "description": "Integer milliseconds since Unix epoch", "multipleOf": 1.0, - "minimum": 0 + "minimum": 1514764800000 }, "uuid": { "$id": "#/definitions/uuid", @@ -37,41 +37,76 @@ "type": "string", "description": "The version of MDS this data represents", "examples": [ - "1.1.0" + "1.2.0" ], - "pattern": "^1\\.1\\.[0-9]+$" + "pattern": "^1\\.2\\.[0-9]+$" }, - "Point": { - "$id": "#/definitions/Point", - "title": "GeoJSON Point", + "MDS_Feature_Point": { + "$id": "#/definitions/MDS_Feature_Point", + "title": "MDS GeoJSON Feature Point", "type": "object", "required": [ "type", - "coordinates" + "properties", + "geometry" ], "properties": { "type": { "type": "string", "enum": [ - "Point" + "Feature" ] }, - "coordinates": { - "type": "array", - "minItems": 2, - "items": [ + "id": { + "oneOf": [ { - "type": "number", - "minimum": -180.0, - "maximum": 180.0 + "type": "number" }, { - "type": "number", - "minimum": -90.0, - "maximum": 90.0 + "type": "string" } + ] + }, + "properties": { + "type": "object", + "required": [ + "timestamp" ], - "maxItems": 2 + "properties": { + "timestamp": { + "$ref": "#/definitions/timestamp" + }, + "stop_id": { + "$ref": "#/definitions/uuid" + }, + "altitude": { + "type": "number", + "description": "Altitude above mean sea level in meters" + }, + "heading": { + "type": "number", + "description": "Degrees - clockwise starting at 0 degrees at true North" + }, + "speed": { + "type": "number", + "description": "Estimated speed in meters / sec as reported by the GPS chipset" + }, + "accuracy": { + "type": "number", + "description": "Horizontal accuracy, in meters" + }, + "hdop": { + "type": "number", + "description": "Horizontal GPS or GNSS accuracy value" + }, + "satellites": { + "type": "integer", + "description": "Number of GPS or GNSS satellites" + } + } + }, + "geometry": { + "$ref": "#/definitions/Point" }, "bbox": { "type": "array", @@ -82,38 +117,37 @@ } } }, - "MDS_Feature_Point": { - "$id": "#/definitions/MDS_Feature_Point", - "title": "MDS GeoJSON Feature Point", + "Point": { + "$id": "#/definitions/Point", + "title": "GeoJSON Point", "type": "object", "required": [ "type", - "properties", - "geometry" + "coordinates" ], "properties": { "type": { "type": "string", "enum": [ - "Feature" + "Point" ] }, - "properties": { - "type": "object", - "required": [ - "timestamp" - ], - "properties": { - "timestamp": { - "$ref": "#/definitions/timestamp" + "coordinates": { + "type": "array", + "minItems": 2, + "items": [ + { + "type": "number", + "minimum": -180.0, + "maximum": 180.0 }, - "stop_id": { - "$ref": "#/definitions/uuid" + { + "type": "number", + "minimum": -90.0, + "maximum": 90.0 } - } - }, - "geometry": { - "$ref": "#/definitions/Point" + ], + "maxItems": 2 }, "bbox": { "type": "array", @@ -274,6 +308,7 @@ "description": "The type of vehicle", "enum": [ "bicycle", + "cargo_bicycle", "car", "scooter", "moped", @@ -394,8 +429,12 @@ "last_vehicle_state": { "enum": [ "available", + "elsewhere", "non_operational", - "reserved" + "on_trip", + "removed", + "reserved", + "unknown" ] } } @@ -492,6 +531,7 @@ "contains": { "enum": [ "agency_pick_up", + "comms_restored", "compliance_pick_up", "decommissioned", "located", diff --git a/providers.csv b/providers.csv index aa612664..dad60d11 100644 --- a/providers.csv +++ b/providers.csv @@ -22,10 +22,12 @@ Baus,638ba503-4006-4c53-bf34-cd625cc03d61,https://bausmoves.com,https://mds.bird Bolt Technology,7ea695ca-9de7-4b3b-9f3c-241b2045a1fe,https://bolt.eu,https://mds.bolt.eu,https://mds.bolt.eu/gbfs/1 Grin,8a0ecfce-5fb3-451e-8069-434b79c8b01a,https://ongrin.com,https://open-data.grow.mobi/mds/,https://open-data.grow.mobi/api/v1/gbfs/ Dott,0a899e3a-705a-46f2-9189-d78cc83a2db4,https://ridedott.com,https://mds.api.ridedott.com,https://gbfs.api.ridedott.com -Superpedestrian,420e6e94-55a6-4946-b6b3-4398fe22e912,https://www.superpedestrian.com,https://wrangler-mds-production.herokuapp.com/mds, +Superpedestrian,420e6e94-55a6-4946-b6b3-4398fe22e912,https://www.superpedestrian.com,https://mds.linkyour.city/mds, Circ,03d5d605-e5c9-45a1-a1dd-144aa8649525,https://www.circ.com,https://mds.bird.co, GIG,42475742-8618-4fc0-aa8f-3b3948b84b85,https://gigcarshare.com/,https://global.us.prod.svc.ridecell.io/reporting/api/mds, Zisch,fb62cfe6-757c-4e5b-a264-18b43b3fc40b,https://www.e-zisch.ch,https://mds.bird.co, Seven Group,7fca7812-c718-48dd-b9f2-d1ae160f2ae4,https://www.group-seven.ch,https://mds.bird.co, Pony,f190d330-b49e-4590-871b-0bcbec565a8c,https://getapony.com/,https://mds.getapony.com/v1, SHARE-NOW,d8f880d6-96d8-4cdc-8069-32a683b2bce6,https://www.share-now.com,https://external.share-now.com/api/rental/mds/,https://external.share-now.com/api/rental/mds/gbfs/ +Boaz Bikes,7c96bc58-fb63-433a-b77f-84ccb1c9d737,https://www.boazbikes.com/,https://mds.movatic.co/,https://gbsf.movatic.co/en/1.1/576347857979998215 +VeoRide INC.,6e39f9db-751b-5cea-ae7e-486f579a56bc,https://www.veoride.com/,https://cluster-prod.veoride.com/api/shares/mds,https://cluster-prod.veoride.com/api/shares/gbfs diff --git a/schema/README.md b/schema/README.md index ccb9b892..04200056 100644 --- a/schema/README.md +++ b/schema/README.md @@ -7,10 +7,10 @@ This directory contains the templates and code that _generate_ the official JSON At a command prompt within this `schema` directory run: ```bash -python generate_schemas.py [--agency] [--policy] [--provider] +python generate_schemas.py [--agency] [--geography] [--policy] [--provider] ``` -The optional flags `--agency`, `--policy`, and `--provider` can be used to specify which +The optional flags `--agency`, `--geography`, `--policy`, and `--provider` can be used to specify which set of schemas to generate. The default is to generate all schemas. ## Updating Schemas diff --git a/schema/agency.py b/schema/agency.py index bbd34968..9c7e41c0 100644 --- a/schema/agency.py +++ b/schema/agency.py @@ -7,6 +7,16 @@ import common +def vehicle_telemetry(): + telemetry = common.load_definitions("telemetry") + vehicle_telemetry = common.load_definitions("vehicle_telemetry") + + # merge the standard telemetry props into vehicle_telemetry.gps + vehicle_telemetry["properties"]["gps"]["properties"].update(telemetry["properties"]) + + return vehicle_telemetry + + def get_stops_schema(): """ Create the schema for the Agency GET /stops endpoint. @@ -113,10 +123,10 @@ def post_vehicle_event_schema(): # load schema template and insert definitions schema = common.load_json("./templates/agency/post_vehicle_event.json") definitions = common.load_definitions( - "telemetry", "timestamp", "uuid" ) + definitions["vehicle_telemetry"] = vehicle_telemetry() schema["definitions"].update(definitions) # merge the state machine definitions and transition combinations rule @@ -139,10 +149,10 @@ def post_vehicle_telemetry_schema(): # load schema template and insert definitions schema = common.load_json("./templates/agency/post_vehicle_telemetry.json") definitions = common.load_definitions( - "telemetry", "timestamp", "uuid" ) + definitions["vehicle_telemetry"] = vehicle_telemetry() schema["definitions"].update(definitions) # verify and return diff --git a/schema/common.py b/schema/common.py index 837fd964..cd0184fc 100644 --- a/schema/common.py +++ b/schema/common.py @@ -11,6 +11,9 @@ COMMON_DEFINITIONS = {} +MDS_FEATURE_POINT = "MDS_Feature_Point" + + def load_json(path): """ Load a JSON file from disk. @@ -71,14 +74,14 @@ def vehicle_state_machine(vehicle_state=None, vehicle_events=None): return (state_machine_defs, transitions) -def vehicle_type_counts_definition(definitions=None): +def vehicle_type_counts_definition(definitions): """ Generate a definition for a dict of vehicle_type: int. """ vehicle_type_counts = {} def_name = "vehicle_type_counts" def_id = definition_id(def_name) - vehicle_types = definitions["vehicle_type"] if definitions else load_definitions("vehicle_type") + vehicle_types = definitions["vehicle_type"] for vehicle_type in vehicle_types["enum"]: vehicle_type_counts[vehicle_type] = { @@ -128,66 +131,45 @@ def point_definition(): } -def feature_schema(id=None, title=None, geometry=None, properties=None, required=None): +def mds_feature_point_definition(definitions): """ - Get the canonical schema for a GeoJSON Feature, - and make any given modifications. - - :id: overrides the `$id` metadata - :title: overrides the `title` metadata - :geometry: overrides the allowed `geometry` for the Feature - :properties: overrides the `properties` definitions for this Feature - :required: is a list of required :properties: + Create a customized definition of the GeoJSON Feature schema for MDS Points. """ # Get the canonical Feature schema feature = requests.get("http://geojson.org/schema/Feature.json").json() - # Modify some metadata + # Modify metadata feature.pop("$schema") - if id is not None: - feature["$id"] = id - if title is not None: - feature["title"] = title + feature["$id"] = definition_id(MDS_FEATURE_POINT) + feature["title"] = "MDS GeoJSON Feature Point" - if geometry is not None: - feature["properties"]["geometry"] = geometry + # Only allow GeoJSON Point feature geometry + feature["properties"]["geometry"] = { "$ref": definition_id("Point") } + # Modfy properties definition/requirements f_properties = feature["properties"]["properties"] - if required is not None: - del f_properties["oneOf"] - f_properties["type"] = "object" - f_properties["required"] = required - if properties is not None: - f_properties["properties"] = properties + del f_properties["oneOf"] + f_properties["type"] = "object" - return feature + # Point features must include the timestamp + f_properties["required"] = ["timestamp"] - -def mds_feature_point_definition(): - """ - Create a customized definition of the GeoJSON Feature schema for MDS Points. - """ - name = "MDS_Feature_Point" - return { - name: feature_schema( - id = definition_id(name), - title = "MDS GeoJSON Feature Point", - # Only allow GeoJSON Point feature geometry - geometry = { "$ref": definition_id("Point") }, - properties = { - "timestamp": { - "$ref": definition_id("timestamp") - }, - # Locations corresponding to Stops must include a `stop_id` reference - "stop_id": { - "$ref": definition_id("uuid") - } - }, - # Point features *must* include the `timestamp` - required = ["timestamp"] - ) + f_properties["properties"] = { + "timestamp": { + "$ref": definition_id("timestamp") + }, + # Locations corresponding to Stops must include a stop_id reference + "stop_id": { + "$ref": definition_id("uuid") + } } + # merge telemetry props + telemetry = definitions["telemetry"] + f_properties["properties"].update(telemetry["properties"]) + + return {MDS_FEATURE_POINT: feature} + def stop_definitions(): """ @@ -200,9 +182,9 @@ def stop_definitions(): "timestamp", "uuid", "uuid_array", - "vehicle_type_counts" + "vehicle_type_counts", + MDS_FEATURE_POINT ) - definitions.update(mds_feature_point_definition()) return definitions @@ -238,7 +220,7 @@ def load_definitions(*args, allow_null=False): common_definitions = common["definitions"] # MDS specific geography definition - mds_feature = mds_feature_point_definition() + mds_feature = mds_feature_point_definition(common_definitions) common_definitions.update(mds_feature) # vehicle_type -> count definition diff --git a/schema/generate_schemas.py b/schema/generate_schemas.py index cfe14285..01454588 100644 --- a/schema/generate_schemas.py +++ b/schema/generate_schemas.py @@ -2,12 +2,13 @@ Generate the JSON Schema documents for MDS endpoints. USAGE: - python generate_schemas.py [--agency] [--policy] [--provider] + python generate_schemas.py [--agency] [--geography] [--policy] [--provider] """ import sys import agency +import geography import policy import provider @@ -15,12 +16,16 @@ if __name__ == "__main__": if len(sys.argv) == 1: agency.write_schema_files() + geography.write_schema_files() policy.write_schema_files() provider.write_schema_files() else: if "--agency" in sys.argv: agency.write_schema_files() sys.argv.remove("--agency") + if "--geography" in sys.argv: + geography.write_schema_files() + sys.argv.remove("--geography") if "--policy" in sys.argv: policy.write_schema_files() sys.argv.remove("--policy") diff --git a/schema/geography.py b/schema/geography.py new file mode 100644 index 00000000..125402fd --- /dev/null +++ b/schema/geography.py @@ -0,0 +1,71 @@ +""" +Schema generators for Geography endpoints. +""" + +import json + +import common + + +def geography_schema(): + """ + Create the schema for the Geography endpoint. + """ + # load schema template and insert definitions + schema = common.load_json("./templates/geography/geography.json") + definitions = common.load_definitions( + "string", + "timestamp", + "uuid", + "version" + ) + definitions.update(common.load_definitions( + "timestamp", + "uuid_array", + allow_null=True + )) + schema["definitions"].update(definitions) + + # verify and return + return common.check_schema(schema) + + +def geographies_schema(): + """ + Create the schema for the Geographies endpoint. + """ + # load schema template and insert definitions from geography + geography = geography_schema() + schema = common.load_json("./templates/geography/geographies.json") + schema["definitions"].update(geography["definitions"]) + + return common.check_schema(schema) + + +def schema_generators(): + """ + The dict of schema generators for Geography. + + The key is the name of the schema file/template file. + The value is the generator function, taking a dict of common definitions as an argument. + The generator function should return the complete, validated schema document as a dict. + """ + return { + "geography": geography_schema, + "geographies": geographies_schema + } + + +def write_schema_files(): + """ + Create each of the Geography endpoint schema files in the appropriate directory. + """ + print("\nStarting to generate Geography JSON Schemas...\n") + + for name, generator in schema_generators().items(): + schema = generator() + with open(f"../geography/{name}.json", "w") as schemafile: + schemafile.write(json.dumps(schema, indent=2)) + print(f"Wrote {name}.json") + + print("\nFinished generating Geography JSON Schemas") diff --git a/schema/provider.py b/schema/provider.py index b682ee04..cb8efad9 100644 --- a/schema/provider.py +++ b/schema/provider.py @@ -48,10 +48,10 @@ def endpoint_schema(endpoint, extra_definitions={}): "string", "timestamp", "uuid", - "version" + "version", + common.MDS_FEATURE_POINT ) definitions.update(common.point_definition()) - definitions.update(common.mds_feature_point_definition()) definitions.update(extra_definitions) endpoint_schema = common.load_json(f"./templates/provider/{endpoint}.json") diff --git a/schema/templates/agency/post_vehicle_event.json b/schema/templates/agency/post_vehicle_event.json index c2880b9c..d7b4dfdc 100644 --- a/schema/templates/agency/post_vehicle_event.json +++ b/schema/templates/agency/post_vehicle_event.json @@ -21,7 +21,7 @@ "$ref": "#/definitions/timestamp" }, "telemetry": { - "$ref": "#/definitions/telemetry" + "$ref": "#/definitions/vehicle_telemetry" }, "trip_id": { "$ref": "#/definitions/uuid" diff --git a/schema/templates/agency/post_vehicle_telemetry.json b/schema/templates/agency/post_vehicle_telemetry.json index 39e93dbf..f7ad99f9 100644 --- a/schema/templates/agency/post_vehicle_telemetry.json +++ b/schema/templates/agency/post_vehicle_telemetry.json @@ -11,7 +11,7 @@ "data": { "type": "array", "items": { - "$ref": "#/definitions/telemetry" + "$ref": "#/definitions/vehicle_telemetry" } } }, diff --git a/schema/templates/common.json b/schema/templates/common.json index 20ff387f..08919fd1 100644 --- a/schema/templates/common.json +++ b/schema/templates/common.json @@ -307,71 +307,32 @@ "telemetry": { "$id": "#/definitions/telemetry", "type": "object", - "description": "A vehicle telemetry datum", - "required": [ - "device_id", - "timestamp", - "gps" - ], + "description": "A telemetry datum", "additionalProperties": false, "properties": { - "device_id": { - "$ref": "#/definitions/uuid" + "altitude": { + "type": "number", + "description": "Altitude above mean sea level in meters" }, - "timestamp": { - "$ref": "#/definitions/timestamp" + "heading": { + "type": "number", + "description": "Degrees - clockwise starting at 0 degrees at true North" }, - "gps": { - "type": "object", - "required": [ - "lat", - "lng" - ], - "additionalProperties": false, - "properties": { - "lat": { - "type": "number", - "description": "Latitude of the location", - "minimum": -90, - "maximum": 90 - }, - "lng": { - "type": "number", - "description": "Longitude of the location", - "minimum": -180, - "maximum": 180 - }, - "altitude": { - "type": "number", - "description": "Altitude above mean sea level in meters" - }, - "heading": { - "type": "number", - "description": "Degrees clockwise from true north" - }, - "speed": { - "type": "number", - "description": "Speed in meters/sec" - }, - "accuracy": { - "type": "number", - "description": "Accuracy in meters" - }, - "hdop": { - "type": "number", - "description": "Horizontal GPS or GNSS accuracy value" - }, - "satellites": { - "type": "integer", - "description": "Number of GPS or GNSS satellites" - } - } + "speed": { + "type": "number", + "description": "Estimated speed in meters / sec as reported by the GPS chipset" }, - "charge": { + "accuracy": { "type": "number", - "description": "Fraction of charge of the vehicle (required if applicable", - "minimum": 0, - "maximum": 1 + "description": "Horizontal accuracy, in meters" + }, + "hdop": { + "type": "number", + "description": "Horizontal GPS or GNSS accuracy value" + }, + "satellites": { + "type": "integer", + "description": "Number of GPS or GNSS satellites" } } }, @@ -380,7 +341,7 @@ "type": "number", "description": "Integer milliseconds since Unix epoch", "multipleOf": 1.0, - "minimum": 0 + "minimum": 1514764800000 }, "trip_id_reference": { "description": "Conditionally require a trip_id reference", @@ -631,6 +592,7 @@ "contains": { "enum": [ "agency_pick_up", + "comms_restored", "compliance_pick_up", "decommissioned", "located", @@ -677,12 +639,60 @@ } ] }, + "vehicle_telemetry": { + "$id": "#/definitions/vehicle_telemetry", + "type": "object", + "description": "A telemetry datum for a specific vehicle at a specific time", + "required": [ + "device_id", + "timestamp", + "gps" + ], + "additionalProperties": false, + "properties": { + "device_id": { + "$ref": "#/definitions/uuid" + }, + "timestamp": { + "$ref": "#/definitions/timestamp" + }, + "gps": { + "type": "object", + "required": [ + "lat", + "lng" + ], + "additionalProperties": false, + "properties": { + "lat": { + "type": "number", + "description": "Latitude of the location", + "minimum": -90, + "maximum": 90 + }, + "lng": { + "type": "number", + "description": "Longitude of the location", + "minimum": -180, + "maximum": 180 + } + } + }, + "charge": { + "type": "number", + "description": "Fraction of charge of the vehicle (required if applicable)", + "minimum": 0, + "maximum": 1 + } + } + }, "vehicle_type": { "$id": "#/definitions/vehicle_type", "type": "string", "description": "The type of vehicle", "enum": [ "bicycle", + "cargo_bicycle", "car", "scooter", "moped", @@ -704,9 +714,9 @@ "type": "string", "description": "The version of MDS this data represents", "examples": [ - "1.1.0" + "1.2.0" ], - "pattern": "^1\\.1\\.[0-9]+$" + "pattern": "^1\\.2\\.[0-9]+$" } } } diff --git a/schema/templates/geography/geographies.json b/schema/templates/geography/geographies.json new file mode 100644 index 00000000..13380181 --- /dev/null +++ b/schema/templates/geography/geographies.json @@ -0,0 +1,35 @@ +{ + "$id": "https://raw.githubusercontent.com/openmobilityfoundation/mobility-data-specification/main/geography/geographies.json", + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "The MDS Geographies Schema", + "type": "object", + "definitions": { + }, + "required": [ + "geographies", + "updated", + "version" + ], + "properties": { + "geographies": { + "$id": "#/properties/geographies", + "type": "array", + "title": "The array of policy objects in this payload", + "items": { + "$id": "#/properties/geographies/items", + "$ref": "#/definitions/geography" + } + }, + "updated": { + "$id": "#/properties/updated", + "$ref": "#/definitions/timestamp", + "description": "The timestamp when geographies was last updated" + }, + "version": { + "$id": "#/properties/version", + "$ref": "#/definitions/version", + "description": "The version of MDS that the geographies represents" + } + }, + "additionalProperties": false +} diff --git a/schema/templates/geography/geography.json b/schema/templates/geography/geography.json new file mode 100644 index 00000000..e5987bcc --- /dev/null +++ b/schema/templates/geography/geography.json @@ -0,0 +1,86 @@ +{ + "$id": "https://raw.githubusercontent.com/openmobilityfoundation/mobility-data-specification/main/geography/geography.json", + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "The MDS Geography Schema", + "type": "object", + "definitions": { + "geography": { + "$id": "#/definitions/geography", + "type": "object", + "title": "The geography object schema", + "additionalProperties": false, + "required": [ + "name", + "geography_id", + "geography_json", + "published_date" + ], + "properties": { + "name": { + "$id": "#/definitions/geography/properties/name", + "$ref": "#/definitions/string", + "description": "Name of geography" + }, + "description": { + "$id": "#/definitions/geography/properties/description", + "$ref": "#/definitions/string", + "description": "Description of geography" + }, + "geography_type": { + "$id": "#/definitions/geography/properties/geography_type", + "type": "string", + "description": "The type of geography" + }, + "geography_id": { + "$id": "#/definitions/geography/properties/geography_id", + "$ref": "#/definitions/uuid", + "description": "Unique ID of geography" + }, + "geography_json": { + "$id": "#/definitions/geography/properties/geography_json", + "$ref": "https://geojson.org/schema/FeatureCollection.json", + "description": "The GeoJSON FeatureCollection that defines the geographical coordinates" + }, + "effective_date":{ + "$id": "#/definitions/geography/properties/effective_date", + "$ref": "#/definitions/null_timestamp", + "description": "The date at which a Geography is considered 'live'. Must be at or after published_date." + }, + "published_date": { + "$id": "#/definitions/geography/properties/published_date", + "$ref": "#/definitions/timestamp", + "description": "Timestamp at which the geography was published i.e. made immutable" + }, + "retire_date": { + "$id": "#/definitions/geography/properties/end_date", + "$ref": "#/definitions/null_timestamp", + "description": "Time that the geography is slated to retire. Once the retire date is passed, new policies can no longer reference it and old policies referencing it should be updated. Retired geographies should continue to be returned in the geographies list. Must be after effective_date." + }, + "prev_geographies": { + "$id": "#/definitions/geography/properties/prev_geographies", + "$ref": "#/definitions/null_uuid_array", + "description": "Unique IDs of prior geographies replaced by this one", + "uniqueItems": true + } + } + } + }, + "required": [ + "geography", + "version" + ], + "properties": { + "geography": { + "$id": "#/properties/geography", + "$ref": "#/definitions/geography", + "description": "The geography in this payload", + "additionalProperties": false + }, + "version": { + "$id": "#/properties/version", + "$ref": "#/definitions/version", + "description": "The version of MDS that the geography represents" + } + }, + "additionalProperties": false +} diff --git a/schema/templates/policy/policy.json b/schema/templates/policy/policy.json index 7f23ed44..408b8818 100644 --- a/schema/templates/policy/policy.json +++ b/schema/templates/policy/policy.json @@ -171,6 +171,16 @@ "type": ["null", "integer"], "description": "Maximum value, if applicable (default unlimited)" }, + "inclusive_minimum": { + "$id": "#/definitions/rule/properties/inclusive_minimum", + "type": ["null", "boolean"], + "description": "Whether the rule minimum is considered in-bounds (default true)" + }, + "inclusive_maximum": { + "$id": "#/definitions/rule/properties/inclusive_maximum", + "type": ["null", "boolean"], + "description": "Whether the rule maximum is considered in-bounds (default true)" + }, "rate_amount": { "$id": "#/definitions/rule/properties/rate_amount", "type": ["null", "integer"], @@ -179,13 +189,23 @@ "rate_recurrence": { "$id": "#/definitions/rule/properties/rate_recurrence", "type": ["string", "null"], - "description": "Specify when a rate is applied – either once, or periodically according to a time unit specified using rule_units", + "description": "Specify how a rate is applied – either once, or periodically according to a time unit specified using rule_units", "enum": [ - "once", + "once_on_match", + "once_on_unmatch", "each_time_unit", "per_complete_time_unit" ] }, + "rate_applies_when": { + "$id": "#/definitions/rule/properties/rate_applies_when", + "type": ["string", "null"], + "description": "Specify when a rate is applicable to an event or count: when it's within rule bounds or when it is not", + "enum": [ + "in_bounds", + "out_of_bounds" + ] + }, "start_time": { "$id": "#/definitions/rule/properties/start_time", "$ref": "#/definitions/null_iso_time", @@ -316,15 +336,15 @@ "type": "object", "description": "The data records in this payload", "required": [ - "policy" + "policies" ], "properties": { - "policy": { - "$id": "#/properties/data/properties/policy", + "policies": { + "$id": "#/properties/data/properties/policies", "type": "array", - "title": "The policy payload", + "title": "The array of policy objects in this payload", "items": { - "$id": "#/properties/data/properties/policy/items", + "$id": "#/properties/data/properties/policies/items", "$ref": "#/definitions/policy" } } @@ -348,4 +368,4 @@ } }, "additionalProperties": false -} \ No newline at end of file +} diff --git a/schema/templates/provider/vehicles.json b/schema/templates/provider/vehicles.json index 14d4386f..e9117524 100644 --- a/schema/templates/provider/vehicles.json +++ b/schema/templates/provider/vehicles.json @@ -60,8 +60,12 @@ "last_vehicle_state": { "enum": [ "available", + "elsewhere", "non_operational", - "reserved" + "on_trip", + "removed", + "reserved", + "unknown" ] } } @@ -69,4 +73,4 @@ ] } } -} \ No newline at end of file +}