diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml index c2979504992..28a84953af2 100644 --- a/.github/workflows/cibuild.yml +++ b/.github/workflows/cibuild.yml @@ -20,20 +20,20 @@ jobs: timeout-minutes: 20 steps: # Starting in v2.2 checkout action fetches all tags when fetch-depth=0, for auto-versioning. - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v4 with: fetch-depth: 0 # nodejs is needed because the dynamic download of it via the prettier maven plugin often # times out # Example: https://github.com/opentripplanner/OpenTripPlanner/actions/runs/4490450225/jobs/7897533439 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 18 # Java setup step completes very fast, no need to run in a preconfigured docker container - name: Set up JDK 21 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21 distribution: temurin @@ -65,9 +65,9 @@ jobs: timeout-minutes: 20 runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK 21 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21 distribution: temurin @@ -91,7 +91,7 @@ jobs: steps: - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v4 # this is necessary so that the correct credentials are put into the git configuration # when we push to dev-2.x and push the HTML to the git repo if: github.event_name == 'push' && (github.ref == 'refs/heads/dev-2.x' || github.ref == 'refs/heads/master') @@ -101,7 +101,7 @@ jobs: # was modified last fetch-depth: 1000 - - uses: actions/checkout@v3.3.0 + - uses: actions/checkout@v4 # for a simple PR where we don't push, we don't need any credentials if: github.event_name == 'pull_request' @@ -113,7 +113,7 @@ jobs: if: github.event_name == 'pull_request' run: mkdocs build - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 18 @@ -174,8 +174,8 @@ jobs: if: github.repository_owner == 'opentripplanner' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3.1.0 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 16 - name: Run code generator @@ -184,7 +184,7 @@ jobs: yarn install yarn generate - name: Set up JDK 21 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21 distribution: temurin @@ -199,16 +199,16 @@ jobs: - build-windows - build-linux steps: - - uses: actions/checkout@v3.1.0 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up JDK 21 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21 distribution: temurin cache: maven - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 18 - name: Build container image with Jib, push to Dockerhub diff --git a/.github/workflows/debug-client.yml b/.github/workflows/debug-client.yml index aed7943e63a..de92c121db7 100644 --- a/.github/workflows/debug-client.yml +++ b/.github/workflows/debug-client.yml @@ -30,7 +30,7 @@ jobs: - uses: actions/checkout@v4 if: github.event_name == 'pull_request' - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 18 diff --git a/.github/workflows/performance-test.yml b/.github/workflows/performance-test.yml index a5274d9839f..1b8201a01ae 100644 --- a/.github/workflows/performance-test.yml +++ b/.github/workflows/performance-test.yml @@ -60,14 +60,14 @@ jobs: profile: extended steps: - - uses: actions/checkout@v3.1.0 + - uses: actions/checkout@v4 if: matrix.profile == 'core' || github.ref == 'refs/heads/dev-2.x' with: fetch-depth: 0 - name: Set up JDK if: matrix.profile == 'core' || github.ref == 'refs/heads/dev-2.x' - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 21 distribution: temurin diff --git a/.github/workflows/post-merge.yml b/.github/workflows/post-merge.yml index 1500a0f9d9e..a2f29f0d1b0 100644 --- a/.github/workflows/post-merge.yml +++ b/.github/workflows/post-merge.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 with: token: ${{ secrets.CHANGELOG_TOKEN }} @@ -62,7 +62,7 @@ jobs: git config --global user.email 'serialization-version-bot@opentripplanner.org' - name: Checkout - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 with: token: ${{ secrets.CHANGELOG_TOKEN }} diff --git a/client-next/src/components/MapView/MapView.tsx b/client-next/src/components/MapView/MapView.tsx index 51cb39068f8..96b4e820bae 100644 --- a/client-next/src/components/MapView/MapView.tsx +++ b/client-next/src/components/MapView/MapView.tsx @@ -75,7 +75,7 @@ export function MapView({ }} // it's unfortunate that you have to list these layers here. // maybe there is a way around it: https://github.com/visgl/react-map-gl/discussions/2343 - interactiveLayerIds={['regular-stop', 'vertex', 'edge', 'link']} + interactiveLayerIds={['regular-stop', 'area-stop', 'vertex', 'edge', 'link']} onClick={showFeaturePropPopup} // put lat/long in URL and pan to it on page reload hash={true} diff --git a/doc-templates/Configuration.md b/doc-templates/Configuration.md index eabd4895a1f..6344e270570 100644 --- a/doc-templates/Configuration.md +++ b/doc-templates/Configuration.md @@ -128,8 +128,8 @@ you can run the following bash command: - `head -c 29 Graph.obj ==> OpenTripPlannerGraph;0000007;` (file header) - `head -c 28 Graph.obj | tail -c 7 ==> 0000007` (version id) -The Maven _pom.xml_, the _META-INF/MANIFEST.MF_, the OTP command line(`--serVerId`), log start-up -messages and all OTP APIs can be used to get the OTP Serialization Version Id. +The Maven _pom.xml_, the _META-INF/MANIFEST.MF_, the OTP command line(`--serializationVersionId`), +log start-up messages and all OTP APIs can be used to get the OTP Serialization Version Id. ## Include file directive diff --git a/doc-templates/sandbox/MapboxVectorTilesApi.md b/doc-templates/sandbox/MapboxVectorTilesApi.md new file mode 100644 index 00000000000..dfec1ed085a --- /dev/null +++ b/doc-templates/sandbox/MapboxVectorTilesApi.md @@ -0,0 +1,194 @@ +# Mapbox Vector Tiles API + +## Contact Info + +- HSL, Finland +- Arcadis, US + +## Documentation + +This API produces [Mapbox vector tiles](https://docs.mapbox.com/vector-tiles/reference/), which are +used by [Digitransit-ui](https://github.com/HSLdevcom/digitransit-ui) and +[`otp-react-redux`](https://github.com/opentripplanner/otp-react-redux) to show information about +public transit entities on the map. + +The tiles can be fetched from `/otp/routers/{routerId}/vectorTiles/{layers}/{z}/{x}/{y}.pbf`, +where `layers` is a comma separated list of layer names from the configuration. + +Maplibre/Mapbox GL JS also requires a tilejson.json endpoint which is available at +`/otp/routers/{routerId}/vectorTiles/{layers}/tilejson.json`. + +Translatable fields in the tiles are translated based on the `accept-language` header in requests. +Currently, only the language with the highest priority from the header is used. + +### Configuration + +To enable this you need to add the feature `otp-config.json`. + +```json +// otp-config.json +{ + "otpFeatures": { + "SandboxAPIMapboxVectorTilesApi": true + } +} +``` + +The feature must be configured in `router-config.json` as follows + +```JSON +{ + "vectorTiles": { + "basePath": "/only/configure/if/required", + "layers": [ + { + "name": "stops", + "type": "Stop", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 600 + }, + { + "name": "stations", + "type": "Station", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 12, + "cacheMaxSeconds": 600 + }, + // all rental places: stations and free-floating vehicles + { + "name": "citybikes", + "type": "VehicleRental", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 60, + "expansionFactor": 0.25 + }, + // just free-floating vehicles + { + "name": "rentalVehicles", + "type": "VehicleRentalVehicle", + "mapper": "DigitransitRealtime", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 60 + }, + // just rental stations + { + "name": "rentalStations", + "type": "VehicleRentalStation", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 600 + }, + // Contains just stations and real-time information for them + { + "name": "realtimeRentalStations", + "type": "VehicleRentalStation", + "mapper": "DigitransitRealtime", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 60 + }, + // This exists for backwards compatibility. At some point, we might want + // to add a new real-time parking mapper with better translation support + // and less unnecessary fields. + { + "name": "stadtnaviVehicleParking", + "type": "VehicleParking", + "mapper": "Stadtnavi", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 60, + "expansionFactor": 0.25 + }, + // no real-time, translatable fields are translated based on accept-language header + // and contains less fields than the Stadtnavi mapper + { + "name": "vehicleParking", + "type": "VehicleParking", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 600, + "expansionFactor": 0.25 + }, + { + "name": "vehicleParkingGroups", + "type": "VehicleParkingGroup", + "mapper": "Digitransit", + "maxZoom": 17, + "minZoom": 14, + "cacheMaxSeconds": 600, + "expansionFactor": 0.25 + } + ] + } +} +``` + +For each layer, the configuration includes: + +- `name` which is used in the url to fetch tiles, and as the layer name in the vector tiles. +- `type` which tells the type of the layer. Currently supported: + - `Stop` + - `Station` + - `VehicleRental`: all rental places: stations and free-floating vehicles + - `VehicleRentalVehicle`: free-floating rental vehicles + - `VehicleRentalStation`: rental stations + - `VehicleParking` + - `VehicleParkingGroup` + + + +### Extending + +If more generic layers are created for this API, the code should be moved out from the sandbox, into +the core, perhaps potentially leaving specific property mappers in place. + +#### Creating a new layer + +In order to create a new type of layer, you need to create a new class extending `LayerBuilder`. +You need to implement two methods, `List getGeometries(Envelope query)`, which returns a +list of geometries, with an object of type `T` as their userData in the geometry, +and `double getExpansionFactor()`, which describes how much information outside the tile bounds +should be included. This layer then needs to be added into `VectorTilesResource.layers`, with a +new `LayerType` enum as the key, and the class constructor as the value. + +A new mapper needs to be added every time a new layer is added. See below for information. + +#### Creating a new mapper + +The mapping contains information of what data to include in the vector tiles. The mappers are +defined per layer. + +In order to create a new mapper for a layer, you need to create a new class +extending `PropertyMapper`. In that class, you need to implement the +method `Collection> map(T input)`. The type T is dependent on the layer for which +you implement the mapper for. It needs to return a list of attributes, as key-value pairs which will +be written into the vector tile. + +The mapper needs to be added to the `mappers` map in the layer, with a new `MapperType` enum as the +key, and a function to create the mapper, with a `Graph` object as a parameter, as the value. + +## Changelog + +- 2020-07-09: Initial version of Mapbox vector tiles API +- 2021-05-12: Make expansion factor configurable +- 2021-09-07: Rename `BikeRental` to `VehicleRental` +- 2021-10-13: Correctly serialize the vehicle rental name [#3648](https://github.com/opentripplanner/OpenTripPlanner/pull/3648) +- 2022-01-03: Add support for VehicleParking entities +- 2022-04-27: Read the headsign for frequency-only patterns correctly [#4122](https://github.com/opentripplanner/OpenTripPlanner/pull/4122) +- 2022-08-23: Remove patterns and add route gtfsTypes to stop layer [#4404](https://github.com/opentripplanner/OpenTripPlanner/pull/4404) +- 2022-10-11: Added layer for VehicleParkingGroups [#4510](https://github.com/opentripplanner/OpenTripPlanner/pull/4510) +- 2022-10-14: Add separate layers for vehicle rental place types [#4516](https://github.com/opentripplanner/OpenTripPlanner/pull/4516) +- 2022-10-19 [#4529](https://github.com/opentripplanner/OpenTripPlanner/pull/4529): + * Translatable fields are now translated based on accept-language header + * Added DigitransitRealtime for vehicle rental stations + * Changed old vehicle parking mapper to be Stadtnavi + * Added a new Digitransit vehicle parking mapper with no real-time information and less fields +- 2024-01-22: Make `basePath` configurable [#5627](https://github.com/opentripplanner/OpenTripPlanner/pull/5627) \ No newline at end of file diff --git a/docs/Changelog.md b/docs/Changelog.md index 6f27f60d947..35fc86ec3bc 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -78,6 +78,12 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Add new path for GTFS GraphQL API, remove batch feature [#5581](https://github.com/opentripplanner/OpenTripPlanner/pull/5581) - Restructure walk/bicycle/car preferences in router-config.json [#5582](https://github.com/opentripplanner/OpenTripPlanner/pull/5582) - Revert REST API spelling change of real-time [#5629](https://github.com/opentripplanner/OpenTripPlanner/pull/5629) +- Remove `FareComponent` [#5613](https://github.com/opentripplanner/OpenTripPlanner/pull/5613) +- Add AreaStop layer to new debug frontend [#5636](https://github.com/opentripplanner/OpenTripPlanner/pull/5636) +- Allow configuration of vector tiles base path [#5627](https://github.com/opentripplanner/OpenTripPlanner/pull/5627) +- Change Transmodel API path to `/otp/transmodel/v3` [#5637](https://github.com/opentripplanner/OpenTripPlanner/pull/5637) +- Add flexibleArea to GroupStop Quays [#5625](https://github.com/opentripplanner/OpenTripPlanner/pull/5625) +- Pass-through should override transit-group-priority [#5638](https://github.com/opentripplanner/OpenTripPlanner/pull/5638) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.4.0 (2023-09-13) diff --git a/docs/Configuration.md b/docs/Configuration.md index d43ff150926..858edf0f9b4 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -155,8 +155,8 @@ you can run the following bash command: - `head -c 29 Graph.obj ==> OpenTripPlannerGraph;0000007;` (file header) - `head -c 28 Graph.obj | tail -c 7 ==> 0000007` (version id) -The Maven _pom.xml_, the _META-INF/MANIFEST.MF_, the OTP command line(`--serVerId`), log start-up -messages and all OTP APIs can be used to get the OTP Serialization Version Id. +The Maven _pom.xml_, the _META-INF/MANIFEST.MF_, the OTP command line(`--serializationVersionId`), +log start-up messages and all OTP APIs can be used to get the OTP Serialization Version Id. ## Include file directive diff --git a/docs/RouterConfiguration.md b/docs/RouterConfiguration.md index 23e2e3763d4..62fb5d9617a 100644 --- a/docs/RouterConfiguration.md +++ b/docs/RouterConfiguration.md @@ -67,7 +67,7 @@ A full list of them can be found in the [RouteRequest](RouteRequest.md). |    [hideFeedId](#transmodelApi_hideFeedId) | `boolean` | Hide the FeedId in all API output, and add it to input. | *Optional* | `false` | na | |    [tracingHeaderTags](#transmodelApi_tracingHeaderTags) | `string[]` | Used to group requests when monitoring OTP. | *Optional* | | na | | [updaters](UpdaterConfig.md) | `object[]` | Configuration for the updaters that import various types of data into OTP. | *Optional* | | 1.5 | -| [vectorTileLayers](sandbox/MapboxVectorTilesApi.md) | `object[]` | Configuration of the individual layers for the Mapbox vector tiles. | *Optional* | | 2.0 | +| [vectorTiles](sandbox/MapboxVectorTilesApi.md) | `object` | Vector tile configuration | *Optional* | | na | | [vehicleRentalServiceDirectory](sandbox/VehicleRentalServiceDirectory.md) | `object` | Configuration for the vehicle rental service directory. | *Optional* | | 2.0 | @@ -625,58 +625,61 @@ Used to group requests when monitoring OTP. "transmodelApi" : { "hideFeedId" : true }, - "vectorTileLayers" : [ - { - "name" : "stops", - "type" : "Stop", - "mapper" : "Digitransit", - "maxZoom" : 20, - "minZoom" : 14, - "cacheMaxSeconds" : 600 - }, - { - "name" : "stations", - "type" : "Station", - "mapper" : "Digitransit", - "maxZoom" : 20, - "minZoom" : 12, - "cacheMaxSeconds" : 600 - }, - { - "name" : "rentalPlaces", - "type" : "VehicleRental", - "mapper" : "Digitransit", - "maxZoom" : 20, - "minZoom" : 14, - "cacheMaxSeconds" : 60, - "expansionFactor" : 0.25 - }, - { - "name" : "rentalVehicle", - "type" : "VehicleRentalVehicle", - "mapper" : "Digitransit", - "maxZoom" : 20, - "minZoom" : 14, - "cacheMaxSeconds" : 60 - }, - { - "name" : "rentalStation", - "type" : "VehicleRentalStation", - "mapper" : "Digitransit", - "maxZoom" : 20, - "minZoom" : 14, - "cacheMaxSeconds" : 600 - }, - { - "name" : "vehicleParking", - "type" : "VehicleParking", - "mapper" : "Digitransit", - "maxZoom" : 20, - "minZoom" : 14, - "cacheMaxSeconds" : 60, - "expansionFactor" : 0.25 - } - ], + "vectorTiles" : { + "basePath" : "/otp_ct/vectorTiles", + "layers" : [ + { + "name" : "stops", + "type" : "Stop", + "mapper" : "Digitransit", + "maxZoom" : 20, + "minZoom" : 14, + "cacheMaxSeconds" : 600 + }, + { + "name" : "stations", + "type" : "Station", + "mapper" : "Digitransit", + "maxZoom" : 20, + "minZoom" : 12, + "cacheMaxSeconds" : 600 + }, + { + "name" : "rentalPlaces", + "type" : "VehicleRental", + "mapper" : "Digitransit", + "maxZoom" : 20, + "minZoom" : 14, + "cacheMaxSeconds" : 60, + "expansionFactor" : 0.25 + }, + { + "name" : "rentalVehicle", + "type" : "VehicleRentalVehicle", + "mapper" : "Digitransit", + "maxZoom" : 20, + "minZoom" : 14, + "cacheMaxSeconds" : 60 + }, + { + "name" : "rentalStation", + "type" : "VehicleRentalStation", + "mapper" : "Digitransit", + "maxZoom" : 20, + "minZoom" : 14, + "cacheMaxSeconds" : 600 + }, + { + "name" : "vehicleParking", + "type" : "VehicleParking", + "mapper" : "Digitransit", + "maxZoom" : 20, + "minZoom" : 14, + "cacheMaxSeconds" : 60, + "expansionFactor" : 0.25 + } + ] + }, "timetableUpdates" : { "purgeExpiredData" : false, "maxSnapshotFrequency" : "2s" diff --git a/docs/SandboxExtension.md b/docs/SandboxExtension.md index 38988f8245f..4b978313ca6 100644 --- a/docs/SandboxExtension.md +++ b/docs/SandboxExtension.md @@ -11,7 +11,7 @@ provided "as is". - [Google Cloud Storage](sandbox/GoogleCloudStorage.md) - Enable Google Cloud Storage as a OTP Data Source - [Actuator API](sandbox/ActuatorAPI.md) - API used to check the health status of the OTP instance. -- [Geocoder API](sandbox/GeocoderAPI.md) - Adds an API to search for corners, stops and stations. +- [Geocoder API](sandbox/GeocoderAPI.md) - Adds an API to search for stops and stations. - [Transfer analyser](sandbox/transferanalyzer.md) - Module used for analyzing the transfers between nearby stops generated by routing via OSM data. - [SIRI Updater](sandbox/SiriUpdater.md) - Update OTP with real-time information from a Transmodel SIRI data source. diff --git a/docs/apis/Apis.md b/docs/apis/Apis.md index d9f0467bbca..ab6b41a25cd 100644 --- a/docs/apis/Apis.md +++ b/docs/apis/Apis.md @@ -26,4 +26,4 @@ The OTP REST API used to power many apps and frontends. For years it was the onl OTP programmatically. Over time it has been replaced by the GraphQL APIs and is scheduled to be disabled by default -and eventually removed completely. It's therefore not recommended to use it. \ No newline at end of file +and eventually removed completely. It's therefore not recommended to use it. diff --git a/docs/apis/TransmodelApi.md b/docs/apis/TransmodelApi.md index ce772734276..6f99847798a 100644 --- a/docs/apis/TransmodelApi.md +++ b/docs/apis/TransmodelApi.md @@ -13,12 +13,14 @@ Transmodel (NeTEx) with some limitations/simplification. It provides both a rout Entur provides a [GraphQL explorer](https://api.entur.io/graphql-explorer) where you may browse the GraphQL schema and try your own queries. -When running OTP locally the endpoint is available at: `http://localhost:8080/otp/routers/default/transmodel/index/graphql` +When running OTP locally the endpoint is available at: `http://localhost:8080/otp/transmodel/v3` -### Configuration +**Note!** Versions `v1` and `v2` do not exist in the main OTP git repository, but in +the [Entur fork](https://github.com/entur/OpenTripPlanner) from which this code originates from. -To turn this API off, add the feature `TransmodelGraphQlApi : false` in _otp-config.json_. +### Configuration +To turn this API off, add the feature `TransmodelGraphQlApi : false` in `otp-config.json`. ## Changelog - old diff --git a/docs/examples/ibi/portland/build-config.json b/docs/examples/ibi/portland/build-config.json index 4b3a232ffba..46309d21c59 100644 --- a/docs/examples/ibi/portland/build-config.json +++ b/docs/examples/ibi/portland/build-config.json @@ -6,7 +6,7 @@ "transitFeeds": [ { "type": "gtfs", - "feedId": "trimet", + "feedId": "TriMet", "source": "https://developer.trimet.org/schedule/gtfs.zip" } ] diff --git a/docs/sandbox/MapboxVectorTilesApi.md b/docs/sandbox/MapboxVectorTilesApi.md index 8ef8ee179e7..da9fd1120e1 100644 --- a/docs/sandbox/MapboxVectorTilesApi.md +++ b/docs/sandbox/MapboxVectorTilesApi.md @@ -3,18 +3,21 @@ ## Contact Info - HSL, Finland -- Kyyti Group Oy, Finland -- Hannes Junnila +- Arcadis, US ## Documentation This API produces [Mapbox vector tiles](https://docs.mapbox.com/vector-tiles/reference/), which are -used by eg. [Digitransit-ui](https://github.com/HSLdevcom/digitransit-ui) to show information about +used by [Digitransit-ui](https://github.com/HSLdevcom/digitransit-ui) and +[`otp-react-redux`](https://github.com/opentripplanner/otp-react-redux) to show information about public transit entities on the map. The tiles can be fetched from `/otp/routers/{routerId}/vectorTiles/{layers}/{z}/{x}/{y}.pbf`, where `layers` is a comma separated list of layer names from the configuration. +Maplibre/Mapbox GL JS also requires a tilejson.json endpoint which is available at +`/otp/routers/{routerId}/vectorTiles/{layers}/tilejson.json`. + Translatable fields in the tiles are translated based on the `accept-language` header in requests. Currently, only the language with the highest priority from the header is used. @@ -35,93 +38,96 @@ The feature must be configured in `router-config.json` as follows ```JSON { - "vectorTileLayers": [ - { - "name": "stops", - "type": "Stop", - "mapper": "Digitransit", - "maxZoom": 20, - "minZoom": 14, - "cacheMaxSeconds": 600 - }, - { - "name": "stations", - "type": "Station", - "mapper": "Digitransit", - "maxZoom": 20, - "minZoom": 12, - "cacheMaxSeconds": 600 - }, - // all rental places: stations and free-floating vehicles - { - "name": "citybikes", - "type": "VehicleRental", - "mapper": "Digitransit", - "maxZoom": 20, - "minZoom": 14, - "cacheMaxSeconds": 60, - "expansionFactor": 0.25 - }, - // just free-floating vehicles - { - "name": "rentalVehicles", - "type": "VehicleRentalVehicle", - "mapper": "DigitransitRealtime", - "maxZoom": 20, - "minZoom": 14, - "cacheMaxSeconds": 60 - }, - // just rental stations - { - "name": "rentalStations", - "type": "VehicleRentalStation", - "mapper": "Digitransit", - "maxZoom": 20, - "minZoom": 14, - "cacheMaxSeconds": 600 - }, - // Contains just stations and real-time information for them - { - "name": "realtimeRentalStations", - "type": "VehicleRentalStation", - "mapper": "DigitransitRealtime", - "maxZoom": 20, - "minZoom": 14, - "cacheMaxSeconds": 60 - }, - // This exists for backwards compatibility. At some point, we might want - // to add a new real-time parking mapper with better translation support - // and less unnecessary fields. - { - "name": "stadtnaviVehicleParking", - "type": "VehicleParking", - "mapper": "Stadtnavi", - "maxZoom": 20, - "minZoom": 14, - "cacheMaxSeconds": 60, - "expansionFactor": 0.25 - }, - // no real-time, translatable fields are translated based on accept-language header - // and contains less fields than the Stadtnavi mapper - { - "name": "vehicleParking", - "type": "VehicleParking", - "mapper": "Digitransit", - "maxZoom": 20, - "minZoom": 14, - "cacheMaxSeconds": 600, - "expansionFactor": 0.25 - }, - { - "name": "vehicleParkingGroups", - "type": "VehicleParkingGroup", - "mapper": "Digitransit", - "maxZoom": 17, - "minZoom": 14, - "cacheMaxSeconds": 600, - "expansionFactor": 0.25 - } - ] + "vectorTiles": { + "basePath": "/only/configure/if/required", + "layers": [ + { + "name": "stops", + "type": "Stop", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 600 + }, + { + "name": "stations", + "type": "Station", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 12, + "cacheMaxSeconds": 600 + }, + // all rental places: stations and free-floating vehicles + { + "name": "citybikes", + "type": "VehicleRental", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 60, + "expansionFactor": 0.25 + }, + // just free-floating vehicles + { + "name": "rentalVehicles", + "type": "VehicleRentalVehicle", + "mapper": "DigitransitRealtime", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 60 + }, + // just rental stations + { + "name": "rentalStations", + "type": "VehicleRentalStation", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 600 + }, + // Contains just stations and real-time information for them + { + "name": "realtimeRentalStations", + "type": "VehicleRentalStation", + "mapper": "DigitransitRealtime", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 60 + }, + // This exists for backwards compatibility. At some point, we might want + // to add a new real-time parking mapper with better translation support + // and less unnecessary fields. + { + "name": "stadtnaviVehicleParking", + "type": "VehicleParking", + "mapper": "Stadtnavi", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 60, + "expansionFactor": 0.25 + }, + // no real-time, translatable fields are translated based on accept-language header + // and contains less fields than the Stadtnavi mapper + { + "name": "vehicleParking", + "type": "VehicleParking", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 600, + "expansionFactor": 0.25 + }, + { + "name": "vehicleParkingGroups", + "type": "VehicleParkingGroup", + "mapper": "Digitransit", + "maxZoom": 17, + "minZoom": 14, + "cacheMaxSeconds": 600, + "expansionFactor": 0.25 + } + ] + } } ``` @@ -136,19 +142,91 @@ For each layer, the configuration includes: - `VehicleRentalStation`: rental stations - `VehicleParking` - `VehicleParkingGroup` -- `mapper` which describes the mapper converting the properties from the OTP model entities to the - vector tile properties. Currently `Digitransit` is supported for all layer types. -- `minZoom` and `maxZoom` which describe the zoom levels the layer is active for. -- `cacheMaxSeconds` which sets the cache header in the response. The lowest value of the layers - included is selected. -- `expansionFactor` How far outside its boundaries should the tile contain information. The value is - a fraction of the tile size. If you are having problem with icons and shapes being clipped at tile - edges, then increase this number. + + + + +| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | +|----------------------------------------------------------------|:----------:|--------------------------------------------------------------------------------------------|:----------:|---------------|:-----:| +| [basePath](#vectorTiles_basePath) | `string` | The path of the vector tile source URLs in `tilejson.json`. | *Optional* | | 2.5 | +| [layers](#vectorTiles_layers) | `object[]` | Configuration of the individual layers for the Mapbox vector tiles. | *Optional* | | 2.0 | +|       type = "stop" | `enum` | Type of the layer. | *Required* | | 2.0 | +|       [cacheMaxSeconds](#vectorTiles_layers_0_cacheMaxSeconds) | `integer` | Sets the cache header in the response. | *Optional* | `-1` | 2.0 | +|       [expansionFactor](#vectorTiles_layers_0_expansionFactor) | `double` | How far outside its boundaries should the tile contain information. | *Optional* | `0.25` | 2.0 | +|       [mapper](#vectorTiles_layers_0_mapper) | `string` | Describes the mapper converting from the OTP model entities to the vector tile properties. | *Required* | | 2.0 | +|       maxZoom | `integer` | Maximum zoom levels the layer is active for. | *Optional* | `20` | 2.0 | +|       minZoom | `integer` | Minimum zoom levels the layer is active for. | *Optional* | `9` | 2.0 | +|       name | `string` | Used in the url to fetch tiles, and as the layer name in the vector tiles. | *Required* | | 2.0 | + + +#### Details + +

basePath

+ +**Since version:** `2.5` ∙ **Type:** `string` ∙ **Cardinality:** `Optional` +**Path:** /vectorTiles + +The path of the vector tile source URLs in `tilejson.json`. + +This is useful if you have a proxy setup and rewrite the path that is passed to OTP. + +If you don't configure this optional value then the path returned in `tilejson.json` is in +the format `/otp/routers/default/vectorTiles/layer1,layer2/{z}/{x}/{x}.pbf`. +If you, for example, set a value of `/otp_test/tiles` then the returned path changes to +`/otp_test/tiles/layer1,layer2/{z}/{x}/{x}.pbf`. + +The protocol and host are always read from the incoming HTTP request. If you run OTP behind +a proxy then make sure to set the headers `X-Forwarded-Proto` and `X-Forwarded-Host` to make OTP +return the protocol and host for the original request and not the proxied one. + +**Note:** This does _not_ change the path that OTP itself serves the tiles or `tilejson.json` +responses but simply changes the URLs listed in `tilejson.json`. The rewriting of the path +is expected to be handled by a proxy. + + +

layers

+ +**Since version:** `2.0` ∙ **Type:** `object[]` ∙ **Cardinality:** `Optional` +**Path:** /vectorTiles + +Configuration of the individual layers for the Mapbox vector tiles. + +

cacheMaxSeconds

+ +**Since version:** `2.0` ∙ **Type:** `integer` ∙ **Cardinality:** `Optional` ∙ **Default value:** `-1` +**Path:** /vectorTiles/layers/[0] + +Sets the cache header in the response. + +The lowest value of the layers included is selected. + +

expansionFactor

+ +**Since version:** `2.0` ∙ **Type:** `double` ∙ **Cardinality:** `Optional` ∙ **Default value:** `0.25` +**Path:** /vectorTiles/layers/[0] + +How far outside its boundaries should the tile contain information. + +The value is a fraction of the tile size. If you are having problem with icons and shapes being clipped at tile edges, then increase this number. + +

mapper

+ +**Since version:** `2.0` ∙ **Type:** `string` ∙ **Cardinality:** `Required` +**Path:** /vectorTiles/layers/[0] + +Describes the mapper converting from the OTP model entities to the vector tile properties. + +Currently `Digitransit` is supported for all layer types. + + + + + ### Extending -If more generic layers are created for this API, it should be moved out from the sandbox, into the -core code, with potentially leaving specific property mappers in place. +If more generic layers are created for this API, the code should be moved out from the sandbox, into +the core, perhaps potentially leaving specific property mappers in place. #### Creating a new layer @@ -168,7 +246,7 @@ defined per layer. In order to create a new mapper for a layer, you need to create a new class extending `PropertyMapper`. In that class, you need to implement the -method `Collection> map(T input)`. The type T is dependent on the layer for which +method `Collection> map(T input)`. The type T is dependent on the layer for which you implement the mapper for. It needs to return a list of attributes, as key-value pairs which will be written into the vector tile. @@ -191,3 +269,4 @@ key, and a function to create the mapper, with a `Graph` object as a parameter, * Added DigitransitRealtime for vehicle rental stations * Changed old vehicle parking mapper to be Stadtnavi * Added a new Digitransit vehicle parking mapper with no real-time information and less fields +- 2024-01-22: Make `basePath` configurable [#5627](https://github.com/opentripplanner/OpenTripPlanner/pull/5627) \ No newline at end of file diff --git a/docs/sandbox/ReportApi.md b/docs/sandbox/ReportApi.md index fdb6c2d3146..1a0668d1740 100644 --- a/docs/sandbox/ReportApi.md +++ b/docs/sandbox/ReportApi.md @@ -32,6 +32,8 @@ This module mounts an endpoint for generating reports under `otp/report`. Availa - [German version](http://localhost:8080/otp/report/bicycle-safety.csv?osmWayPropertySet=germany) - [UK version](http://localhost:8080/otp/report/bicycle-safety.csv?osmWayPropertySet=uk) - [Finnish version](http://localhost:8080/otp/report/bicycle-safety.csv?osmWayPropertySet=finland) +- [/otp/report/transit/group/priorities](http://localhost:8080/otp/report/transit/group/priorities): + List all transit groups used for transit-group-priority (Competition neutral planning). ### Configuration diff --git a/pom.xml b/pom.xml index 34f4365b580..ce77a303d7d 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ - 137 + 140 30.1 2.50 diff --git a/src/client/debug-client-preview/index.html b/src/client/debug-client-preview/index.html index e9443e240c4..2653fdf36c2 100644 --- a/src/client/debug-client-preview/index.html +++ b/src/client/debug-client-preview/index.html @@ -5,8 +5,8 @@ OTP Debug Client - - + +
diff --git a/src/client/graphiql/index.html b/src/client/graphiql/index.html index d96f736b287..0e8d4afd1ea 100644 --- a/src/client/graphiql/index.html +++ b/src/client/graphiql/index.html @@ -153,7 +153,7 @@ let apiFlavor = parameters.flavor || "gtfs"; let urls = { gtfs: '/otp/gtfs/v1', - transmodel: '/otp/routers/default/transmodel/index/graphql' + transmodel: '/otp/transmodel/v3' } let defaultQueries = { diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedLegsFareServiceTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedLegsFareServiceTest.java index c5901a0269e..350bae04dce 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedLegsFareServiceTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedLegsFareServiceTest.java @@ -69,11 +69,11 @@ void modes(CombinationMode mode, Itinerary itinerary, Money totalPrice, String h assertEquals(totalPrice, price); var firstLeg = itinerary.getTransitLeg(0); - var uses = fare.legProductsFromComponents().get(firstLeg); + var uses = fare.getLegProducts().get(firstLeg); assertEquals(1, uses.size()); var secondLeg = itinerary.getTransitLeg(1); - uses = fare.legProductsFromComponents().get(secondLeg); + uses = fare.getLegProducts().get(secondLeg); assertEquals(1, uses.size()); } @@ -89,17 +89,17 @@ void legFares() { var fare = service.calculateFares(itinerary); var firstLeg = itinerary.getTransitLeg(0); - var uses = List.copyOf(fare.legProductsFromComponents().get(firstLeg)); + var uses = List.copyOf(fare.getLegProducts().get(firstLeg)); assertEquals(1, uses.size()); - var firstLegUse = uses.get(0); + var firstLegUse = uses.getFirst(); assertEquals(tenDollars, firstLegUse.product().price()); var secondLeg = itinerary.getTransitLeg(1); - uses = List.copyOf(fare.legProductsFromComponents().get(secondLeg)); + uses = List.copyOf(fare.getLegProducts().get(secondLeg)); assertEquals(1, uses.size()); - var secondLegUse = uses.get(0); + var secondLegUse = uses.getFirst(); assertEquals(tenDollars, secondLegUse.product().price()); // the same far product is used for both legs as you only need to buy one diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceTest.java index 768e586da31..c5e26cc3c9b 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceTest.java @@ -3,11 +3,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.ext.fares.impl.FareModelForTest.AIRPORT_STOP; import static org.opentripplanner.ext.fares.impl.FareModelForTest.AIRPORT_TO_CITY_CENTER_SET; import static org.opentripplanner.ext.fares.impl.FareModelForTest.CITY_CENTER_A_STOP; import static org.opentripplanner.ext.fares.impl.FareModelForTest.CITY_CENTER_B_STOP; +import static org.opentripplanner.ext.fares.impl.FareModelForTest.CITY_CENTER_C_STOP; +import static org.opentripplanner.ext.fares.impl.FareModelForTest.FREE_TRANSFERS_IN_CITY_SET; import static org.opentripplanner.ext.fares.impl.FareModelForTest.INSIDE_CITY_CENTER_SET; import static org.opentripplanner.ext.fares.impl.FareModelForTest.OTHER_FEED_ATTRIBUTE; import static org.opentripplanner.ext.fares.impl.FareModelForTest.OTHER_FEED_ROUTE; @@ -56,13 +57,38 @@ void simpleZoneBasedFare() { assertEquals(TEN_DOLLARS, fp.price()); assertEquals("F:regular", fp.id().toString()); - var lp = fare.legProductsFromComponents(); + var lp = fare.getLegProducts(); assertEquals(1, lp.size()); var product = lp.values().iterator().next().product(); assertEquals(TEN_DOLLARS, product.price()); + } + + @Test + void applyToSeveralLegs() { + var service = new DefaultFareService(); + service.addFareRules(FareType.regular, List.of(FREE_TRANSFERS_IN_CITY_SET)); + var itin = newItinerary(Place.forStop(CITY_CENTER_A_STOP), T11_00) + .bus(1, T11_00, T11_12, Place.forStop(CITY_CENTER_B_STOP)) + .bus(1, T11_16, T11_20, Place.forStop(CITY_CENTER_C_STOP)) + .build(); + + var fare = service.calculateFares(itin); + assertNotNull(fare); + + var legProducts = fare.getLegProducts(); + + var firstLeg = itin.getTransitLeg(0); + var secondLeg = itin.getTransitLeg(1); + + var firstProducts = legProducts.get(firstLeg); + var secondProducts = legProducts.get(secondLeg); - // the leg products from the components and the "true" leg products are different collections - assertTrue(fare.getLegProducts().isEmpty()); + assertEquals(firstProducts, secondProducts); + + assertEquals( + "[FareProductUse[id=ddbf1572-18bc-3724-8b64-e1c7d5c8b6c6, product=FareProduct{id: 'F:free-transfers', amount: $20.00}]]", + firstProducts.toString() + ); } @Test @@ -91,21 +117,19 @@ void shouldNotCombineInterlinedLegs() { assertEquals(TWENTY_DOLLARS, price); - assertTrue(fare.getLegProducts().isEmpty()); + var legProducts = fare.getLegProducts(); - var legProductsFromComponents = fare.legProductsFromComponents(); + var firstLeg = itin.getLegs().getFirst(); + var products = List.copyOf(legProducts.get(firstLeg)); - var firstLeg = itin.getLegs().get(0); - var products = List.copyOf(legProductsFromComponents.get(firstLeg)); - - assertEquals(TEN_DOLLARS, products.get(0).product().price()); + assertEquals(TEN_DOLLARS, products.getFirst().product().price()); var secondLeg = itin.getLegs().get(1); - products = List.copyOf(legProductsFromComponents.get(secondLeg)); - assertEquals(TEN_DOLLARS, products.get(0).product().price()); + products = List.copyOf(legProducts.get(secondLeg)); + assertEquals(TEN_DOLLARS, products.getFirst().product().price()); assertEquals(1, fare.getItineraryProducts().size()); - assertEquals(TWENTY_DOLLARS, fare.getItineraryProducts().get(0).price()); + assertEquals(TWENTY_DOLLARS, fare.getItineraryProducts().getFirst().price()); } @Test @@ -124,18 +148,18 @@ void unknownLeg() { var price = fare.getFare(FareType.regular); assertEquals(Money.usDollars(-0.01f), price); - var components = fare.getComponents(FareType.regular); - assertEquals(1, components.size()); + var fareProducts = List.copyOf(fare.getLegProducts().values()); + assertEquals(1, fareProducts.size()); - var component = components.get(0); - assertEquals(AIRPORT_TO_CITY_CENTER_SET.getFareAttribute().getId(), component.fareId()); - assertEquals(TEN_DOLLARS, component.price()); + var fp = fareProducts.get(0).product(); + assertEquals(AIRPORT_TO_CITY_CENTER_SET.getFareAttribute().getId(), fp.id()); + assertEquals(TEN_DOLLARS, fp.price()); var firstBusLeg = itin.firstTransitLeg().get(); - assertEquals(List.of(firstBusLeg), component.legs()); + //assertEquals(List.of(firstBusLeg), fp.legs()); - var legProductsFromComponent = fare.legProductsFromComponents(); - assertEquals(1, legProductsFromComponent.size()); + var legProducts = fare.getLegProducts(); + assertEquals(1, legProducts.size()); } @Test @@ -149,19 +173,19 @@ void multipleFeeds() { .build(); var result = service.calculateFares(itin); - var resultComponents = result - .getComponents(FareType.regular) + var fareProductIds = result + .getLegProducts() + .values() .stream() - .map(r -> r.fareId()) + .map(r -> r.product().id()) .toList(); - var resultPrice = result.getFare(FareType.regular); - assertEquals( List.of(AIRPORT_TO_CITY_CENTER_SET.getFareAttribute().getId(), OTHER_FEED_ATTRIBUTE.getId()), - resultComponents + fareProductIds ); + var resultPrice = result.getFare(FareType.regular); assertEquals(TWENTY_DOLLARS, resultPrice); } @@ -178,18 +202,26 @@ void multipleFeedsWithTransfersWithinFeed() { .build(); var result = service.calculateFares(itin); - var resultComponents = result - .getComponents(FareType.regular) - .stream() - .map(r -> r.fareId()) - .toList(); + var legProducts = result.getLegProducts(); - var resultPrice = result.getFare(FareType.regular); + var firstBusLeg = itin.getTransitLeg(0); + var secondBusLeg = itin.getTransitLeg(2); + var finalBusLeg = itin.getTransitLeg(4); + + assertEquals( + "[FareProductUse[id=5d0d58f4-b97a-38db-921c-8b5fc6392b54, product=FareProduct{id: 'F2:other-feed-attribute', amount: $10.00}]]", + legProducts.get(firstBusLeg).toString() + ); assertEquals( - List.of(INSIDE_CITY_CENTER_SET.getFareAttribute().getId(), OTHER_FEED_ATTRIBUTE.getId()), - resultComponents + "[FareProductUse[id=1d270201-412b-3b86-80f6-92ab144fa2e5, product=FareProduct{id: 'F:airport-to-city-center', amount: $10.00}]]", + legProducts.get(secondBusLeg).toString() + ); + assertEquals( + "[FareProductUse[id=5d0d58f4-b97a-38db-921c-8b5fc6392b54, product=FareProduct{id: 'F2:other-feed-attribute', amount: $10.00}]]", + legProducts.get(finalBusLeg).toString() ); + var resultPrice = result.getFare(FareType.regular); assertEquals(TWENTY_DOLLARS, resultPrice); } @@ -203,13 +235,14 @@ void multipleFeedsWithUnknownFareLegs() { .bus(OTHER_FEED_ROUTE, 2, T11_20, T11_32, Place.forStop(OTHER_FEED_STOP)) .build(); var result = service.calculateFares(itin); - var resultComponents = result - .getComponents(FareType.regular) + var resultProductIds = result + .getLegProducts() + .values() .stream() - .map(r -> r.fareId()) + .map(r -> r.product().id()) .toList(); var resultPrice = result.getFare(FareType.regular); - assertEquals(List.of(OTHER_FEED_ATTRIBUTE.getId()), resultComponents); + assertEquals(List.of(OTHER_FEED_ATTRIBUTE.getId()), resultProductIds); assertEquals(Money.usDollars(-0.01f), resultPrice); } } diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/impl/FareModelForTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/impl/FareModelForTest.java index 200220587a6..e498588d00f 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/impl/FareModelForTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/impl/FareModelForTest.java @@ -6,6 +6,7 @@ import org.opentripplanner.ext.fares.model.FareAttribute; import org.opentripplanner.ext.fares.model.FareRuleSet; import org.opentripplanner.framework.geometry.WgsCoordinate; +import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.transit.model.basic.Money; import org.opentripplanner.transit.model.basic.TransitMode; @@ -27,44 +28,56 @@ public class FareModelForTest { private static final StopModelBuilder STOP_MODEL_BUILDER = StopModel.of(); - static RegularStop AIRPORT_STOP = STOP_MODEL_BUILDER + static final RegularStop AIRPORT_STOP = STOP_MODEL_BUILDER .regularStop(id("airport")) .withCoordinate(new WgsCoordinate(1, 1)) .addFareZones(AIRPORT_ZONE) - .withName(new NonLocalizedString("Airport")) + .withName(I18NString.of("Airport")) .build(); - static RegularStop CITY_CENTER_A_STOP = STOP_MODEL_BUILDER + static final RegularStop CITY_CENTER_A_STOP = STOP_MODEL_BUILDER .regularStop(id("city-center-a")) .withCoordinate(new WgsCoordinate(1, 2)) .addFareZones(CITY_CENTER_ZONE) - .withName(new NonLocalizedString("City center: stop A")) + .withName(I18NString.of("City center: stop A")) .build(); - static RegularStop CITY_CENTER_B_STOP = STOP_MODEL_BUILDER + static final RegularStop CITY_CENTER_B_STOP = STOP_MODEL_BUILDER .regularStop(id("city-center-b")) .withCoordinate(new WgsCoordinate(1, 3)) .addFareZones(CITY_CENTER_ZONE) - .withName(new NonLocalizedString("City center: stop B")) + .withName(I18NString.of("City center: stop B")) .build(); - static RegularStop SUBURB_STOP = STOP_MODEL_BUILDER + static final RegularStop CITY_CENTER_C_STOP = STOP_MODEL_BUILDER + .regularStop(id("city-center-c")) + .withCoordinate(new WgsCoordinate(1, 4)) + .addFareZones(CITY_CENTER_ZONE) + .withName(I18NString.of("City center: stop C")) + .build(); + static final RegularStop SUBURB_STOP = STOP_MODEL_BUILDER .regularStop(id("suburb")) .withCoordinate(new WgsCoordinate(1, 4)) - .withName(new NonLocalizedString("Suburb")) + .withName(I18NString.of("Suburb")) .build(); - static RegularStop OTHER_FEED_STOP = STOP_MODEL_BUILDER + static final RegularStop OTHER_FEED_STOP = STOP_MODEL_BUILDER .regularStop(FeedScopedId.ofNullable("F2", "other-feed-stop")) .withCoordinate(new WgsCoordinate(1, 5)) - .withName(new NonLocalizedString("Other feed stop")) + .withName(I18NString.of("Other feed stop")) .addFareZones(OTHER_FEED_ZONE) .build(); - static FareAttribute TEN_DOLLARS = FareAttribute + static final FareAttribute TEN_DOLLARS = FareAttribute .of(id("airport-to-city-center")) .setPrice(Money.usDollars(10)) .setTransfers(0) .build(); - static FareAttribute OTHER_FEED_ATTRIBUTE = FareAttribute + static final FareAttribute FREE_TRANSFERS = FareAttribute + .of(id("free-transfers")) + .setPrice(Money.usDollars(20)) + .setTransfers(10) + .build(); + + static final FareAttribute OTHER_FEED_ATTRIBUTE = FareAttribute .of(FeedScopedId.ofNullable("F2", "other-feed-attribute")) .setPrice(Money.usDollars(10)) .setTransfers(1) @@ -74,6 +87,7 @@ public class FareModelForTest { // Fare rule sets static FareRuleSet AIRPORT_TO_CITY_CENTER_SET = new FareRuleSet(TEN_DOLLARS); static FareRuleSet INSIDE_CITY_CENTER_SET = new FareRuleSet(TEN_DOLLARS); + static FareRuleSet FREE_TRANSFERS_IN_CITY_SET = new FareRuleSet(FREE_TRANSFERS); static FareRuleSet OTHER_FEED_SET = new FareRuleSet(OTHER_FEED_ATTRIBUTE); @@ -82,6 +96,10 @@ public class FareModelForTest { AIRPORT_ZONE.getId().getId(), CITY_CENTER_ZONE.getId().getId() ); + FREE_TRANSFERS_IN_CITY_SET.addOriginDestination( + CITY_CENTER_ZONE.getId().getId(), + CITY_CENTER_ZONE.getId().getId() + ); INSIDE_CITY_CENTER_SET.addOriginDestination( CITY_CENTER_ZONE.getId().getId(), CITY_CENTER_ZONE.getId().getId() @@ -95,7 +113,7 @@ public class FareModelForTest { static Route OTHER_FEED_ROUTE = Route .of(new FeedScopedId("F2", "other-feed-route")) .withAgency(OTHER_FEED_AGENCY) - .withLongName(new NonLocalizedString("other-feed-route")) + .withLongName(I18NString.of("other-feed-route")) .withMode(TransitMode.BUS) .build(); } diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/impl/FaresIntegrationTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/impl/FaresIntegrationTest.java index 939d05cb2c2..d6a1432a75b 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/impl/FaresIntegrationTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/impl/FaresIntegrationTest.java @@ -22,9 +22,7 @@ import org.opentripplanner.routing.core.FareType; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.standalone.api.OtpServerRequestContext; -import org.opentripplanner.test.support.ResourceLoader; import org.opentripplanner.transit.model.basic.Money; -import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.service.TransitModel; public class FaresIntegrationTest { @@ -106,122 +104,6 @@ public void testPortland() { // assertEquals(cost.getFare(FareType.regular), new Money(new WrappedCurrency("USD"), 430)); } - @Test - public void testFareComponent() { - TestOtpModel model = ConstantsForTests.buildGtfsGraph( - ResourceLoader.of(this).file("farecomponents.gtfs.zip") - ); - Graph graph = model.graph(); - TransitModel transitModel = model.transitModel(); - String feedId = transitModel.getFeedIds().iterator().next(); - - var serverContext = TestServerContext.createServerContext(graph, transitModel); - - Money tenUSD = Money.usDollars(10); - - var dateTime = LocalDateTime - .of(2009, 8, 7, 0, 0, 0) - .atZone(ZoneId.of("America/Los_Angeles")) - .toInstant(); - - // A -> B, base case - - var from = GenericLocation.fromStopId("Origin", feedId, "A"); - var to = GenericLocation.fromStopId("Destination", feedId, "B"); - - var fare = getFare(from, to, dateTime, serverContext); - - var fareComponents = fare.getComponents(FareType.regular); - assertEquals(fareComponents.size(), 1); - assertEquals(fareComponents.get(0).price(), tenUSD); - assertEquals(fareComponents.get(0).fareId(), new FeedScopedId(feedId, "AB")); - assertEquals(fareComponents.get(0).routes().get(0), new FeedScopedId(feedId, "1")); - - // D -> E, null case - - from = GenericLocation.fromStopId("Origin", feedId, "D"); - to = GenericLocation.fromStopId("Destination", feedId, "E"); - fare = getFare(from, to, dateTime, serverContext); - assertEquals(ItineraryFares.empty(), fare); - - // A -> C, 2 components in a path - - from = GenericLocation.fromStopId("Origin", feedId, "A"); - to = GenericLocation.fromStopId("Destination", feedId, "C"); - fare = getFare(from, to, dateTime, serverContext); - - fareComponents = fare.getComponents(FareType.regular); - assertEquals(fareComponents.size(), 2); - assertEquals(fareComponents.get(0).price(), tenUSD); - assertEquals(fareComponents.get(0).fareId(), new FeedScopedId(feedId, "AB")); - assertEquals(fareComponents.get(0).routes().get(0), new FeedScopedId(feedId, "1")); - assertEquals(fareComponents.get(1).price(), tenUSD); - assertEquals(fareComponents.get(1).fareId(), new FeedScopedId(feedId, "BC")); - assertEquals(fareComponents.get(1).routes().get(0), new FeedScopedId(feedId, "2")); - - // B -> D, 2 fully connected components - from = GenericLocation.fromStopId("Origin", feedId, "B"); - to = GenericLocation.fromStopId("Destination", feedId, "D"); - fare = getFare(from, to, dateTime, serverContext); - - fareComponents = fare.getComponents(FareType.regular); - assertEquals(fareComponents.size(), 1); - assertEquals(fareComponents.get(0).price(), tenUSD); - assertEquals(fareComponents.get(0).fareId(), new FeedScopedId(feedId, "BD")); - assertEquals(fareComponents.get(0).routes().get(0), new FeedScopedId(feedId, "2")); - assertEquals(fareComponents.get(0).routes().get(1), new FeedScopedId(feedId, "3")); - - // E -> G, missing in between fare - from = GenericLocation.fromStopId("Origin", feedId, "E"); - to = GenericLocation.fromStopId("Destination", feedId, "G"); - fare = getFare(from, to, dateTime, serverContext); - - fareComponents = fare.getComponents(FareType.regular); - assertEquals(fareComponents.size(), 1); - assertEquals(tenUSD, fareComponents.get(0).price()); - assertEquals(fareComponents.get(0).fareId(), new FeedScopedId(feedId, "EG")); - assertEquals(fareComponents.get(0).routes().get(0), new FeedScopedId(feedId, "5")); - assertEquals(fareComponents.get(0).routes().get(1), new FeedScopedId(feedId, "6")); - - // C -> E, missing fare after - from = GenericLocation.fromStopId("Origin", feedId, "C"); - to = GenericLocation.fromStopId("Destination", feedId, "E"); - fare = getFare(from, to, dateTime, serverContext); - - fareComponents = fare.getComponents(FareType.regular); - assertEquals(fareComponents.size(), 1); - assertEquals(fareComponents.get(0).price(), tenUSD); - assertEquals(fareComponents.get(0).fareId(), new FeedScopedId(feedId, "CD")); - assertEquals(fareComponents.get(0).routes().get(0), new FeedScopedId(feedId, "3")); - - // D -> G, missing fare before - from = GenericLocation.fromStopId("Origin", feedId, "D"); - to = GenericLocation.fromStopId("Destination", feedId, "G"); - fare = getFare(from, to, dateTime, serverContext); - - fareComponents = fare.getComponents(FareType.regular); - assertEquals(fareComponents.size(), 1); - assertEquals(fareComponents.get(0).price(), tenUSD); - assertEquals(fareComponents.get(0).fareId(), new FeedScopedId(feedId, "EG")); - assertEquals(fareComponents.get(0).routes().get(0), new FeedScopedId(feedId, "5")); - assertEquals(fareComponents.get(0).routes().get(1), new FeedScopedId(feedId, "6")); - - // A -> D, use individual component parts - from = GenericLocation.fromStopId("Origin", feedId, "A"); - to = GenericLocation.fromStopId("Destination", feedId, "D"); - fare = getFare(from, to, dateTime, serverContext); - - fareComponents = fare.getComponents(FareType.regular); - assertEquals(fareComponents.size(), 2); - assertEquals(fareComponents.get(0).price(), tenUSD); - assertEquals(fareComponents.get(0).fareId(), new FeedScopedId(feedId, "AB")); - assertEquals(fareComponents.get(0).routes().get(0), new FeedScopedId(feedId, "1")); - assertEquals(fareComponents.get(1).price(), tenUSD); - assertEquals(fareComponents.get(1).fareId(), new FeedScopedId(feedId, "BD")); - assertEquals(fareComponents.get(1).routes().get(0), new FeedScopedId(feedId, "2")); - assertEquals(fareComponents.get(1).routes().get(1), new FeedScopedId(feedId, "3")); - } - private static ItineraryFares getFare( GenericLocation from, GenericLocation to, diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/impl/HSLFareServiceTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/impl/HSLFareServiceTest.java index 14fbe18f792..4e1347cae16 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/impl/HSLFareServiceTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/impl/HSLFareServiceTest.java @@ -1,5 +1,6 @@ package org.opentripplanner.ext.fares.impl; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.opentripplanner.model.plan.TestItineraryBuilder.newItinerary; import static org.opentripplanner.transit.model._data.TransitModelForTest.FEED_ID; @@ -7,7 +8,6 @@ import java.util.LinkedList; import java.util.List; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -18,7 +18,6 @@ import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Place; import org.opentripplanner.model.plan.PlanTestConstants; -import org.opentripplanner.routing.core.FareComponent; import org.opentripplanner.routing.core.FareType; import org.opentripplanner.routing.fares.FareService; import org.opentripplanner.transit.model._data.TransitModelForTest; @@ -39,13 +38,14 @@ public void canCalculateHSLFares( Itinerary i, List expectedFareIds ) { - Assertions.assertArrayEquals( + assertArrayEquals( expectedFareIds.toArray(), fareService .calculateFares(i) - .getComponents(FareType.regular) + .getLegProducts() + .values() .stream() - .map(FareComponent::fareId) + .map(u -> u.product().id()) .toArray() ); } @@ -382,7 +382,7 @@ private static List createTestCases() { "Bus ride within zone A, then another one outside of HSL's area", hslFareService, A1_A2_F, - List.of(fareAttributeAB.getId()) + List.of(fareAttributeAB.getId(), fareAttributeAB.getId()) ) ); diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/impl/HighestFareInFreeTransferWindowFareServiceTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/impl/HighestFareInFreeTransferWindowFareServiceTest.java index 57621c9ab0f..5664dcc765d 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/impl/HighestFareInFreeTransferWindowFareServiceTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/impl/HighestFareInFreeTransferWindowFareServiceTest.java @@ -1,7 +1,7 @@ package org.opentripplanner.ext.fares.impl; -import static graphql.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.opentripplanner.model.plan.TestItineraryBuilder.newItinerary; import static org.opentripplanner.transit.model._data.TransitModelForTest.FEED_ID; import static org.opentripplanner.transit.model._data.TransitModelForTest.id; @@ -44,10 +44,9 @@ public void canCalculateFare( ) { var fares = fareService.calculateFares(i); assertEquals(expectedFare, fares.getFare(FareType.regular)); + assertFalse(fares.getItineraryProducts().isEmpty()); for (var type : fares.getFareTypes()) { - assertTrue(fares.getComponents(type).isEmpty()); - var prices = fares .getItineraryProducts() .stream() diff --git a/src/ext-test/java/org/opentripplanner/ext/restapi/mapping/FareMapperTest.java b/src/ext-test/java/org/opentripplanner/ext/restapi/mapping/FareMapperTest.java index bd1bd07aa43..4b455cce0f7 100644 --- a/src/ext-test/java/org/opentripplanner/ext/restapi/mapping/FareMapperTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/restapi/mapping/FareMapperTest.java @@ -3,7 +3,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.opentripplanner.model.plan.TestItineraryBuilder.newItinerary; -import java.util.List; import java.util.Locale; import org.junit.jupiter.api.Test; import org.opentripplanner.model.fare.ItineraryFares; @@ -29,6 +28,5 @@ public void emptyDetails() { var apiMoney = apiFare.fare().get(FareType.regular.name()); assertEquals(500, apiMoney.cents()); assertEquals("USD", apiMoney.currency().currency()); - assertEquals(List.of(), apiFare.details().get(FareType.regular.name())); } } diff --git a/src/ext-test/java/org/opentripplanner/ext/vectortiles/VectorTilesConfigDocTest.java b/src/ext-test/java/org/opentripplanner/ext/vectortiles/VectorTilesConfigDocTest.java new file mode 100644 index 00000000000..233e3fa3737 --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/vectortiles/VectorTilesConfigDocTest.java @@ -0,0 +1,74 @@ +package org.opentripplanner.ext.vectortiles; + +import static org.opentripplanner.framework.io.FileUtils.assertFileEquals; +import static org.opentripplanner.framework.io.FileUtils.readFile; +import static org.opentripplanner.framework.io.FileUtils.writeFile; +import static org.opentripplanner.framework.text.MarkdownFormatter.HEADER_4; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceSection; +import static org.opentripplanner.standalone.config.framework.json.JsonSupport.jsonNodeFromPath; + +import java.io.File; +import org.junit.jupiter.api.Test; +import org.opentripplanner.generate.doc.framework.DocBuilder; +import org.opentripplanner.generate.doc.framework.GeneratesDocumentation; +import org.opentripplanner.generate.doc.framework.ParameterDetailsList; +import org.opentripplanner.generate.doc.framework.ParameterSummaryTable; +import org.opentripplanner.generate.doc.framework.SkipNodes; +import org.opentripplanner.standalone.config.RouterConfig; +import org.opentripplanner.standalone.config.framework.json.NodeAdapter; +import org.opentripplanner.test.support.ResourceLoader; + +@GeneratesDocumentation +public class VectorTilesConfigDocTest { + + private static final String DOCUMENT = "sandbox/MapboxVectorTilesApi.md"; + private static final File TEMPLATE = new File(TEMPLATE_ROOT, DOCUMENT); + private static final File OUT_FILE = new File(DOCS_ROOT, DOCUMENT); + private static final SkipNodes SKIP_NODES = SkipNodes.of().build(); + + @Test + public void updateDoc() { + NodeAdapter node = readVectorTiles(); + + // Read and close input file (same as output file) + String template = readFile(TEMPLATE); + String original = readFile(OUT_FILE); + + template = replaceSection(template, "parameters", vectorTilesDoc(node)); + + writeFile(OUT_FILE, template); + assertFileEquals(original, OUT_FILE); + } + + private NodeAdapter readVectorTiles() { + var path = ResourceLoader.of(this).file("router-config.json").toPath(); + var json = jsonNodeFromPath(path); + var conf = new RouterConfig(json, path.toString(), false); + return conf.asNodeAdapter().child("vectorTiles"); + } + + private String vectorTilesDoc(NodeAdapter node) { + DocBuilder buf = new DocBuilder(); + addParameterSummaryTable(buf, node); + addDetailsSection(buf, node); + return buf.toString(); + } + + private void addParameterSummaryTable(DocBuilder buf, NodeAdapter node) { + buf.addSection(new ParameterSummaryTable(SKIP_NODES).createTable(node).toMarkdownTable()); + } + + private void addDetailsSection(DocBuilder buf, NodeAdapter node) { + String details = getParameterDetailsTable(node); + + if (!details.isBlank()) { + buf.header(4, "Details", null).addSection(details); + } + } + + private String getParameterDetailsTable(NodeAdapter node) { + return ParameterDetailsList.listParametersWithDetails(node, SKIP_NODES, HEADER_4); + } +} diff --git a/src/ext-test/java/org/opentripplanner/ext/vectortiles/VectorTilesResourceTest.java b/src/ext-test/java/org/opentripplanner/ext/vectortiles/VectorTilesResourceTest.java new file mode 100644 index 00000000000..ff9509a8474 --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/vectortiles/VectorTilesResourceTest.java @@ -0,0 +1,31 @@ +package org.opentripplanner.ext.vectortiles; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.glassfish.grizzly.http.server.Request; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.opentripplanner.TestServerContext; +import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.test.support.HttpForTest; +import org.opentripplanner.transit.service.TransitModel; + +class VectorTilesResourceTest { + + @Test + void tileJson() { + // the Grizzly request is awful to instantiate, using Mockito + var grizzlyRequest = Mockito.mock(Request.class); + var resource = new VectorTilesResource( + TestServerContext.createServerContext(new Graph(), new TransitModel()), + grizzlyRequest, + "default" + ); + var req = HttpForTest.containerRequest(); + var tileJson = resource.getTileJson(req.getUriInfo(), req, "layer1,layer2"); + assertEquals( + "https://localhost:8080/otp/routers/default/vectorTiles/layer1,layer2/{z}/{x}/{y}.pbf", + tileJson.tiles[0] + ); + } +} diff --git a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingGroupsLayerTest.java b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingGroupsLayerTest.java index 1442b57fd60..1ec7d042894 100644 --- a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingGroupsLayerTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingGroupsLayerTest.java @@ -97,21 +97,23 @@ public void vehicleParkingGroupGeometryTest() { var config = """ { - "vectorTileLayers": [ - { - "name": "vehicleParkingGroups", - "type": "VehicleParkingGroup", - "mapper": "Digitransit", - "maxZoom": 20, - "minZoom": 14, - "cacheMaxSeconds": 600, - "expansionFactor": 0 - } - ] + "vectorTiles": { + "layers" :[ + { + "name": "vehicleParkingGroups", + "type": "VehicleParkingGroup", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 600, + "expansionFactor": 0 + } + ] + } } """; var nodeAdapter = newNodeAdapterForTest(config); - var tiles = VectorTileConfig.mapVectorTilesParameters(nodeAdapter, "vectorTileLayers"); + var tiles = VectorTileConfig.mapVectorTilesParameters(nodeAdapter, "vectorTiles"); assertEquals(1, tiles.layers().size()); var builder = new VehicleParkingGroupsLayerBuilderWithPublicGeometry( graph, diff --git a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingsLayerTest.java b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingsLayerTest.java index b4988ab398d..fdb723b3dc7 100644 --- a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingsLayerTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingsLayerTest.java @@ -93,23 +93,25 @@ public void vehicleParkingGeometryTest() { var config = """ { - "vectorTileLayers": [ - { - "name": "vehicleParking", - "type": "VehicleParking", - "mapper": "Stadtnavi", - "maxZoom": 20, - "minZoom": 14, - "cacheMaxSeconds": 60, - "expansionFactor": 0 - } - ] + "vectorTiles": { + "layers" : [ + { + "name": "vehicleParking", + "type": "VehicleParking", + "mapper": "Stadtnavi", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 60, + "expansionFactor": 0 + } + ] + } } """; var nodeAdapter = newNodeAdapterForTest(config); - var tiles = VectorTileConfig.mapVectorTilesParameters(nodeAdapter, "vectorTileLayers"); + var tiles = VectorTileConfig.mapVectorTilesParameters(nodeAdapter, "vectorTiles"); assertEquals(1, tiles.layers().size()); - var builder = new VehicleParkingsLayerBuilder(graph, tiles.layers().get(0), Locale.US); + var builder = new VehicleParkingsLayerBuilder(graph, tiles.layers().getFirst(), Locale.US); List geometries = builder.getGeometries(new Envelope(0.99, 1.01, 1.99, 2.01)); diff --git a/src/ext-test/java/org/opentripplanner/ext/vehiclerentalservicedirectory/generatedoc/VehicleRentalServiceDirectoryConfigDocTest.java b/src/ext-test/java/org/opentripplanner/ext/vehiclerentalservicedirectory/generatedoc/VehicleRentalServiceDirectoryConfigDocTest.java index 51f72c05e3e..4936bb4dd44 100644 --- a/src/ext-test/java/org/opentripplanner/ext/vehiclerentalservicedirectory/generatedoc/VehicleRentalServiceDirectoryConfigDocTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/vehiclerentalservicedirectory/generatedoc/VehicleRentalServiceDirectoryConfigDocTest.java @@ -37,7 +37,7 @@ public class VehicleRentalServiceDirectoryConfigDocTest { public void updateConfigurationDoc() { NodeAdapter node = readConfigDefaults(); - // Read and close inout file (same as output file) + // Read and close input file (same as output file) String doc = readFile(TEMPLATE); String original = readFile(OUT_FILE); diff --git a/src/ext-test/resources/org/opentripplanner/ext/fares/impl/farecomponents.gtfs.zip b/src/ext-test/resources/org/opentripplanner/ext/fares/impl/farecomponents.gtfs.zip deleted file mode 100644 index 4f7625f55c5..00000000000 Binary files a/src/ext-test/resources/org/opentripplanner/ext/fares/impl/farecomponents.gtfs.zip and /dev/null differ diff --git a/src/ext/java/org/opentripplanner/ext/fares/FaresToItineraryMapper.java b/src/ext/java/org/opentripplanner/ext/fares/FaresToItineraryMapper.java index a2b0984e4a3..c545cbbb858 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/FaresToItineraryMapper.java +++ b/src/ext/java/org/opentripplanner/ext/fares/FaresToItineraryMapper.java @@ -1,11 +1,9 @@ package org.opentripplanner.ext.fares; -import com.google.common.collect.Multimap; import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.model.fare.FareProductUse; import org.opentripplanner.model.fare.ItineraryFares; import org.opentripplanner.model.plan.Itinerary; -import org.opentripplanner.model.plan.Leg; /** * Takes fares and applies them to the legs of an itinerary. @@ -22,13 +20,9 @@ public static void addFaresToLegs(ItineraryFares fares, Itinerary i) { }) .toList(); - final Multimap legProductsFromComponents = fares.legProductsFromComponents(); - i.transformTransitLegs(leg -> { - var legInstances = fares.getLegProducts().get(leg); - leg.setFareProducts( - ListUtils.combine(itineraryFareUses, legProductsFromComponents.get(leg), legInstances) - ); + var legUses = fares.getLegProducts().get(leg); + leg.setFareProducts(ListUtils.combine(itineraryFareUses, legUses)); return leg; }); } diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareService.java b/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareService.java index 6b7081de5ec..0bc4408b635 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareService.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareService.java @@ -1,5 +1,7 @@ package org.opentripplanner.ext.fares.impl; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; import java.time.Duration; import java.time.ZonedDateTime; import java.util.ArrayList; @@ -18,11 +20,11 @@ import org.opentripplanner.ext.fares.model.FareRuleSet; import org.opentripplanner.ext.flex.FlexibleTransitLeg; import org.opentripplanner.model.fare.FareProduct; +import org.opentripplanner.model.fare.FareProductUse; import org.opentripplanner.model.fare.ItineraryFares; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Leg; import org.opentripplanner.model.plan.ScheduledTransitLeg; -import org.opentripplanner.routing.core.FareComponent; import org.opentripplanner.routing.core.FareType; import org.opentripplanner.routing.fares.FareService; import org.opentripplanner.transit.model.basic.Money; @@ -119,7 +121,7 @@ public ItineraryFares calculateFares(Itinerary itinerary) { ItineraryFares fare = ItineraryFares.empty(); boolean hasFare = false; for (FareType fareType : fareRulesPerType.keySet()) { - List components = new ArrayList<>(); + final Multimap fareProducts = LinkedHashMultimap.create(); List fares = new ArrayList<>(); boolean legWithoutRulesFound = false; boolean legsWithoutMatchingRulesFound = false; @@ -143,14 +145,9 @@ public ItineraryFares calculateFares(Itinerary itinerary) { } hasFare = feedHasFare || hasFare; // Other feeds might still have fare for some legs - components.addAll(currentFare.getComponents(fareType)); + fareProducts.putAll(currentFare.getLegProducts()); fare.addFare(fareType, currentFare.getFare(fareType)); - currentFare - .getLegProducts() - .entries() - .forEach(entry -> fare.addFareProduct(entry.getKey(), entry.getValue().product())); - fares.add(currentFare.getFare(fareType)); // If all the legs are from one feed, consider itinerary products @@ -170,7 +167,7 @@ public ItineraryFares calculateFares(Itinerary itinerary) { } } - fare.addFareComponent(fareType, components); + fare.addFareProductUses(fareProducts); // No fares will be discovered after this point if (!hasFare) { @@ -244,7 +241,7 @@ protected boolean populateFare( ) { FareSearch r = performSearch(fareType, legs, fareRules); - List components = new ArrayList<>(); + Multimap fareProductUses = LinkedHashMultimap.create(); int count = 0; int start = 0; int end = legs.size() - 1; @@ -261,8 +258,11 @@ protected boolean populateFare( int via = r.next[start][r.endOfComponent[start]]; float cost = r.resultTable[start][via]; FeedScopedId fareId = r.fareIds[start][via]; + var product = FareProduct + .of(fareId, fareType.name(), Money.ofFractionalAmount(currency, cost)) + .build(); - var componentLegs = new ArrayList(); + List applicableLegs = new ArrayList<>(); for (int i = start; i <= via; ++i) { final var leg = legs.get(i); // if we have a leg that is combined for the purpose of fare calculation we need to @@ -271,21 +271,29 @@ protected boolean populateFare( // (remember that the combined leg only exists during fare calculation and is thrown away // afterwards to associating fare products with it will result in the API not showing any.) if (leg instanceof CombinedInterlinedTransitLeg combinedLeg) { - componentLegs.addAll(combinedLeg.originalLegs()); + applicableLegs.addAll(combinedLeg.originalLegs()); } else { - componentLegs.add(leg); + applicableLegs.add(leg); } } - components.add( - new FareComponent(fareId, Money.ofFractionalAmount(currency, cost), componentLegs) - ); + + if (!applicableLegs.isEmpty()) { + final var use = new FareProductUse( + product.uniqueInstanceId(applicableLegs.getFirst().getStartTime()), + product + ); + applicableLegs.forEach(leg -> { + fareProductUses.put(leg, use); + }); + } + ++count; start = via + 1; } var amount = r.resultTable[0][legs.size() - 1]; fare.addFare(fareType, Money.ofFractionalAmount(currency, amount)); - fare.addFareComponent(fareType, components); + fare.addFareProductUses(fareProductUses); return count > 0; } diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java b/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java index 42736472767..4879d858e86 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareService.java @@ -23,13 +23,9 @@ import org.opentripplanner.transit.model.basic.Money; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.Route; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class OrcaFareService extends DefaultFareService { - private static final Logger LOG = LoggerFactory.getLogger(OrcaFareService.class); - private static final Duration MAX_TRANSFER_DISCOUNT_DURATION = Duration.ofHours(2); public static final String COMM_TRANS_AGENCY_ID = "29"; diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFaresData.java b/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFaresData.java index e970cb51718..acef59a7c03 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFaresData.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFaresData.java @@ -1,7 +1,5 @@ package org.opentripplanner.ext.fares.impl; -import static org.opentripplanner.transit.model.basic.Money.USD; - import java.util.Map; import org.opentripplanner.routing.core.FareType; import org.opentripplanner.transit.model.basic.Money; diff --git a/src/ext/java/org/opentripplanner/ext/fares/model/FareRuleSet.java b/src/ext/java/org/opentripplanner/ext/fares/model/FareRuleSet.java index 61e813e6ea4..30119631216 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/model/FareRuleSet.java +++ b/src/ext/java/org/opentripplanner/ext/fares/model/FareRuleSet.java @@ -15,7 +15,6 @@ public class FareRuleSet implements Serializable { private final Set routeOriginDestinations; private final Set contains; private final FareAttribute fareAttribute; - private final Set trips; public FareRuleSet(FareAttribute fareAttribute) { this.fareAttribute = fareAttribute; @@ -23,7 +22,6 @@ public FareRuleSet(FareAttribute fareAttribute) { originDestinations = new HashSet<>(); routeOriginDestinations = new HashSet<>(); contains = new HashSet<>(); - trips = new HashSet<>(); } public void addOriginDestination(String origin, String destination) { @@ -62,14 +60,6 @@ public FareAttribute getFareAttribute() { return fareAttribute; } - public void addTrip(FeedScopedId trip) { - trips.add(trip); - } - - public Set getTrips() { - return trips; - } - public boolean matches( String startZone, String endZone, @@ -81,7 +71,7 @@ public boolean matches( Duration journeyTime ) { //check for matching origin/destination, if this ruleset has any origin/destination restrictions - if (originDestinations.size() > 0) { + if (!originDestinations.isEmpty()) { var od = new OriginDestination(startZone, endZone); if (!originDestinations.contains(od)) { var od2 = new OriginDestination(od.origin, null); @@ -95,25 +85,19 @@ public boolean matches( } //check for matching contains, if this ruleset has any containment restrictions - if (contains.size() > 0) { + if (!contains.isEmpty()) { if (!zonesVisited.equals(contains)) { return false; } } //check for matching routes - if (routes.size() != 0) { + if (!routes.isEmpty()) { if (!routes.containsAll(routesVisited)) { return false; } } - //check for matching trips - if (trips.size() != 0) { - if (!trips.containsAll(tripsVisited)) { - return false; - } - } if (fareAttribute.isTransfersSet() && fareAttribute.getTransfers() < transfersUsed) { return false; } diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java b/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java index 5cab995d419..a875ba0f516 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexIndex.java @@ -33,7 +33,7 @@ public FlexIndex(TransitModel transitModel) { tripById.put(flexTrip.getTrip().getId(), flexTrip); for (StopLocation stop : flexTrip.getStops()) { if (stop instanceof GroupStop groupStop) { - for (StopLocation stopElement : groupStop.getLocations()) { + for (StopLocation stopElement : groupStop.getChildLocations()) { flexTripsByStop.put(stopElement, flexTrip); } } else { diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java index edff0860933..e16e1e5e1f7 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java @@ -248,7 +248,7 @@ public TransitBuilder copy( private Collection expandStops(StopLocation stop) { return stop instanceof GroupStop groupStop - ? groupStop.getLocations() + ? groupStop.getChildLocations() : Collections.singleton(stop); } @@ -259,7 +259,7 @@ private int getFromIndex(NearbyStop accessEgress) { } StopLocation stop = stopTimes[i].stop; if (stop instanceof GroupStop groupStop) { - if (groupStop.getLocations().contains(accessEgress.stop)) { + if (groupStop.getChildLocations().contains(accessEgress.stop)) { return i; } } else { @@ -278,7 +278,7 @@ private int getToIndex(NearbyStop accessEgress) { } StopLocation stop = stopTimes[i].stop; if (stop instanceof GroupStop groupStop) { - if (groupStop.getLocations().contains(accessEgress.stop)) { + if (groupStop.getChildLocations().contains(accessEgress.stop)) { return i; } } else { diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java index 71a77eee5e7..c5968507676 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -300,7 +300,7 @@ public TransitBuilder copy() { private Stream expandStops(int index) { var stop = stopTimes[index].stop(); return stop instanceof GroupStop groupStop - ? groupStop.getLocations().stream().map(s -> new IndexedStopLocation(index, s)) + ? groupStop.getChildLocations().stream().map(s -> new IndexedStopLocation(index, s)) : Stream.of(new IndexedStopLocation(index, stop)); } @@ -311,7 +311,7 @@ private int getFromIndex(NearbyStop accessEgress) { } StopLocation stop = stopTimes[i].stop(); if (stop instanceof GroupStop groupStop) { - if (groupStop.getLocations().contains(accessEgress.stop)) { + if (groupStop.getChildLocations().contains(accessEgress.stop)) { return i; } } else { @@ -330,7 +330,7 @@ private int getToIndex(NearbyStop accessEgress) { } StopLocation stop = stopTimes[i].stop(); if (stop instanceof GroupStop groupStop) { - if (groupStop.getLocations().contains(accessEgress.stop)) { + if (groupStop.getChildLocations().contains(accessEgress.stop)) { return i; } } else { diff --git a/src/ext/java/org/opentripplanner/ext/reportapi/model/TransitGroupPriorityReport.java b/src/ext/java/org/opentripplanner/ext/reportapi/model/TransitGroupPriorityReport.java new file mode 100644 index 00000000000..635469cb3a2 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/reportapi/model/TransitGroupPriorityReport.java @@ -0,0 +1,86 @@ +package org.opentripplanner.ext.reportapi.model; + +import java.util.Collection; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.stream.Collectors; +import org.opentripplanner.routing.algorithm.raptoradapter.transit.request.PriorityGroupConfigurator; +import org.opentripplanner.routing.api.request.request.TransitRequest; +import org.opentripplanner.transit.model.network.TripPattern; + +/** + * This class is used to report all transit-groups used for transit-group-priority. The report is + * useful when configuring/debugging this functionality. + *

+ * The format is pure text. + */ +public class TransitGroupPriorityReport { + + public static String build(Collection patterns, TransitRequest request) { + var c = PriorityGroupConfigurator.of( + request.priorityGroupsByAgency(), + request.priorityGroupsGlobal() + ); + + var map = new TreeMap(); + for (var it : patterns) { + int groupId = c.lookupTransitGroupPriorityId(it); + var de = map.computeIfAbsent(groupId, DebugEntity::new); + de.add( + it.getRoute().getAgency().getId().toString(), + it.getMode().name(), + it.getNetexSubmode().name() + ); + } + return ( + "TRANSIT GROUPS PRIORITY" + + map.values().stream().map(DebugEntity::toString).sorted().collect(Collectors.joining("")) + ); + } + + private static class DebugEntity { + + private final int groupId; + private final TreeMap agencies = new TreeMap<>(); + + public DebugEntity(int groupId) { + this.groupId = groupId; + } + + void add(String agency, String mode, String submode) { + agencies.computeIfAbsent(agency, AgencyEntry::new).add(mode, submode); + } + + @Override + public String toString() { + var buf = new StringBuilder("\n %#010x".formatted(groupId)); + for (var it : agencies.values()) { + buf.append("\n ").append(it.toString()); + } + return buf.toString(); + } + } + + private record AgencyEntry(String agency, TreeMap> modes) { + private AgencyEntry(String agency) { + this(agency, new TreeMap<>()); + } + + void add(String mode, String submode) { + modes.computeIfAbsent(mode, m -> new TreeSet<>()).add(submode); + } + + @Override + public String toString() { + var buf = new StringBuilder(); + for (var it : modes.entrySet()) { + buf.append(", "); + buf.append(it.getKey()); + if (!it.getValue().isEmpty()) { + buf.append(" (").append(String.join(", ", it.getValue())).append(")"); + } + } + return agency + " ~ " + buf.substring(2); + } + } +} diff --git a/src/ext/java/org/opentripplanner/ext/reportapi/resource/ReportResource.java b/src/ext/java/org/opentripplanner/ext/reportapi/resource/ReportResource.java index 6ccb728800e..a859b4ff78a 100644 --- a/src/ext/java/org/opentripplanner/ext/reportapi/resource/ReportResource.java +++ b/src/ext/java/org/opentripplanner/ext/reportapi/resource/ReportResource.java @@ -17,8 +17,10 @@ import org.opentripplanner.ext.reportapi.model.GraphReportBuilder; import org.opentripplanner.ext.reportapi.model.GraphReportBuilder.GraphStats; import org.opentripplanner.ext.reportapi.model.TransfersReport; +import org.opentripplanner.ext.reportapi.model.TransitGroupPriorityReport; import org.opentripplanner.model.transfer.TransferService; import org.opentripplanner.openstreetmap.tagmapping.OsmTagMapperSource; +import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.standalone.api.OtpServerRequestContext; import org.opentripplanner.transit.service.TransitService; @@ -33,11 +35,13 @@ public class ReportResource { private final TransferService transferService; private final TransitService transitService; + private final RouteRequest defaultRequest; @SuppressWarnings("unused") public ReportResource(@Context OtpServerRequestContext requestContext) { this.transferService = requestContext.transitService().getTransferService(); this.transitService = requestContext.transitService(); + this.defaultRequest = requestContext.defaultRouteRequest(); } @GET @@ -80,6 +84,16 @@ public Response getBicycleSafetyAsCsv( .build(); } + @GET + @Path("/transit/group/priorities") + @Produces(MediaType.TEXT_PLAIN) + public String getTransitGroupPriorities() { + return TransitGroupPriorityReport.build( + transitService.getAllTripPatterns(), + defaultRequest.journey().transit() + ); + } + @GET @Path("/graph.json") public Response stats(@Context OtpServerRequestContext serverRequestContext) { diff --git a/src/ext/java/org/opentripplanner/ext/restapi/mapping/FareMapper.java b/src/ext/java/org/opentripplanner/ext/restapi/mapping/FareMapper.java index a6020c6bf37..e8582607ce5 100644 --- a/src/ext/java/org/opentripplanner/ext/restapi/mapping/FareMapper.java +++ b/src/ext/java/org/opentripplanner/ext/restapi/mapping/FareMapper.java @@ -11,7 +11,6 @@ import java.util.stream.Collectors; import javax.annotation.Nullable; import org.opentripplanner.ext.restapi.model.ApiCurrency; -import org.opentripplanner.ext.restapi.model.ApiFareComponent; import org.opentripplanner.ext.restapi.model.ApiFareProduct; import org.opentripplanner.ext.restapi.model.ApiFareQualifier; import org.opentripplanner.ext.restapi.model.ApiItineraryFares; @@ -24,7 +23,6 @@ import org.opentripplanner.model.fare.RiderCategory; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Leg; -import org.opentripplanner.routing.core.FareComponent; import org.opentripplanner.transit.model.basic.Money; public class FareMapper { @@ -38,11 +36,10 @@ public FareMapper(Locale locale) { public ApiItineraryFares mapFare(Itinerary itinerary) { var fares = itinerary.getFares(); Map apiFare = toApiMoneys(fares); - Map> apiComponent = toApiFareComponents(fares); return new ApiItineraryFares( apiFare, - apiComponent, + Map.of(), toApiFareProducts(fares.getItineraryProducts()), toApiLegProducts(itinerary, fares.getLegProducts()) ); @@ -105,17 +102,6 @@ private List toApiFareProducts(Collection product) } } - private Map> toApiFareComponents(ItineraryFares fare) { - return fare - .getFareTypes() - .stream() - .map(key -> { - var money = fare.getComponents(key).stream().map(this::toApiFareComponent).toList(); - return new SimpleEntry<>(key, money); - }) - .collect(Collectors.toMap(e -> e.getKey().name(), Entry::getValue)); - } - private Map toApiMoneys(ItineraryFares fare) { return fare .getFareTypes() @@ -139,8 +125,4 @@ private ApiMoney toApiMoney(Money m) { ) ); } - - private ApiFareComponent toApiFareComponent(FareComponent m) { - return new ApiFareComponent(m.fareId(), null, toApiMoney(m.price()), m.routes()); - } } diff --git a/src/ext/java/org/opentripplanner/ext/vectortiles/VectorTilesResource.java b/src/ext/java/org/opentripplanner/ext/vectortiles/VectorTilesResource.java index af2715d6928..772db7394f3 100644 --- a/src/ext/java/org/opentripplanner/ext/vectortiles/VectorTilesResource.java +++ b/src/ext/java/org/opentripplanner/ext/vectortiles/VectorTilesResource.java @@ -66,7 +66,7 @@ public Response tileGet( z, locale, Arrays.asList(requestedLayers.split(",")), - serverContext.vectorTileLayers().layers(), + serverContext.vectorTileConfig().layers(), VectorTilesResource::crateLayerBuilder, serverContext ); @@ -89,15 +89,19 @@ public TileJson getTileJson( .filter(Predicate.not(Objects::isNull)) .toList(); - return new TileJson( - uri, - headers, - requestedLayers, - ignoreRouterId, - "vectorTiles", - envelope, - feedInfos - ); + List rLayers = Arrays.asList(requestedLayers.split(",")); + + var url = serverContext + .vectorTileConfig() + .basePath() + .map(overrideBasePath -> + TileJson.urlFromOverriddenBasePath(uri, headers, overrideBasePath, rLayers) + ) + .orElseGet(() -> + TileJson.urlWithDefaultPath(uri, headers, rLayers, ignoreRouterId, "vectorTiles") + ); + + return new TileJson(url, envelope, feedInfos); } private static LayerBuilder crateLayerBuilder( diff --git a/src/ext/resources/org/opentripplanner/ext/vectortiles/router-config.json b/src/ext/resources/org/opentripplanner/ext/vectortiles/router-config.json new file mode 100644 index 00000000000..df325d076a3 --- /dev/null +++ b/src/ext/resources/org/opentripplanner/ext/vectortiles/router-config.json @@ -0,0 +1,16 @@ +{ + "vectorTiles": { + "basePath": "/otp_ct/vectorTiles", + "layers": [ + { + "name": "stops", + "type": "Stop", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 600 + } + ] + } +} + diff --git a/src/main/java/org/opentripplanner/apis/APIEndpoints.java b/src/main/java/org/opentripplanner/apis/APIEndpoints.java index 555ea1659f1..b6b70eb238e 100644 --- a/src/main/java/org/opentripplanner/apis/APIEndpoints.java +++ b/src/main/java/org/opentripplanner/apis/APIEndpoints.java @@ -54,6 +54,8 @@ private APIEndpoints() { // scheduled to be removed and only here for backwards compatibility addIfEnabled(GtfsGraphQlApi, GtfsGraphQLAPI.GtfsGraphQLAPIOldPath.class); addIfEnabled(TransmodelGraphQlApi, TransmodelAPI.class); + // scheduled to be removed and only here for backwards compatibility + addIfEnabled(TransmodelGraphQlApi, TransmodelAPI.TransmodelAPIOldPath.class); // Sandbox extension APIs addIfEnabled(ActuatorAPI, ActuatorAPI.class); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java index b50bbaa9b9e..7101d132834 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GtfsGraphQLIndex.java @@ -77,7 +77,6 @@ import org.opentripplanner.apis.gtfs.datafetchers.VehicleRentalStationImpl; import org.opentripplanner.apis.gtfs.datafetchers.debugOutputImpl; import org.opentripplanner.apis.gtfs.datafetchers.elevationProfileComponentImpl; -import org.opentripplanner.apis.gtfs.datafetchers.fareComponentImpl; import org.opentripplanner.apis.gtfs.datafetchers.fareImpl; import org.opentripplanner.apis.gtfs.datafetchers.placeAtDistanceImpl; import org.opentripplanner.apis.gtfs.datafetchers.serviceTimeRangeImpl; @@ -129,7 +128,6 @@ protected static GraphQLSchema buildSchema() { .type(typeWiring.build(debugOutputImpl.class)) .type(typeWiring.build(DepartureRowImpl.class)) .type(typeWiring.build(elevationProfileComponentImpl.class)) - .type(typeWiring.build(fareComponentImpl.class)) .type(typeWiring.build(fareImpl.class)) .type(typeWiring.build(FeedImpl.class)) .type(typeWiring.build(FeedImpl.class)) diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/ItineraryImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/ItineraryImpl.java index 5e0ee6285a8..5399783bee0 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/ItineraryImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/ItineraryImpl.java @@ -3,6 +3,7 @@ import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; @@ -54,7 +55,7 @@ public DataFetcher>> fares() { Map result = new HashMap<>(); result.put("name", fareKey); result.put("fare", fare.getFare(fareKey)); - result.put("details", fare.getComponents(fareKey)); + result.put("details", List.of()); return result; }) .collect(Collectors.toList()); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/fareComponentImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/fareComponentImpl.java deleted file mode 100644 index a8c1b4d38f5..00000000000 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/fareComponentImpl.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.opentripplanner.apis.gtfs.datafetchers; - -import graphql.schema.DataFetcher; -import graphql.schema.DataFetchingEnvironment; -import java.util.stream.Collectors; -import org.opentripplanner.apis.gtfs.GraphQLRequestContext; -import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; -import org.opentripplanner.routing.core.FareComponent; -import org.opentripplanner.transit.model.network.Route; -import org.opentripplanner.transit.service.TransitService; - -public class fareComponentImpl implements GraphQLDataFetchers.GraphQLFareComponent { - - @Override - public DataFetcher cents() { - return environment -> getSource(environment).price().minorUnitAmount(); - } - - @Override - public DataFetcher currency() { - return environment -> getSource(environment).price().currency().getCurrencyCode(); - } - - @Override - public DataFetcher fareId() { - return environment -> getSource(environment).fareId().toString(); - } - - @Override - public DataFetcher> routes() { - return environment -> { - TransitService transitService = getTransitService(environment); - return getSource(environment) - .routes() - .stream() - .map(transitService::getRouteForId) - .collect(Collectors.toList()); - }; - } - - private TransitService getTransitService(DataFetchingEnvironment environment) { - return environment.getContext().transitService(); - } - - private FareComponent getSource(DataFetchingEnvironment environment) { - return environment.getSource(); - } -} diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/fareImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/fareImpl.java index e1986d215be..fb2c6ee184c 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/fareImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/fareImpl.java @@ -2,9 +2,9 @@ import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; +import java.util.List; import java.util.Map; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; -import org.opentripplanner.routing.core.FareComponent; import org.opentripplanner.transit.model.basic.Money; public class fareImpl implements GraphQLDataFetchers.GraphQLFare { @@ -15,8 +15,8 @@ public DataFetcher cents() { } @Override - public DataFetcher> components() { - return environment -> (Iterable) getSource(environment).get("details"); + public DataFetcher> components() { + return environment -> List.of(); } @Override diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java index d89453be056..cd002ec187b 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -40,7 +40,6 @@ import org.opentripplanner.model.plan.WalkStep; import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.routing.api.response.RoutingError; -import org.opentripplanner.routing.core.FareComponent; import org.opentripplanner.routing.graphfinder.NearbyStop; import org.opentripplanner.routing.graphfinder.PatternAtStop; import org.opentripplanner.routing.graphfinder.PlaceAtDistance; @@ -1239,7 +1238,7 @@ public interface GraphQLElevationProfileComponent { public interface GraphQLFare { public DataFetcher cents(); - public DataFetcher> components(); + public DataFetcher> components(); public DataFetcher currency(); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml b/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml index 68901bdccef..c720fc5a119 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/graphql-codegen.yml @@ -55,7 +55,6 @@ config: elevationProfileComponent: org.opentripplanner.model.plan.ElevationProfile.Step Emissions: org.opentripplanner.model.plan.Emissions#Emissions fare: java.util.Map#Map - fareComponent: org.opentripplanner.routing.core.FareComponent#FareComponent Feed: String Geometry: org.locationtech.jts.geom.Geometry#Geometry InputField: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField#GraphQLInputField diff --git a/src/main/java/org/opentripplanner/apis/support/TileJson.java b/src/main/java/org/opentripplanner/apis/support/TileJson.java index 2259d72d828..75aabb2b6c6 100644 --- a/src/main/java/org/opentripplanner/apis/support/TileJson.java +++ b/src/main/java/org/opentripplanner/apis/support/TileJson.java @@ -4,7 +4,9 @@ import jakarta.ws.rs.core.UriInfo; import java.io.Serializable; import java.util.Collection; +import java.util.List; import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; import org.opentripplanner.framework.io.HttpUtils; import org.opentripplanner.model.FeedInfo; import org.opentripplanner.service.worldenvelope.model.WorldEnvelope; @@ -34,15 +36,7 @@ public class TileJson implements Serializable { public final double[] bounds; public final double[] center; - public TileJson( - UriInfo uri, - HttpHeaders headers, - String layers, - String ignoreRouterId, - String path, - WorldEnvelope envelope, - Collection feedInfos - ) { + public TileJson(String tileUrl, WorldEnvelope envelope, Collection feedInfos) { attribution = feedInfos .stream() @@ -51,15 +45,7 @@ public TileJson( ) .collect(Collectors.joining(", ")); - tiles = - new String[] { - "%s/otp/routers/%s/%s/%s/{z}/{x}/{y}.pbf".formatted( - HttpUtils.getBaseAddress(uri, headers), - ignoreRouterId, - path, - layers - ), - }; + tiles = new String[] { tileUrl }; bounds = new double[] { @@ -72,4 +58,42 @@ public TileJson( var c = envelope.center(); center = new double[] { c.longitude(), c.latitude(), 9 }; } + + /** + * Creates a vector source layer URL from a hard-coded path plus information from the incoming + * HTTP request. + */ + public static String urlWithDefaultPath( + UriInfo uri, + HttpHeaders headers, + List layers, + String ignoreRouterId, + String path + ) { + return "%s/otp/routers/%s/%s/%s/{z}/{x}/{y}.pbf".formatted( + HttpUtils.getBaseAddress(uri, headers), + ignoreRouterId, + path, + String.join(",", layers) + ); + } + + /** + * Creates a vector source layer URL from a configured base path plus information from the incoming + * HTTP request. + */ + public static String urlFromOverriddenBasePath( + UriInfo uri, + HttpHeaders headers, + String overridePath, + List layers + ) { + var strippedPath = StringUtils.stripStart(overridePath, "/"); + strippedPath = StringUtils.stripEnd(strippedPath, "/"); + return "%s/%s/%s/{z}/{x}/{y}.pbf".formatted( + HttpUtils.getBaseAddress(uri, headers), + strippedPath, + String.join(",", layers) + ); + } } diff --git a/src/main/java/org/opentripplanner/apis/transmodel/TransmodelAPI.java b/src/main/java/org/opentripplanner/apis/transmodel/TransmodelAPI.java index 6af4ab1953c..3df6e1d6a51 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/TransmodelAPI.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/TransmodelAPI.java @@ -6,7 +6,6 @@ import jakarta.ws.rs.BadRequestException; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DefaultValue; -import jakarta.ws.rs.GET; import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; @@ -30,14 +29,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -// TODO move to org.opentripplanner.api.resource, this is a Jersey resource class - -@Path("/routers/{ignoreRouterId}/transmodel/index") -// It would be nice to get rid of the final /index. -@Produces(MediaType.APPLICATION_JSON) // One @Produces annotation for all endpoints. +@Path("/transmodel/v3") +@Produces(MediaType.APPLICATION_JSON) public class TransmodelAPI { - @SuppressWarnings("unused") private static final Logger LOG = LoggerFactory.getLogger(TransmodelAPI.class); private static GraphQLSchema schema; @@ -47,18 +42,25 @@ public class TransmodelAPI { private final TransmodelGraph index; private final ObjectMapper deserializer = new ObjectMapper(); - public TransmodelAPI( - @Context OtpServerRequestContext serverContext, - /** - * @deprecated The support for multiple routers are removed from OTP2. - * See https://github.com/opentripplanner/OpenTripPlanner/issues/2760 - */ - @Deprecated @PathParam("ignoreRouterId") String ignoreRouterId - ) { + public TransmodelAPI(@Context OtpServerRequestContext serverContext) { this.serverContext = serverContext; this.index = new TransmodelGraph(schema); } + /** + * This class is only here for backwards-compatibility. It will be removed in the future. + */ + @Path("/routers/{ignoreRouterId}/transmodel/index/graphql") + public static class TransmodelAPIOldPath extends TransmodelAPI { + + public TransmodelAPIOldPath( + @Context OtpServerRequestContext serverContext, + @PathParam("ignoreRouterId") String ignore + ) { + super(serverContext); + } + } + /** * This method should be called BEFORE the Web-Container is started and load new instances of this * class. This is a hack, and it would be better if the configuration was done more explicit and @@ -77,17 +79,7 @@ public static void setUp( schema = TransmodelGraphQLSchema.create(defaultRouteRequest, gqlUtil); } - /** - * Return 200 when service is loaded. - */ - @GET - @Path("/live") - public Response isAlive() { - return Response.status(Response.Status.NO_CONTENT).build(); - } - @POST - @Path("/graphql") @Consumes(MediaType.APPLICATION_JSON) public Response getGraphQL( HashMap queryParameters, @@ -130,7 +122,6 @@ public Response getGraphQL( } @POST - @Path("/graphql") @Consumes("application/graphql") public Response getGraphQL( String query, diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/stop/QuayType.java b/src/main/java/org/opentripplanner/apis/transmodel/model/stop/QuayType.java index b80efab77b5..0b22086dfaf 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/stop/QuayType.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/stop/QuayType.java @@ -15,6 +15,7 @@ import java.util.Collection; import java.util.Objects; import java.util.Optional; +import org.locationtech.jts.geom.Geometry; import org.opentripplanner.apis.transmodel.model.EnumTypes; import org.opentripplanner.apis.transmodel.model.plan.JourneyWhiteListed; import org.opentripplanner.apis.transmodel.model.scalars.GeoJSONCoordinatesScalar; @@ -25,9 +26,6 @@ import org.opentripplanner.transit.model.basic.Accessibility; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.network.TripPattern; -import org.opentripplanner.transit.model.site.AreaStop; -import org.opentripplanner.transit.model.site.GroupStop; -import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.Station; import org.opentripplanner.transit.model.site.StopLocation; @@ -80,11 +78,8 @@ public static GraphQLObjectType create( .type(Scalars.GraphQLString) .build() ) - .dataFetcher(environment -> - ( - ((StopLocation) environment.getSource()).getName() - .toString(GqlUtil.getLocale(environment)) - ) + .dataFetcher(env -> + (((StopLocation) env.getSource()).getName().toString(GqlUtil.getLocale(env))) ) .build() ) @@ -93,7 +88,7 @@ public static GraphQLObjectType create( .newFieldDefinition() .name("latitude") .type(Scalars.GraphQLFloat) - .dataFetcher(environment -> (((StopLocation) environment.getSource()).getLat())) + .dataFetcher(env -> (((StopLocation) env.getSource()).getLat())) .build() ) .field( @@ -101,7 +96,7 @@ public static GraphQLObjectType create( .newFieldDefinition() .name("longitude") .type(Scalars.GraphQLFloat) - .dataFetcher(environment -> (((StopLocation) environment.getSource()).getLon())) + .dataFetcher(env -> (((StopLocation) env.getSource()).getLon())) .build() ) .field( @@ -109,11 +104,8 @@ public static GraphQLObjectType create( .newFieldDefinition() .name("description") .type(Scalars.GraphQLString) - .dataFetcher(environment -> - GraphQLUtils.getTranslation( - ((StopLocation) environment.getSource()).getDescription(), - environment - ) + .dataFetcher(env -> + GraphQLUtils.getTranslation(((StopLocation) env.getSource()).getDescription(), env) ) .build() ) @@ -123,12 +115,12 @@ public static GraphQLObjectType create( .name("stopPlace") .description("The stop place to which this quay belongs to.") .type(stopPlaceType) - .dataFetcher(environment -> { - Station station = ((StopLocation) environment.getSource()).getParentStation(); + .dataFetcher(env -> { + Station station = ((StopLocation) env.getSource()).getParentStation(); if (station != null) { return new MonoOrMultiModalStation( station, - GqlUtil.getTransitService(environment).getMultiModalStationForStation(station) + GqlUtil.getTransitService(env).getMultiModalStationForStation(station) ); } else { return null; @@ -142,9 +134,9 @@ public static GraphQLObjectType create( .name("wheelchairAccessible") .type(EnumTypes.WHEELCHAIR_BOARDING) .description("Whether this quay is suitable for wheelchair boarding.") - .dataFetcher(environment -> + .dataFetcher(env -> Objects.requireNonNullElse( - (((StopLocation) environment.getSource()).getWheelchairAccessibility()), + (((StopLocation) env.getSource()).getWheelchairAccessibility()), Accessibility.NO_INFORMATION ) ) @@ -155,10 +147,8 @@ public static GraphQLObjectType create( .newFieldDefinition() .name("timeZone") .type(Scalars.GraphQLString) - .dataFetcher(environment -> - Optional - .ofNullable(((StopLocation) environment.getSource()).getTimeZone()) - .map(ZoneId::getId) + .dataFetcher(env -> + Optional.ofNullable(((StopLocation) env.getSource()).getTimeZone()).map(ZoneId::getId) ) .build() ) @@ -170,7 +160,7 @@ public static GraphQLObjectType create( .description( "Public code used to identify this quay within the stop place. For instance a platform code." ) - .dataFetcher(environment -> (((StopLocation) environment.getSource()).getPlatformCode())) + .dataFetcher(env -> (((StopLocation) env.getSource()).getPlatformCode())) .build() ) .field( @@ -180,10 +170,10 @@ public static GraphQLObjectType create( .withDirective(gqlUtil.timingData) .description("List of lines servicing this quay") .type(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(lineType)))) - .dataFetcher(environment -> + .dataFetcher(env -> GqlUtil - .getTransitService(environment) - .getPatternsForStop(environment.getSource(), true) + .getTransitService(env) + .getPatternsForStop(env.getSource(), true) .stream() .map(TripPattern::getRoute) .distinct() @@ -198,8 +188,8 @@ public static GraphQLObjectType create( .withDirective(gqlUtil.timingData) .description("List of journey patterns servicing this quay") .type(new GraphQLNonNull(new GraphQLList(journeyPatternType))) - .dataFetcher(environment -> - GqlUtil.getTransitService(environment).getPatternsForStop(environment.getSource(), true) + .dataFetcher(env -> + GqlUtil.getTransitService(env).getPatternsForStop(env.getSource(), true) ) .build() ) @@ -361,17 +351,9 @@ public static GraphQLObjectType create( .newFieldDefinition() .name("stopType") .type(Scalars.GraphQLString) - .dataFetcher(environment -> { - StopLocation stopLocation = environment.getSource(); - if (stopLocation instanceof RegularStop) { - return "regular"; - } else if (stopLocation instanceof AreaStop) { - return "flexible_area"; - } else if (stopLocation instanceof GroupStop) { - return "flexible_group"; - } - return null; - }) + .dataFetcher(env -> + StopTypeMapper.getStopType(((StopLocation) env.getSource()).getStopType()) + ) .build() ) .field( @@ -380,28 +362,22 @@ public static GraphQLObjectType create( .name("flexibleArea") .description("Geometry for flexible area.") .type(GeoJSONCoordinatesScalar.getGraphQGeoJSONCoordinatesScalar()) - .dataFetcher(environment -> - ( - environment.getSource() instanceof AreaStop areaStop - ? areaStop.getGeometry().getCoordinates() - : null - ) - ) + .dataFetcher(env -> { + StopLocation stopLocation = env.getSource(); + return stopLocation + .getEncompassingAreaGeometry() + .map(Geometry::getCoordinates) + .orElse(null); + }) .build() ) .field( GraphQLFieldDefinition .newFieldDefinition() .name("flexibleGroup") - .description("the Quays part of an flexible group.") + .description("the Quays part of a flexible group.") .type(GraphQLList.list(REF)) - .dataFetcher(environment -> - ( - environment.getSource() instanceof GroupStop groupStop - ? groupStop.getLocations() - : null - ) - ) + .dataFetcher(env -> ((StopLocation) env.getSource()).getChildLocations()) .build() ) .field( @@ -409,7 +385,7 @@ public static GraphQLObjectType create( .newFieldDefinition() .name("tariffZones") .type(new GraphQLNonNull(new GraphQLList(tariffZoneType))) - .dataFetcher(environment -> ((StopLocation) environment.getSource()).getFareZones()) + .dataFetcher(env -> ((StopLocation) env.getSource()).getFareZones()) .build() ) .build(); diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/stop/StopTypeMapper.java b/src/main/java/org/opentripplanner/apis/transmodel/model/stop/StopTypeMapper.java new file mode 100644 index 00000000000..e94347b72e5 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/stop/StopTypeMapper.java @@ -0,0 +1,17 @@ +package org.opentripplanner.apis.transmodel.model.stop; + +import org.opentripplanner.transit.model.site.StopType; + +/** + * Maps the StopType enum to a String used in the GraphQL API. + */ +public class StopTypeMapper { + + public static String getStopType(StopType stopType) { + return switch (stopType) { + case REGULAR -> "regular"; + case FLEXIBLE_AREA -> "flexible_area"; + case FLEXIBLE_GROUP -> "flexible_group"; + }; + } +} diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index f7ca564b149..f45d7d36413 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -52,6 +52,7 @@ public class DebugStyleSpec { static StyleSpec build( VectorSourceLayer regularStops, + VectorSourceLayer areaStops, VectorSourceLayer edges, VectorSourceLayer vertices ) { @@ -112,6 +113,15 @@ static StyleSpec build( .minZoom(15) .maxZoom(MAX_ZOOM) .intiallyHidden(), + StyleBuilder + .ofId("area-stop") + .typeFill() + .vectorSourceLayer(areaStops) + .fillColor(GREEN) + .fillOpacity(0.5f) + .fillOutlineColor(BLACK) + .minZoom(6) + .maxZoom(MAX_ZOOM), StyleBuilder .ofId("regular-stop") .typeCircle() diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java index 5d0778eae6d..03f4357e540 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java @@ -1,5 +1,6 @@ package org.opentripplanner.apis.vectortiles; +import static org.opentripplanner.apis.vectortiles.model.LayerType.AreaStop; import static org.opentripplanner.apis.vectortiles.model.LayerType.Edge; import static org.opentripplanner.apis.vectortiles.model.LayerType.GeofencingZones; import static org.opentripplanner.apis.vectortiles.model.LayerType.RegularStop; @@ -47,7 +48,7 @@ public class GraphInspectorVectorTileResource { private static final LayerParams REGULAR_STOPS = new LayerParams("regularStops", RegularStop); - private static final LayerParams AREA_STOPS = new LayerParams("areaStops", LayerType.AreaStop); + private static final LayerParams AREA_STOPS = new LayerParams("areaStops", AreaStop); private static final LayerParams GEOFENCING_ZONES = new LayerParams( "geofencingZones", GeofencingZones @@ -109,16 +110,16 @@ public TileJson getTileJson( ) { var envelope = serverContext.worldEnvelopeService().envelope().orElseThrow(); List feedInfos = feedInfos(); + List rlayer = Arrays.asList(requestedLayers.split(",")); - return new TileJson( + var url = TileJson.urlWithDefaultPath( uri, headers, - requestedLayers, + rlayer, ignoreRouterId, - "inspector/vectortile", - envelope, - feedInfos + "inspector/vectortile" ); + return new TileJson(url, envelope, feedInfos); } @GET @@ -140,6 +141,7 @@ public StyleSpec getTileJson(@Context UriInfo uri, @Context HttpHeaders headers) return DebugStyleSpec.build( REGULAR_STOPS.toVectorSourceLayer(stopsSource), + AREA_STOPS.toVectorSourceLayer(stopsSource), EDGES.toVectorSourceLayer(streetSource), VERTICES.toVectorSourceLayer(streetSource) ); diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java index 3b2f36d3156..07efe376968 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/model/StyleBuilder.java @@ -40,6 +40,7 @@ public enum LayerType { Circle, Line, Raster, + Fill, } private StyleBuilder(String id) { @@ -88,6 +89,11 @@ public StyleBuilder typeLine() { return this; } + public StyleBuilder typeFill() { + type(LayerType.Fill); + return this; + } + private StyleBuilder type(LayerType type) { props.put(TYPE, type.name().toLowerCase()); return this; @@ -132,6 +138,21 @@ public StyleBuilder lineWidth(ZoomDependentNumber zoomStops) { return this; } + public StyleBuilder fillColor(String color) { + paint.put("fill-color", validateColor(color)); + return this; + } + + public StyleBuilder fillOpacity(float opacity) { + paint.put("fill-opacity", opacity); + return this; + } + + public StyleBuilder fillOutlineColor(String color) { + paint.put("fill-outline-color", validateColor(color)); + return this; + } + /** * Hide this layer when the debug client starts. It can be made visible in the UI later. */ diff --git a/src/main/java/org/opentripplanner/framework/lang/ObjectUtils.java b/src/main/java/org/opentripplanner/framework/lang/ObjectUtils.java index 1e67b8f253e..a3f18748987 100644 --- a/src/main/java/org/opentripplanner/framework/lang/ObjectUtils.java +++ b/src/main/java/org/opentripplanner/framework/lang/ObjectUtils.java @@ -1,6 +1,7 @@ package org.opentripplanner.framework.lang; import java.util.function.Function; +import java.util.function.Supplier; import javax.annotation.Nullable; /** @@ -33,6 +34,18 @@ public static T ifNotNull( return ifNotNull(getter.apply(entity), defaultValue); } + /** + * Get the value or {@code null}, ignore any exceptions. This is useful if you must traverse + * a long call-chain like {@code a.b().c().d()...} when e.g. logging. + */ + public static T safeGetOrNull(Supplier body) { + try { + return body.get(); + } catch (Exception ignore) { + return null; + } + } + public static T requireNotInitialized(T oldValue, T newValue) { return requireNotInitialized(null, oldValue, newValue); } diff --git a/src/main/java/org/opentripplanner/framework/logging/Throttle.java b/src/main/java/org/opentripplanner/framework/logging/Throttle.java index 631d59a2697..ed8a2c1bef4 100644 --- a/src/main/java/org/opentripplanner/framework/logging/Throttle.java +++ b/src/main/java/org/opentripplanner/framework/logging/Throttle.java @@ -1,5 +1,6 @@ package org.opentripplanner.framework.logging; +import java.time.Duration; import org.opentripplanner.framework.time.TimeUtils; /** @@ -26,17 +27,20 @@ public class Throttle { private long timeout = Long.MIN_VALUE; private final String setupInfo; - Throttle(int quietPeriodMilliseconds) { - this.quietPeriodMilliseconds = quietPeriodMilliseconds; + /** + * Package local to be able to unit test. + */ + Throttle(Duration quietPeriod) { + this.quietPeriodMilliseconds = (int) quietPeriod.toMillis(); this.setupInfo = "(throttle " + TimeUtils.msToString(quietPeriodMilliseconds) + " interval)"; } public static Throttle ofOneSecond() { - return new Throttle(1000); + return new Throttle(Duration.ofSeconds(1)); } public static Throttle ofOneMinute() { - return new Throttle(1000 * 60); + return new Throttle(Duration.ofMinutes(1)); } public String setupInfo() { diff --git a/src/main/java/org/opentripplanner/framework/tostring/ToStringBuilder.java b/src/main/java/org/opentripplanner/framework/tostring/ToStringBuilder.java index cb5d26294db..6480b6f1187 100644 --- a/src/main/java/org/opentripplanner/framework/tostring/ToStringBuilder.java +++ b/src/main/java/org/opentripplanner/framework/tostring/ToStringBuilder.java @@ -13,9 +13,11 @@ import java.util.Collection; import java.util.Objects; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import org.opentripplanner.framework.lang.ObjectUtils; import org.opentripplanner.framework.lang.OtpNumberFormat; import org.opentripplanner.framework.time.DurationUtils; import org.opentripplanner.framework.time.TimeUtils; @@ -134,12 +136,20 @@ public ToStringBuilder addObj(String name, Object value, @Nullable Object ignore return addIfNotIgnored(name, value, ignoreValue, Object::toString); } + /** + * Add the result of the given supplier. If the supplier return {@code null} or an exceptions + * is thrown, then nothing is added - the result is ignored. + */ + public ToStringBuilder addObjOpSafe(String name, Supplier body) { + return addObj(name, ObjectUtils.safeGetOrNull(body)); + } + /** * Use this if you would like a custom toString function to convert the value. If the given value * is null, then the value is not printed. *

* Implementation note! The "Op" (Operation) suffix is necessary to separate this from - * {@link #addObj(String, Object, Object)}, when the last argument is null. + * {@link #addObj(String, Object, Object)}, when the last argument is null. */ public ToStringBuilder addObjOp( String name, @@ -176,7 +186,7 @@ public ToStringBuilder addDoubles(String name, double[] value, double ignoreValu return addIt(name, Arrays.toString(value)); } - /** Add collection if not null or not empty, all elements are added */ + /** Add the collection if not null or not empty, all elements are added */ public ToStringBuilder addCol(String name, Collection c) { return addIfNotNull(name, c == null || c.isEmpty() ? null : c); } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java b/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java index 7ec84763d81..37334161b0a 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java @@ -97,7 +97,7 @@ public void linkTransitStops(Graph graph, TransitModel transitModel) { .stream() .filter(GroupStop.class::isInstance) .map(GroupStop.class::cast) - .flatMap(g -> g.getLocations().stream().filter(RegularStop.class::isInstance)) + .flatMap(g -> g.getChildLocations().stream().filter(RegularStop.class::isInstance)) .toList() ); } diff --git a/src/main/java/org/opentripplanner/model/fare/ItineraryFares.java b/src/main/java/org/opentripplanner/model/fare/ItineraryFares.java index 0f9f815efe6..d60dbfb249e 100644 --- a/src/main/java/org/opentripplanner/model/fare/ItineraryFares.java +++ b/src/main/java/org/opentripplanner/model/fare/ItineraryFares.java @@ -1,6 +1,5 @@ package org.opentripplanner.model.fare; -import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; @@ -11,12 +10,10 @@ import java.util.Map; import java.util.Objects; import java.util.Set; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.lang.Sandbox; import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.model.plan.Leg; -import org.opentripplanner.routing.core.FareComponent; import org.opentripplanner.routing.core.FareType; import org.opentripplanner.transit.model.basic.Money; @@ -41,19 +38,6 @@ public class ItineraryFares { */ private final Multimap legProducts = LinkedHashMultimap.create(); - /** - * The fares V1 fare "components" that apply to individual legs (not the entire price of the - * itinerary). - *

- * This is an ill-thought-out concept that was bolted onto the existing implementation in 2016 and - * is going to be removed once HSL has migrated off it. - *

- * Note: LinkedHashMultimap keeps the insertion order - * @deprecated Exists only for backwards compatibility and will be removed in the future. - */ - @Deprecated - private final Multimap components = LinkedHashMultimap.create(); - /** * Holds the "fares" for the entire itinerary. The definition of a fare is not clear so * this is deprecated. @@ -93,19 +77,6 @@ public void addFare(FareType fareType, Money money) { fares.put(fareType, money); } - /** - * Add a collection of "fare components" for a type. These concepts are ill-defined and will be - * removed in the future. - *

- * @deprecated Only exitst for backwards compatibility. - * Use @{link {@link ItineraryFares#addItineraryProducts(Collection)}}, - * {@link ItineraryFares#addFareProduct(Leg, FareProduct)} or - */ - @Deprecated - public void addFareComponent(FareType fareType, List components) { - this.components.replaceValues(fareType, components); - } - /** * Add fare products that cover the entire itinerary, i.e. are valid for all legs. */ @@ -123,21 +94,11 @@ public void addItineraryProducts(Collection products) { * instead. */ @Nullable + @Deprecated public Money getFare(FareType type) { return fares.get(type); } - /** - * Get the "components" of a fare for a specific type. - *

- * Use {@link ItineraryFares#getItineraryProducts()} or {@link ItineraryFares#getLegProducts()} - * instead. - */ - @Deprecated - public List getComponents(FareType type) { - return List.copyOf(components.get(type)); - } - /** * Return the set of {@link FareType}s that are contained in this instance. */ @@ -148,7 +109,7 @@ public Set getFareTypes() { @Override public int hashCode() { - return Objects.hash(itineraryProducts, legProducts, components); + return Objects.hash(itineraryProducts, legProducts); } @Override @@ -188,32 +149,7 @@ public void addFareProduct(Leg leg, Collection fareProduct) { fareProduct.forEach(fp -> addFareProduct(leg, fp)); } - /** - * Convert the fare received via the deprecated {@link FareComponent} to leg products. This - * inverts the relationship: - * - fare component has several legs - * - leg product is a mapping from leg to a list of fare products - */ - @Nonnull - public Multimap legProductsFromComponents() { - Multimap legProductsFromComponents = HashMultimap.create(); - for (var type : getFareTypes()) { - var components = getComponents(type); - - for (var c : components) { - var firstLegStartTime = c.legs().get(0).getStartTime(); - for (var leg : c.legs()) { - final FareProduct fareProduct = FareProduct - .of(c.fareId(), type.name(), c.price()) - .build(); - - legProductsFromComponents.put( - leg, - new FareProductUse(fareProduct.uniqueInstanceId(firstLegStartTime), fareProduct) - ); - } - } - } - return legProductsFromComponents; + public void addFareProductUses(Multimap fareProducts) { + legProducts.putAll(fareProducts); } } diff --git a/src/main/java/org/opentripplanner/netex/mapping/FlexStopsMapper.java b/src/main/java/org/opentripplanner/netex/mapping/FlexStopsMapper.java index 1e6eb1979bb..7e06db882f1 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/FlexStopsMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/FlexStopsMapper.java @@ -62,6 +62,7 @@ StopLocation map(FlexibleStopPlace flexibleStopPlace) { List stops = new ArrayList<>(); TransitMode flexibleStopTransitMode = mapTransitMode(flexibleStopPlace); var areas = flexibleStopPlace.getAreas().getFlexibleAreaOrFlexibleAreaRefOrHailAndRideArea(); + List areaGeometries = new ArrayList<>(); for (var area : areas) { if (!(area instanceof FlexibleArea flexibleArea)) { issueStore.add( @@ -76,6 +77,7 @@ StopLocation map(FlexibleStopPlace flexibleStopPlace) { } Geometry flexibleAreaGeometry = mapGeometry(flexibleArea); + areaGeometries.add(flexibleAreaGeometry); if (shouldAddStopsFromArea(flexibleArea, flexibleStopPlace)) { stops.addAll( @@ -100,7 +102,8 @@ StopLocation map(FlexibleStopPlace flexibleStopPlace) { // get the ids for the area and stop place correct var builder = stopModelBuilder .groupStop(idFactory.createId(flexibleStopPlace.getId())) - .withName(new NonLocalizedString(flexibleStopPlace.getName().getValue())); + .withName(new NonLocalizedString(flexibleStopPlace.getName().getValue())) + .withEncompassingAreaGeometries(areaGeometries); stops.forEach(builder::addLocation); return builder.build(); } diff --git a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java index 30bd04eea73..c3c9ad2d0ae 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java @@ -374,7 +374,7 @@ private void mapFlexibleStopPlaces() { transitBuilder.stopModel().withAreaStop(areaStop); } else if (stopLocation instanceof GroupStop groupStop) { transitBuilder.stopModel().withGroupStop(groupStop); - for (var child : groupStop.getLocations()) { + for (var child : groupStop.getChildLocations()) { if (child instanceof AreaStop areaStop) { transitBuilder.stopModel().withAreaStop(areaStop); } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java index 1b5c836c832..c9e7f92263f 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java @@ -121,7 +121,7 @@ private TransitRouterResult route() { ); // Prepare transit search - var raptorRequest = RaptorRequestMapper.mapRequest( + var raptorRequest = RaptorRequestMapper.mapRequest( request, transitSearchTimeZero, serverContext.raptorConfig().isMultiThreaded(), diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java index 5f3b4b13746..91547b1f62f 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapper.java @@ -12,6 +12,7 @@ import org.opentripplanner.raptor.api.model.GeneralizedCostRelaxFunction; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; import org.opentripplanner.raptor.api.model.RaptorConstants; +import org.opentripplanner.raptor.api.model.RaptorTripSchedule; import org.opentripplanner.raptor.api.model.RelaxFunction; import org.opentripplanner.raptor.api.request.DebugRequestBuilder; import org.opentripplanner.raptor.api.request.Optimization; @@ -28,7 +29,7 @@ import org.opentripplanner.routing.api.request.framework.CostLinearFunction; import org.opentripplanner.transit.model.site.StopLocation; -public class RaptorRequestMapper { +public class RaptorRequestMapper { private final RouteRequest request; private final Collection accessPaths; @@ -56,7 +57,7 @@ private RaptorRequestMapper( this.meterRegistry = meterRegistry; } - public static RaptorRequest mapRequest( + public static RaptorRequest mapRequest( RouteRequest request, ZonedDateTime transitSearchTimeZero, boolean isMultiThreaded, @@ -65,7 +66,7 @@ public static RaptorRequest mapRequest( Duration searchWindowAccessSlack, MeterRegistry meterRegistry ) { - return new RaptorRequestMapper( + return new RaptorRequestMapper( request, isMultiThreaded, accessPaths, @@ -77,8 +78,8 @@ public static RaptorRequest mapRequest( .doMap(); } - private RaptorRequest doMap() { - var builder = new RaptorRequestBuilder(); + private RaptorRequest doMap() { + var builder = new RaptorRequestBuilder(); var searchParams = builder.searchParams(); var preferences = request.preferences(); @@ -119,12 +120,14 @@ private RaptorRequest doMap() { builder.withMultiCriteria(mcBuilder -> { var pt = preferences.transit(); var r = pt.raptor(); - if (!pt.relaxTransitGroupPriority().isNormal()) { - mcBuilder.withTransitPriorityCalculator(TransitGroupPriority32n.priorityCalculator()); - mcBuilder.withRelaxC1(mapRelaxCost(pt.relaxTransitGroupPriority())); - } else { + + // Note! If a pass-through-point exists, then the transit-group-priority feature is disabled + if (!request.getPassThroughPoints().isEmpty()) { mcBuilder.withPassThroughPoints(mapPassThroughPoints()); r.relaxGeneralizedCostAtDestination().ifPresent(mcBuilder::withRelaxCostAtDestination); + } else if (!pt.relaxTransitGroupPriority().isNormal()) { + mcBuilder.withTransitPriorityCalculator(TransitGroupPriority32n.priorityCalculator()); + mcBuilder.withRelaxC1(mapRelaxCost(pt.relaxTransitGroupPriority())); } }); @@ -174,13 +177,15 @@ private RaptorRequest doMap() { } // Add this last, it depends on generating an alias from the set values - builder.performanceTimers( - new PerformanceTimersForRaptor( - builder.generateAlias(), - preferences.system().tags(), - meterRegistry - ) - ); + if (meterRegistry != null) { + builder.performanceTimers( + new PerformanceTimersForRaptor( + builder.generateAlias(), + preferences.system().tags(), + meterRegistry + ) + ); + } return builder.build(); } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfigurator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfigurator.java index 826b9c09a13..6ef82786b99 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfigurator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfigurator.java @@ -11,7 +11,7 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.cost.grouppriority.TransitGroupPriority32n; import org.opentripplanner.routing.api.request.request.filter.TransitGroupSelect; import org.opentripplanner.transit.model.framework.FeedScopedId; -import org.opentripplanner.transit.model.network.RoutingTripPattern; +import org.opentripplanner.transit.model.network.TripPattern; /** * This class dynamically builds an index of transit-group-ids from the @@ -94,16 +94,14 @@ public static PriorityGroupConfigurator of( *

* @throws IllegalArgumentException if more than 32 group-ids are requested. */ - public int lookupTransitGroupPriorityId(RoutingTripPattern tripPattern) { + public int lookupTransitGroupPriorityId(TripPattern tripPattern) { if (!enabled || tripPattern == null) { return baseGroupId; } - var p = tripPattern.getPattern(); - for (var it : agencyMatchersIds) { - if (it.matcher().match(p)) { - var agencyId = p.getRoute().getAgency().getId(); + if (it.matcher().match(tripPattern)) { + var agencyId = tripPattern.getRoute().getAgency().getId(); int groupId = it.ids().get(agencyId); if (groupId < 0) { @@ -115,7 +113,7 @@ public int lookupTransitGroupPriorityId(RoutingTripPattern tripPattern) { } for (var it : globalMatchersIds) { - if (it.matcher.match(p)) { + if (it.matcher.match(tripPattern)) { return it.groupId(); } } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java index b8f915d6eb4..863a4ca9ae8 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java @@ -147,7 +147,7 @@ static List merge( tripPattern.getAlightingPossible(), BoardAlight.ALIGHT ), - priorityGroupConfigurator.lookupTransitGroupPriorityId(tripPattern) + priorityGroupConfigurator.lookupTransitGroupPriorityId(tripPattern.getPattern()) ) ); } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleWithOffset.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleWithOffset.java index 3eb26150d97..642dbd2d6e5 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleWithOffset.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleWithOffset.java @@ -107,7 +107,9 @@ public int getSecondsOffset() { public String toString() { return ToStringBuilder .of(TripScheduleWithOffset.class) - .addObj("trip", pattern.debugInfo()) + .addObj("info", pattern.debugInfo()) + .addObjOpSafe("id", () -> tripTimes.getTrip().getId()) + .addObjOpSafe("pattern.id", () -> pattern.getTripPattern().getPattern().getId()) .addServiceTime("depart", secondsOffset + getOriginalTripTimes().getDepartureTime(0)) .toString(); } diff --git a/src/main/java/org/opentripplanner/routing/core/FareComponent.java b/src/main/java/org/opentripplanner/routing/core/FareComponent.java deleted file mode 100644 index 4c609a80f19..00000000000 --- a/src/main/java/org/opentripplanner/routing/core/FareComponent.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.opentripplanner.routing.core; - -import java.util.List; -import org.opentripplanner.model.fare.FareProduct; -import org.opentripplanner.model.plan.Leg; -import org.opentripplanner.transit.model.basic.Money; -import org.opentripplanner.transit.model.framework.FeedScopedId; - -/** - *

- * FareComponent is a sequence of routes for a particular fare. - *

- * @deprecated Because it exists only for backwards compatibility, and you should use the Fares V2 - * type, namely {@link FareProduct}. - */ -@Deprecated -public record FareComponent(FeedScopedId fareId, Money price, List legs) { - public List routes() { - return legs.stream().map(l -> l.getRoute().getId()).toList(); - } -} diff --git a/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java b/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java index fa6ead99c5e..fa3a7069e2d 100644 --- a/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java +++ b/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java @@ -9,7 +9,6 @@ import org.opentripplanner.ext.emissions.EmissionsService; import org.opentripplanner.ext.ridehailing.RideHailingService; import org.opentripplanner.ext.stopconsolidation.StopConsolidationService; -import org.opentripplanner.ext.vectortiles.VectorTilesResource; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.inspector.raster.TileRendererManager; import org.opentripplanner.raptor.api.request.RaptorTuningParameters; @@ -23,6 +22,7 @@ import org.opentripplanner.service.realtimevehicles.RealtimeVehicleService; import org.opentripplanner.service.vehiclerental.VehicleRentalService; import org.opentripplanner.service.worldenvelope.WorldEnvelopeService; +import org.opentripplanner.standalone.config.routerconfig.VectorTileConfig; import org.opentripplanner.standalone.config.sandbox.FlexConfig; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.search.state.State; @@ -119,7 +119,7 @@ default GraphFinder graphFinder() { FlexConfig flexConfig(); - VectorTilesResource.LayersParameters vectorTileLayers(); + VectorTileConfig vectorTileConfig(); default DataOverlayContext dataOverlayContext(RouteRequest request) { return OTPFeature.DataOverlay.isOnElseNull(() -> diff --git a/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java b/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java index ae92486037d..bf97155b747 100644 --- a/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java @@ -9,7 +9,6 @@ import java.io.Serializable; import java.util.List; import org.opentripplanner.ext.ridehailing.RideHailingServiceParameters; -import org.opentripplanner.ext.vectortiles.VectorTilesResource; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; import org.opentripplanner.standalone.config.routerconfig.RideHailingServicesConfig; @@ -48,7 +47,7 @@ public class RouterConfig implements Serializable { private final RideHailingServicesConfig rideHailingConfig; private final FlexConfig flexConfig; private final TransmodelAPIConfig transmodelApi; - private final VectorTileConfig vectorTileLayers; + private final VectorTileConfig vectorTileConfig; public RouterConfig(JsonNode node, String source, boolean logUnusedParams) { this(new NodeAdapter(node, source), logUnusedParams); @@ -72,7 +71,7 @@ public RouterConfig(JsonNode node, String source, boolean logUnusedParams) { this.routingRequestDefaults.setMaxSearchWindow(transitConfig.maxSearchWindow()); this.updatersParameters = new UpdatersConfig(root); this.rideHailingConfig = new RideHailingServicesConfig(root); - this.vectorTileLayers = VectorTileConfig.mapVectorTilesParameters(root, "vectorTileLayers"); + this.vectorTileConfig = VectorTileConfig.mapVectorTilesParameters(root, "vectorTiles"); this.flexConfig = new FlexConfig(root, "flex"); if (logUnusedParams && LOG.isWarnEnabled()) { @@ -124,8 +123,8 @@ public List rideHailingServiceParameters() { return rideHailingConfig.rideHailingServiceParameters(); } - public VectorTilesResource.LayersParameters vectorTileLayers() { - return vectorTileLayers; + public VectorTileConfig vectorTileConfig() { + return vectorTileConfig; } public FlexConfig flexConfig() { diff --git a/src/main/java/org/opentripplanner/standalone/config/routerconfig/VectorTileConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerconfig/VectorTileConfig.java index a0342910be9..6f7d6967ce8 100644 --- a/src/main/java/org/opentripplanner/standalone/config/routerconfig/VectorTileConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/routerconfig/VectorTileConfig.java @@ -5,9 +5,12 @@ import static org.opentripplanner.inspector.vector.LayerParameters.MAX_ZOOM; import static org.opentripplanner.inspector.vector.LayerParameters.MIN_ZOOM; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_0; +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_5; import java.util.Collection; import java.util.List; +import java.util.Optional; +import javax.annotation.Nullable; import org.opentripplanner.ext.vectortiles.VectorTilesResource; import org.opentripplanner.inspector.vector.LayerParameters; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; @@ -15,12 +18,18 @@ public class VectorTileConfig implements VectorTilesResource.LayersParameters { - List> layers; + public static final VectorTileConfig DEFAULT = new VectorTileConfig(List.of(), null); + private final List> layers; - public VectorTileConfig( - Collection> layers + @Nullable + private final String basePath; + + VectorTileConfig( + Collection> layers, + @Nullable String basePath ) { this.layers = List.copyOf(layers); + this.basePath = basePath; } @Override @@ -28,16 +37,41 @@ public List> layers() { return layers; } - public static VectorTileConfig mapVectorTilesParameters( - NodeAdapter root, - String vectorTileLayers - ) { + public Optional basePath() { + return Optional.ofNullable(basePath); + } + + public static VectorTileConfig mapVectorTilesParameters(NodeAdapter node, String paramName) { + var root = node.of(paramName).summary("Vector tile configuration").asObject(); return new VectorTileConfig( root - .of(vectorTileLayers) + .of("layers") .since(V2_0) .summary("Configuration of the individual layers for the Mapbox vector tiles.") - .asObjects(VectorTileConfig::mapLayer) + .asObjects(VectorTileConfig::mapLayer), + root + .of("basePath") + .since(V2_5) + .summary("The path of the vector tile source URLs in `tilejson.json`.") + .description( + """ + This is useful if you have a proxy setup and rewrite the path that is passed to OTP. + + If you don't configure this optional value then the path returned in `tilejson.json` is in + the format `/otp/routers/default/vectorTiles/layer1,layer2/{z}/{x}/{x}.pbf`. + If you, for example, set a value of `/otp_test/tiles` then the returned path changes to + `/otp_test/tiles/layer1,layer2/{z}/{x}/{x}.pbf`. + + The protocol and host are always read from the incoming HTTP request. If you run OTP behind + a proxy then make sure to set the headers `X-Forwarded-Proto` and `X-Forwarded-Host` to make OTP + return the protocol and host for the original request and not the proxied one. + + **Note:** This does _not_ change the path that OTP itself serves the tiles or `tilejson.json` + responses but simply changes the URLs listed in `tilejson.json`. The rewriting of the path + is expected to be handled by a proxy. + """ + ) + .asString(DEFAULT.basePath) ); } diff --git a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationModule.java b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationModule.java index c9d7253b0be..5d8efcd3a5b 100644 --- a/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationModule.java +++ b/src/main/java/org/opentripplanner/standalone/configure/ConstructApplicationModule.java @@ -49,7 +49,7 @@ OtpServerRequestContext providesServerContext( graph, transitService, Metrics.globalRegistry, - routerConfig.vectorTileLayers(), + routerConfig.vectorTileConfig(), worldEnvelopeService, realtimeVehicleService, vehicleRentalService, diff --git a/src/main/java/org/opentripplanner/standalone/server/DefaultServerRequestContext.java b/src/main/java/org/opentripplanner/standalone/server/DefaultServerRequestContext.java index f14fea66693..9a586219ba1 100644 --- a/src/main/java/org/opentripplanner/standalone/server/DefaultServerRequestContext.java +++ b/src/main/java/org/opentripplanner/standalone/server/DefaultServerRequestContext.java @@ -8,7 +8,6 @@ import org.opentripplanner.ext.emissions.EmissionsService; import org.opentripplanner.ext.ridehailing.RideHailingService; import org.opentripplanner.ext.stopconsolidation.StopConsolidationService; -import org.opentripplanner.ext.vectortiles.VectorTilesResource; import org.opentripplanner.inspector.raster.TileRendererManager; import org.opentripplanner.raptor.api.request.RaptorTuningParameters; import org.opentripplanner.raptor.configure.RaptorConfig; @@ -24,6 +23,7 @@ import org.opentripplanner.standalone.api.HttpRequestScoped; import org.opentripplanner.standalone.api.OtpServerRequestContext; import org.opentripplanner.standalone.config.routerconfig.TransitRoutingConfig; +import org.opentripplanner.standalone.config.routerconfig.VectorTileConfig; import org.opentripplanner.standalone.config.sandbox.FlexConfig; import org.opentripplanner.transit.service.TransitService; @@ -39,7 +39,7 @@ public class DefaultServerRequestContext implements OtpServerRequestContext { private final MeterRegistry meterRegistry; private final RaptorConfig raptorConfig; private final TileRendererManager tileRendererManager; - private final VectorTilesResource.LayersParameters vectorTileLayers; + private final VectorTileConfig vectorTileConfig; private final FlexConfig flexConfig; private final TraverseVisitor traverseVisitor; private final WorldEnvelopeService worldEnvelopeService; @@ -59,7 +59,7 @@ private DefaultServerRequestContext( MeterRegistry meterRegistry, RaptorConfig raptorConfig, TileRendererManager tileRendererManager, - VectorTilesResource.LayersParameters vectorTileLayers, + VectorTileConfig vectorTileConfig, WorldEnvelopeService worldEnvelopeService, RealtimeVehicleService realtimeVehicleService, VehicleRentalService vehicleRentalService, @@ -75,7 +75,7 @@ private DefaultServerRequestContext( this.meterRegistry = meterRegistry; this.raptorConfig = raptorConfig; this.tileRendererManager = tileRendererManager; - this.vectorTileLayers = vectorTileLayers; + this.vectorTileConfig = vectorTileConfig; this.vehicleRentalService = vehicleRentalService; this.flexConfig = flexConfig; this.traverseVisitor = traverseVisitor; @@ -97,7 +97,7 @@ public static DefaultServerRequestContext create( Graph graph, TransitService transitService, MeterRegistry meterRegistry, - VectorTilesResource.LayersParameters vectorTileLayers, + VectorTileConfig vectorTileConfig, WorldEnvelopeService worldEnvelopeService, RealtimeVehicleService realtimeVehicleService, VehicleRentalService vehicleRentalService, @@ -115,7 +115,7 @@ public static DefaultServerRequestContext create( meterRegistry, raptorConfig, new TileRendererManager(graph, routeRequestDefaults.preferences()), - vectorTileLayers, + vectorTileConfig, worldEnvelopeService, realtimeVehicleService, vehicleRentalService, @@ -220,8 +220,8 @@ public FlexConfig flexConfig() { } @Override - public VectorTilesResource.LayersParameters vectorTileLayers() { - return vectorTileLayers; + public VectorTileConfig vectorTileConfig() { + return vectorTileConfig; } @Override diff --git a/src/main/java/org/opentripplanner/transit/model/network/RoutingTripPattern.java b/src/main/java/org/opentripplanner/transit/model/network/RoutingTripPattern.java index 00033b5f798..98d30432b32 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/RoutingTripPattern.java +++ b/src/main/java/org/opentripplanner/transit/model/network/RoutingTripPattern.java @@ -117,7 +117,7 @@ public Route route() { @Override public String debugInfo() { - return pattern.logName() + " @" + index; + return pattern.logName() + " #" + index; } @Override diff --git a/src/main/java/org/opentripplanner/transit/model/site/AreaStop.java b/src/main/java/org/opentripplanner/transit/model/site/AreaStop.java index c369c995f09..35576e0c42f 100644 --- a/src/main/java/org/opentripplanner/transit/model/site/AreaStop.java +++ b/src/main/java/org/opentripplanner/transit/model/site/AreaStop.java @@ -1,6 +1,7 @@ package org.opentripplanner.transit.model.site; import java.util.Objects; +import java.util.Optional; import java.util.function.IntSupplier; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -84,6 +85,12 @@ public I18NString getUrl() { return url; } + @Nonnull + @Override + public StopType getStopType() { + return StopType.FLEXIBLE_AREA; + } + @Override public String getFirstZoneAsString() { return zoneId; @@ -103,6 +110,14 @@ public Geometry getGeometry() { return geometry; } + /** + * Returns the geometry of area that defines the stop, in this case the same as getGeometry. + */ + @Override + public Optional getEncompassingAreaGeometry() { + return Optional.of(geometry); + } + @Override public boolean isPartOfStation() { return false; diff --git a/src/main/java/org/opentripplanner/transit/model/site/GroupStop.java b/src/main/java/org/opentripplanner/transit/model/site/GroupStop.java index ac3cca3dd13..edee00e27e1 100644 --- a/src/main/java/org/opentripplanner/transit/model/site/GroupStop.java +++ b/src/main/java/org/opentripplanner/transit/model/site/GroupStop.java @@ -1,7 +1,8 @@ package org.opentripplanner.transit.model.site; +import java.util.List; import java.util.Objects; -import java.util.Set; +import java.util.Optional; import java.util.function.IntSupplier; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -20,10 +21,12 @@ public class GroupStop implements StopLocation { private final int index; - private final Set stopLocations; + private final List stopLocations; private final I18NString name; private final GeometryCollection geometry; + private final GeometryCollection encompassingAreaGeometry; + private final WgsCoordinate centroid; GroupStop(GroupStopBuilder builder) { @@ -33,6 +36,7 @@ public class GroupStop this.geometry = builder.geometry(); this.centroid = Objects.requireNonNull(builder.centroid()); this.stopLocations = builder.stopLocations(); + this.encompassingAreaGeometry = builder.encompassingAreaGeometry(); } public static GroupStopBuilder of(FeedScopedId id, IntSupplier indexCounter) { @@ -60,6 +64,12 @@ public I18NString getUrl() { return null; } + @Override + @Nonnull + public StopType getStopType() { + return StopType.FLEXIBLE_GROUP; + } + @Override public String getFirstZoneAsString() { return null; @@ -74,11 +84,25 @@ public WgsCoordinate getCoordinate() { return centroid; } + /** + * Returns the geometry of all stops and areas belonging to this location group. + */ @Override public Geometry getGeometry() { return geometry; } + /** + * Returns the geometry of the area that encompasses the bounds of this StopLocation group. If the + * group is defined only as a list of stops, this will return the same as getGeometry. If on the + * other hand the group is defined as an area and the stops are inferred from that area, then this + * will return the geometry of the area. + */ + @Override + public Optional getEncompassingAreaGeometry() { + return Optional.ofNullable(encompassingAreaGeometry).or(() -> Optional.of(geometry)); + } + @Override public boolean isPartOfStation() { return false; @@ -92,7 +116,9 @@ public boolean isPartOfSameStationAs(StopLocation alternativeStop) { /** * Returns all the locations belonging to this location group. */ - public Set getLocations() { + @Override + @Nonnull + public List getChildLocations() { return stopLocations; } @@ -101,7 +127,7 @@ public boolean sameAs(@Nonnull GroupStop other) { return ( getId().equals(other.getId()) && Objects.equals(name, other.getName()) && - Objects.equals(stopLocations, other.getLocations()) + Objects.equals(stopLocations, other.getChildLocations()) ); } diff --git a/src/main/java/org/opentripplanner/transit/model/site/GroupStopBuilder.java b/src/main/java/org/opentripplanner/transit/model/site/GroupStopBuilder.java index 5e0dc596eba..8873e6ede99 100644 --- a/src/main/java/org/opentripplanner/transit/model/site/GroupStopBuilder.java +++ b/src/main/java/org/opentripplanner/transit/model/site/GroupStopBuilder.java @@ -1,7 +1,7 @@ package org.opentripplanner.transit.model.site; -import java.util.HashSet; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; import java.util.function.IntSupplier; import javax.annotation.Nonnull; import org.locationtech.jts.geom.Envelope; @@ -19,13 +19,15 @@ public class GroupStopBuilder extends AbstractEntityBuilder stopLocations = new HashSet<>(); + private List stopLocations = new ArrayList<>(); private GeometryCollection geometry = new GeometryCollection( null, GeometryUtils.getGeometryFactory() ); + private GeometryCollection encompassingAreaGeometry = null; + private WgsCoordinate centroid; GroupStopBuilder(FeedScopedId id, IntSupplier indexCounter) { @@ -38,7 +40,7 @@ public class GroupStopBuilder extends AbstractEntityBuilder(original.getLocations()); + this.stopLocations = new ArrayList<>(original.getChildLocations()); this.geometry = (GeometryCollection) original.getGeometry(); this.centroid = original.getCoordinate(); } @@ -53,6 +55,15 @@ public GroupStopBuilder withName(I18NString name) { return this; } + public GroupStopBuilder withEncompassingAreaGeometries(List geometries) { + this.encompassingAreaGeometry = + new GeometryCollection( + geometries.toArray(new Geometry[0]), + GeometryUtils.getGeometryFactory() + ); + return this; + } + public I18NString name() { return name; } @@ -82,14 +93,18 @@ public GroupStopBuilder addLocation(StopLocation location) { return this; } - public Set stopLocations() { - return Set.copyOf(stopLocations); + public List stopLocations() { + return List.copyOf(stopLocations); } public GeometryCollection geometry() { return geometry; } + public GeometryCollection encompassingAreaGeometry() { + return encompassingAreaGeometry; + } + public WgsCoordinate centroid() { return centroid; } diff --git a/src/main/java/org/opentripplanner/transit/model/site/RegularStop.java b/src/main/java/org/opentripplanner/transit/model/site/RegularStop.java index 5d5760337ff..4c1638e7bae 100644 --- a/src/main/java/org/opentripplanner/transit/model/site/RegularStop.java +++ b/src/main/java/org/opentripplanner/transit/model/site/RegularStop.java @@ -81,6 +81,12 @@ public I18NString getUrl() { return url; } + @Nonnull + @Override + public StopType getStopType() { + return StopType.REGULAR; + } + @Override @Nullable public ZoneId getTimeZone() { diff --git a/src/main/java/org/opentripplanner/transit/model/site/StopLocation.java b/src/main/java/org/opentripplanner/transit/model/site/StopLocation.java index 84658c16be8..a3cbd1a8014 100644 --- a/src/main/java/org/opentripplanner/transit/model/site/StopLocation.java +++ b/src/main/java/org/opentripplanner/transit/model/site/StopLocation.java @@ -3,6 +3,7 @@ import java.time.ZoneId; import java.util.Collection; import java.util.List; +import java.util.Optional; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.locationtech.jts.geom.Geometry; @@ -43,6 +44,9 @@ public interface StopLocation extends LogInfo { @Nullable I18NString getUrl(); + @Nonnull + StopType getStopType(); + /** * Short text or a number that identifies the location for riders. These codes are often used in * phone-based reservation systems to make it easier for riders to specify a particular location. @@ -121,6 +125,14 @@ default String getFirstZoneAsString() { @Nullable Geometry getGeometry(); + /** + * The geometry of the area that encompasses the bounds of the stop area. If the stop is defined + * as a point, this is null. + */ + default Optional getEncompassingAreaGeometry() { + return Optional.empty(); + } + @Nullable default ZoneId getTimeZone() { return null; @@ -135,6 +147,14 @@ default StopTransferPriority getPriority() { boolean isPartOfSameStationAs(StopLocation alternativeStop); + /** + * Returns the child locations of this location, for example StopLocations within a GroupStop. + */ + @Nullable + default List getChildLocations() { + return null; + } + @Override default String logName() { return ObjectUtils.ifNotNull(getName(), Object::toString, null); diff --git a/src/main/java/org/opentripplanner/transit/model/site/StopType.java b/src/main/java/org/opentripplanner/transit/model/site/StopType.java new file mode 100644 index 00000000000..69f11bb6b39 --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/site/StopType.java @@ -0,0 +1,19 @@ +package org.opentripplanner.transit.model.site; + +/** + * The type of a stop location. + */ +public enum StopType { + /** + * A regular stop defined geographically as a point. + */ + REGULAR, + /** + * Boarding and alighting is allowed anywhere within the geographic area of this stop. + */ + FLEXIBLE_AREA, + /** + * A stop that consists of multiple other stops, area or regular. + */ + FLEXIBLE_GROUP, +} diff --git a/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql b/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql index 9b49283c180..25cf95ca2f9 100644 --- a/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql +++ b/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql @@ -579,7 +579,7 @@ type Quay implements PlaceInterface { ): [EstimatedCall!]! @timingData "Geometry for flexible area." flexibleArea: Coordinates - "the Quays part of an flexible group." + "the Quays part of a flexible group." flexibleGroup: [Quay] id: ID! "List of journey patterns servicing this quay" diff --git a/src/test/java/org/opentripplanner/TestServerContext.java b/src/test/java/org/opentripplanner/TestServerContext.java index 1f3e6491232..5d74dbba240 100644 --- a/src/test/java/org/opentripplanner/TestServerContext.java +++ b/src/test/java/org/opentripplanner/TestServerContext.java @@ -16,6 +16,7 @@ import org.opentripplanner.service.worldenvelope.WorldEnvelopeService; import org.opentripplanner.service.worldenvelope.internal.DefaultWorldEnvelopeRepository; import org.opentripplanner.service.worldenvelope.internal.DefaultWorldEnvelopeService; +import org.opentripplanner.service.worldenvelope.model.WorldEnvelope; import org.opentripplanner.standalone.api.OtpServerRequestContext; import org.opentripplanner.standalone.config.RouterConfig; import org.opentripplanner.standalone.server.DefaultServerRequestContext; @@ -42,7 +43,7 @@ public static OtpServerRequestContext createServerContext( graph, new DefaultTransitService(transitModel), Metrics.globalRegistry, - routerConfig.vectorTileLayers(), + routerConfig.vectorTileConfig(), createWorldEnvelopeService(), createRealtimeVehicleService(transitService), createVehicleRentalService(), @@ -58,7 +59,14 @@ public static OtpServerRequestContext createServerContext( /** Static factory method to create a service for test purposes. */ public static WorldEnvelopeService createWorldEnvelopeService() { - return new DefaultWorldEnvelopeService(new DefaultWorldEnvelopeRepository()); + var repository = new DefaultWorldEnvelopeRepository(); + var envelope = WorldEnvelope + .of() + .expandToIncludeStreetEntities(0, 0) + .expandToIncludeStreetEntities(1, 1) + .build(); + repository.saveEnvelope(envelope); + return new DefaultWorldEnvelopeService(repository); } public static RealtimeVehicleService createRealtimeVehicleService(TransitService transitService) { diff --git a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java index a65c46b12fe..6e99e096c2c 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -61,7 +61,6 @@ import org.opentripplanner.routing.alertpatch.TimePeriod; import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.routing.api.request.RouteRequest; -import org.opentripplanner.routing.core.FareComponent; import org.opentripplanner.routing.core.FareType; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.graphfinder.GraphFinder; @@ -185,10 +184,6 @@ static void setup() { var fares = new ItineraryFares(); fares.addFare(FareType.regular, Money.euros(3.1f)); - fares.addFareComponent( - FareType.regular, - List.of(new FareComponent(id("AB"), Money.euros(3.1f), List.of(busLeg))) - ); var dayPass = fareProduct("day-pass"); fares.addItineraryProducts(List.of(dayPass)); diff --git a/src/test/java/org/opentripplanner/apis/support/TileJsonTest.java b/src/test/java/org/opentripplanner/apis/support/TileJsonTest.java new file mode 100644 index 00000000000..ac3b7bca522 --- /dev/null +++ b/src/test/java/org/opentripplanner/apis/support/TileJsonTest.java @@ -0,0 +1,43 @@ +package org.opentripplanner.apis.support; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.glassfish.jersey.server.internal.routing.UriRoutingContext; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.opentripplanner.test.support.HttpForTest; + +class TileJsonTest { + + private static final List LAYERS = List.of("stops", "rentalVehicles"); + + @ParameterizedTest + @ValueSource( + strings = { + "/otp_ct/vectorTiles", + "otp_ct/vectorTiles/", + "otp_ct/vectorTiles///", + "///otp_ct/vectorTiles/", + } + ) + void overrideBasePath(String basePath) { + var req = HttpForTest.containerRequest(); + var uriInfo = new UriRoutingContext(req); + assertEquals( + "https://localhost:8080/otp_ct/vectorTiles/stops,rentalVehicles/{z}/{x}/{y}.pbf", + TileJson.urlFromOverriddenBasePath(uriInfo, req, basePath, LAYERS) + ); + } + + @Test + void defaultPath() { + var req = HttpForTest.containerRequest(); + var uriInfo = new UriRoutingContext(req); + assertEquals( + "https://localhost:8080/otp/routers/default/vectorTiles/stops,rentalVehicles/{z}/{x}/{y}.pbf", + TileJson.urlWithDefaultPath(uriInfo, req, LAYERS, "default", "vectorTiles") + ); + } +} diff --git a/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java b/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java index 9a01a36cbcb..127fe66f0ea 100644 --- a/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/transmodel/mapping/TripRequestMapperTest.java @@ -124,7 +124,7 @@ public class TripRequestMapperTest implements PlanTestConstants { graph, transitService, Metrics.globalRegistry, - RouterConfig.DEFAULT.vectorTileLayers(), + RouterConfig.DEFAULT.vectorTileConfig(), new DefaultWorldEnvelopeService(new DefaultWorldEnvelopeRepository()), new DefaultRealtimeVehicleService(transitService), new DefaultVehicleRentalService(), diff --git a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java index befd34a3b38..f2c3ebf49de 100644 --- a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java +++ b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java @@ -15,10 +15,11 @@ class DebugStyleSpecTest { @Test void spec() { var vectorSource = new VectorSource("vectorSource", "https://example.com"); - var stops = new VectorSourceLayer(vectorSource, "stops"); + var regularStops = new VectorSourceLayer(vectorSource, "stops"); + var areaStops = new VectorSourceLayer(vectorSource, "stops"); var edges = new VectorSourceLayer(vectorSource, "edges"); var vertices = new VectorSourceLayer(vectorSource, "vertices"); - var spec = DebugStyleSpec.build(stops, edges, vertices); + var spec = DebugStyleSpec.build(regularStops, areaStops, edges, vertices); var json = ObjectMappers.ignoringExtraFields().valueToTree(spec); var expectation = RESOURCES.fileToString("style.json"); diff --git a/src/test/java/org/opentripplanner/framework/lang/ObjectUtilsTest.java b/src/test/java/org/opentripplanner/framework/lang/ObjectUtilsTest.java index 9237b221fe5..274fb3e8700 100644 --- a/src/test/java/org/opentripplanner/framework/lang/ObjectUtilsTest.java +++ b/src/test/java/org/opentripplanner/framework/lang/ObjectUtilsTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.time.Duration; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Test; @@ -48,6 +49,18 @@ void requireNotInitialized() { assertEquals("Field is already set! Old value: old, new value: new.", ex.getMessage()); } + @Test + void safeGetOrNull() { + assertEquals("test", ObjectUtils.safeGetOrNull(() -> "test")); + assertEquals(3000, ObjectUtils.safeGetOrNull(() -> Duration.ofSeconds(3).toMillis())); + assertNull(ObjectUtils.safeGetOrNull(() -> null)); + assertNull( + ObjectUtils.safeGetOrNull(() -> { + throw new NullPointerException("Something went wrong - ignore"); + }) + ); + } + @Test void toStringTest() { assertEquals("1", ObjectUtils.toString(1)); diff --git a/src/test/java/org/opentripplanner/framework/logging/ThrottleTest.java b/src/test/java/org/opentripplanner/framework/logging/ThrottleTest.java index 91f1667486d..c9155992daa 100644 --- a/src/test/java/org/opentripplanner/framework/logging/ThrottleTest.java +++ b/src/test/java/org/opentripplanner/framework/logging/ThrottleTest.java @@ -1,5 +1,6 @@ package org.opentripplanner.framework.logging; +import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -36,8 +37,8 @@ void smokeTest() { @Test @Disabled("Run this test manually") void manualTest() { - double quietPeriodMs = 50.0; - var subject = new Throttle((int) quietPeriodMs); + var quietPeriod = Duration.ofMillis(50); + var subject = new Throttle(quietPeriod); List events = createIntegerSequence(20_000_000); long start = System.currentTimeMillis(); diff --git a/src/test/java/org/opentripplanner/framework/tostring/ToStringBuilderTest.java b/src/test/java/org/opentripplanner/framework/tostring/ToStringBuilderTest.java index 3e87006e953..90254c321a1 100644 --- a/src/test/java/org/opentripplanner/framework/tostring/ToStringBuilderTest.java +++ b/src/test/java/org/opentripplanner/framework/tostring/ToStringBuilderTest.java @@ -111,6 +111,26 @@ public void addObj() { ); } + @Test + public void addObjOpSafe() { + assertEquals( + "ToStringBuilderTest{obj: Foo{a: 5, b: 'X'}}", + subject().addObjOpSafe("obj", () -> new Foo(5, "X")).toString() + ); + assertEquals("ToStringBuilderTest{}", subject().addObjOpSafe("obj", () -> null).toString()); + assertEquals( + "ToStringBuilderTest{}", + subject() + .addObjOpSafe( + "obj", + () -> { + throw new IllegalStateException("Ignore"); + } + ) + .toString() + ); + } + @Test public void addObjOp() { var duration = Duration.ofMinutes(1); diff --git a/src/test/java/org/opentripplanner/generate/doc/BuildConfigurationDocTest.java b/src/test/java/org/opentripplanner/generate/doc/BuildConfigurationDocTest.java index 287b0145292..4009a455abe 100644 --- a/src/test/java/org/opentripplanner/generate/doc/BuildConfigurationDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/BuildConfigurationDocTest.java @@ -49,7 +49,7 @@ public class BuildConfigurationDocTest { public void updateBuildConfigurationDoc() { NodeAdapter node = readBuildConfig(); - // Read and close inout file (same as output file) + // Read and close input file (same as output file) String doc = readFile(TEMPLATE); String original = readFile(OUT_FILE); diff --git a/src/test/java/org/opentripplanner/generate/doc/ConfigurationDocTest.java b/src/test/java/org/opentripplanner/generate/doc/ConfigurationDocTest.java index 2f972e8300a..19a562d8b3f 100644 --- a/src/test/java/org/opentripplanner/generate/doc/ConfigurationDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/ConfigurationDocTest.java @@ -36,7 +36,7 @@ public class ConfigurationDocTest { */ @Test public void updateConfigurationDoc() { - // Read and close inout file (same as output file) + // Read and close input file (same as output file) String doc = readFile(TEMPLATE); String original = readFile(OUT_FILE); diff --git a/src/test/java/org/opentripplanner/generate/doc/FlexConfigurationDocTest.java b/src/test/java/org/opentripplanner/generate/doc/FlexConfigurationDocTest.java index 2b440d7545a..fd2d7092dc5 100644 --- a/src/test/java/org/opentripplanner/generate/doc/FlexConfigurationDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/FlexConfigurationDocTest.java @@ -33,7 +33,7 @@ public class FlexConfigurationDocTest { public void updateFlexDoc() { NodeAdapter node = readFlexConfig(); - // Read and close inout file (same as output file) + // Read and close input file (same as output file) String template = readFile(TEMPLATE); String original = readFile(OUT_FILE); diff --git a/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java b/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java index bf2b092e092..5e858ff4d61 100644 --- a/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java @@ -33,7 +33,7 @@ public class GraphQLTutorialDocTest { */ @Test public void updateTutorialDoc() throws IOException { - // Read and close inout file (same as output file) + // Read and close input file (same as output file) String doc = readFile(TEMPLATE); String original = readFile(OUT_FILE); diff --git a/src/test/java/org/opentripplanner/generate/doc/RouteRequestDocTest.java b/src/test/java/org/opentripplanner/generate/doc/RouteRequestDocTest.java index 9a39b3cfa3f..76642db3e5a 100644 --- a/src/test/java/org/opentripplanner/generate/doc/RouteRequestDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/RouteRequestDocTest.java @@ -44,7 +44,7 @@ public class RouteRequestDocTest { public void updateRouteRequestConfigurationDoc() { NodeAdapter node = readRoutingDefaults(); - // Read and close inout file (same as output file) + // Read and close input file (same as output file) String doc = readFile(TEMPLATE); String original = readFile(OUT_FILE); diff --git a/src/test/java/org/opentripplanner/generate/doc/RouterConfigurationDocTest.java b/src/test/java/org/opentripplanner/generate/doc/RouterConfigurationDocTest.java index d13f423ffc4..90cdd9de975 100644 --- a/src/test/java/org/opentripplanner/generate/doc/RouterConfigurationDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/RouterConfigurationDocTest.java @@ -33,7 +33,7 @@ public class RouterConfigurationDocTest { .skip("flex", "sandbox/Flex.md") .skip("routingDefaults", "RouteRequest.md") .skip("updaters", "UpdaterConfig.md") - .skip("vectorTileLayers", "sandbox/MapboxVectorTilesApi.md") + .skip("vectorTiles", "sandbox/MapboxVectorTilesApi.md") .skipNestedElements("transferCacheRequests", "RouteRequest.md") .skip("rideHailingServices", "sandbox/RideHailing.md") .skip("vehicleRentalServiceDirectory", "sandbox/VehicleRentalServiceDirectory.md") @@ -51,7 +51,7 @@ public class RouterConfigurationDocTest { public void updateBuildConfigurationDoc() { NodeAdapter node = readRouterConfig(); - // Read and close inout file (same as output file) + // Read and close input file (same as output file) String doc = readFile(TEMPLATE); String original = readFile(OUT_FILE); diff --git a/src/test/java/org/opentripplanner/generate/doc/RoutingModeDocTest.java b/src/test/java/org/opentripplanner/generate/doc/RoutingModeDocTest.java index e08de453630..0c6edc7e16b 100644 --- a/src/test/java/org/opentripplanner/generate/doc/RoutingModeDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/RoutingModeDocTest.java @@ -24,7 +24,7 @@ public class RoutingModeDocTest { @Test public void updateDocs() { - // Read and close inout file (same as output file) + // Read and close input file (same as output file) String doc = readFile(TEMPLATE); String original = readFile(OUT_FILE); diff --git a/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java b/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java index 3d0cb547b7a..fa5abca7814 100644 --- a/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java @@ -50,7 +50,7 @@ public class UpdaterConfigDocTest { public void updateRouterConfigurationDoc() { NodeAdapter node = readBuildConfig(); - // Read and close inout file (same as output file) + // Read and close input file (same as output file) String template = readFile(TEMPLATE); String original = readFile(OUT_FILE); diff --git a/src/test/java/org/opentripplanner/generate/doc/VehicleParkingDocTest.java b/src/test/java/org/opentripplanner/generate/doc/VehicleParkingDocTest.java index de9b921c27a..abc9ceee806 100644 --- a/src/test/java/org/opentripplanner/generate/doc/VehicleParkingDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/VehicleParkingDocTest.java @@ -33,7 +33,7 @@ public class VehicleParkingDocTest { public void updateVehicleParkingDoc() { NodeAdapter node = readVehicleUpdaters(); - // Read and close inout file (same as output file) + // Read and close input file (same as output file) String template = readFile(TEMPLATE); String original = readFile(OUT_FILE); diff --git a/src/test/java/org/opentripplanner/gtfs/mapping/StopTimeMapperTest.java b/src/test/java/org/opentripplanner/gtfs/mapping/StopTimeMapperTest.java index 6e082d9e98d..94c203c76ae 100644 --- a/src/test/java/org/opentripplanner/gtfs/mapping/StopTimeMapperTest.java +++ b/src/test/java/org/opentripplanner/gtfs/mapping/StopTimeMapperTest.java @@ -228,6 +228,6 @@ public void testFlexLocationGroup() { assertInstanceOf(GroupStop.class, mapped.getStop()); var groupStop = (GroupStop) mapped.getStop(); - assertEquals("[RegularStop{A:1 Stop}]", groupStop.getLocations().toString()); + assertEquals("[RegularStop{A:1 Stop}]", groupStop.getChildLocations().toString()); } } diff --git a/src/test/java/org/opentripplanner/netex/mapping/FlexStopsMapperTest.java b/src/test/java/org/opentripplanner/netex/mapping/FlexStopsMapperTest.java index b59f3c1b58a..7529ae52a34 100644 --- a/src/test/java/org/opentripplanner/netex/mapping/FlexStopsMapperTest.java +++ b/src/test/java/org/opentripplanner/netex/mapping/FlexStopsMapperTest.java @@ -9,7 +9,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.Set; import net.opengis.gml._3.AbstractRingPropertyType; import net.opengis.gml._3.DirectPositionListType; import net.opengis.gml._3.LinearRingType; @@ -258,7 +257,7 @@ private void assertGroupStopMapping(FlexibleStopPlace flexibleStopPlace) { assertNotNull(groupStop); // Only one of the stops should be inside the polygon - Set locations = groupStop.getLocations(); + List locations = groupStop.getChildLocations(); assertEquals(1, locations.size()); assertEquals(stop1.getId(), locations.stream().findFirst().orElseThrow().getId()); } diff --git a/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/BikeRentalSnapshotTest.snap b/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/BikeRentalSnapshotTest.snap index d99f479ac16..41bc4278ca1 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/BikeRentalSnapshotTest.snap +++ b/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/BikeRentalSnapshotTest.snap @@ -22,31 +22,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "17" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -57,7 +33,29 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 3 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2209, "legs" : [ @@ -510,31 +508,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "20" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -545,7 +519,29 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2539, "legs" : [ @@ -956,31 +952,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "77" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -991,7 +963,29 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 1805, "legs" : [ @@ -1298,31 +1292,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "17" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -1333,7 +1303,29 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 3 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2209, "legs" : [ @@ -1786,31 +1778,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "17" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -1821,7 +1789,29 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 3 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2209, "legs" : [ @@ -2274,31 +2264,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "77" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -2309,7 +2275,29 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.accessBikeR "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 1805, "legs" : [ @@ -3291,31 +3279,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "17" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -3326,7 +3290,29 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2119, "legs" : [ @@ -3831,31 +3817,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "20" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -3866,7 +3828,29 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2503, "legs" : [ @@ -4251,31 +4235,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "77" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -4286,7 +4246,29 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2351, "legs" : [ @@ -4645,31 +4627,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "17" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -4680,7 +4638,29 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2119, "legs" : [ @@ -5185,31 +5165,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "20" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -5220,7 +5176,29 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2563, "legs" : [ @@ -5605,31 +5583,7 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "77" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -5640,7 +5594,29 @@ org.opentripplanner.routing.algorithm.mapping.BikeRentalSnapshotTest.egressBikeR "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2351, "legs" : [ diff --git a/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/ElevationSnapshotTest.snap b/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/ElevationSnapshotTest.snap index b5e40b383e4..6579e4bbd37 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/ElevationSnapshotTest.snap +++ b/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/ElevationSnapshotTest.snap @@ -550,31 +550,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "17" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -585,7 +561,29 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2305, "legs" : [ @@ -946,31 +944,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "20" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -981,7 +955,29 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2465, "legs" : [ @@ -1368,31 +1364,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "77" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -1403,7 +1375,29 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2396, "legs" : [ @@ -1764,31 +1758,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "15" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -1799,7 +1769,29 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2787, "legs" : [ @@ -2238,31 +2230,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "17" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -2273,7 +2241,29 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2305, "legs" : [ @@ -2634,31 +2624,7 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "20" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -2669,7 +2635,29 @@ org.opentripplanner.routing.algorithm.mapping.ElevationSnapshotTest.transit=[ "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2525, "legs" : [ diff --git a/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/TransitSnapshotTest.snap b/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/TransitSnapshotTest.snap index f67c75f44ec..d5da342f9a6 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/TransitSnapshotTest.snap +++ b/src/test/java/org/opentripplanner/routing/algorithm/mapping/__snapshots__/TransitSnapshotTest.snap @@ -250,31 +250,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "20" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -285,7 +261,29 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 4281, "legs" : [ @@ -735,31 +733,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "15" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -770,7 +744,29 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2315, "legs" : [ @@ -1285,31 +1281,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "20" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -1320,7 +1292,29 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 4295, "legs" : [ @@ -1770,31 +1764,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "15" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -1805,7 +1775,29 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2385, "legs" : [ @@ -2320,35 +2312,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "70" - }, - { - "feedId" : "prt", - "id" : "77" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -2359,7 +2323,49 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + }, + { + "legIndices" : [ + 2 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 3375, "legs" : [ @@ -3053,31 +3059,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "20" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -3088,7 +3070,29 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2895, "legs" : [ @@ -3541,31 +3545,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "20" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -3576,7 +3556,29 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2925, "legs" : [ @@ -4029,31 +4031,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "20" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -4064,7 +4042,29 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2895, "legs" : [ @@ -4517,35 +4517,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "70" - }, - { - "feedId" : "prt", - "id" : "77" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -4556,7 +4528,49 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + }, + { + "legIndices" : [ + 2 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 2957, "legs" : [ @@ -5029,35 +5043,7 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "name" : "regular" } ], - "details" : { - "regular" : [ - { - "fareId" : { - "feedId" : "prt", - "id" : "8" - }, - "price" : { - "cents" : 200, - "currency" : { - "currency" : "USD", - "currencyCode" : "USD", - "defaultFractionDigits" : 2, - "symbol" : "$" - } - }, - "routes" : [ - { - "feedId" : "prt", - "id" : "20" - }, - { - "feedId" : "prt", - "id" : "15" - } - ] - } - ] - }, + "details" : { }, "fare" : { "regular" : { "cents" : 200, @@ -5068,7 +5054,49 @@ org.opentripplanner.routing.algorithm.mapping.TransitSnapshotTest.test_trip_plan "symbol" : "$" } } - } + }, + "legProducts" : [ + { + "legIndices" : [ + 1 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + }, + { + "legIndices" : [ + 2 + ], + "products" : [ + { + "amount" : { + "cents" : 200, + "currency" : { + "currency" : "USD", + "currencyCode" : "USD", + "defaultFractionDigits" : 2, + "symbol" : "$" + } + }, + "id" : "prt:8", + "name" : "regular" + } + ] + } + ] }, "generalizedCost" : 3015, "legs" : [ diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapperTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapperTest.java index 11c59030180..89348be5c89 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapperTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/RaptorRequestMapperTest.java @@ -1,15 +1,33 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.time.Duration; +import java.time.ZonedDateTime; import java.util.List; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.raptor._data.transit.TestAccessEgress; +import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptor.api.model.RaptorAccessEgress; +import org.opentripplanner.raptor.api.request.RaptorRequest; +import org.opentripplanner.routing.api.request.PassThroughPoint; +import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.framework.CostLinearFunction; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.site.StopLocation; class RaptorRequestMapperTest { + private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + private static final StopLocation STOP_A = TEST_MODEL.stop("Stop:A").build(); + private static final List ACCESS = List.of(TestAccessEgress.walk(12, 45)); + private static final List EGRESS = List.of(TestAccessEgress.walk(144, 54)); + private static final Duration D0s = Duration.ofSeconds(0); + private static final CostLinearFunction R1 = CostLinearFunction.of("50 + 1.0x"); private static final CostLinearFunction R2 = CostLinearFunction.of("0 + 1.5x"); private static final CostLinearFunction R3 = CostLinearFunction.of("30 + 2.0x"); @@ -33,4 +51,47 @@ void mapRelaxCost(CostLinearFunction input, int cost, int expected) { var calcCost = RaptorRequestMapper.mapRelaxCost(input); assertEquals(expected, calcCost.relax(cost)); } + + @Test + void testPassThroughPoints() { + var req = new RouteRequest(); + + req.setPassThroughPoints(List.of(new PassThroughPoint(List.of(STOP_A), "Via A"))); + + var result = map(req); + + assertTrue(result.multiCriteria().hasPassThroughPoints()); + assertEquals( + "[(Via A, stops: " + STOP_A.getIndex() + ")]", + result.multiCriteria().passThroughPoints().toString() + ); + } + + @Test + void testPassThroughPointsTurnTransitGroupPriorityOff() { + var req = new RouteRequest(); + + // Set pass-through and relax transit-group-priority + req.setPassThroughPoints(List.of(new PassThroughPoint(List.of(STOP_A), "Via A"))); + req.withPreferences(p -> + p.withTransit(t -> t.withRelaxTransitGroupPriority(CostLinearFunction.of("30m + 1.2t"))) + ); + + var result = map(req); + + // transit-group-priority CANNOT be used with pass-through and is turned off... + assertTrue(result.multiCriteria().transitPriorityCalculator().isEmpty()); + } + + private static RaptorRequest map(RouteRequest request) { + return RaptorRequestMapper.mapRequest( + request, + ZonedDateTime.now(), + false, + ACCESS, + EGRESS, + D0s, + null + ); + } } diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfiguratorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfiguratorTest.java index cc4bb09f01e..7f974927c1b 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfiguratorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/PriorityGroupConfiguratorTest.java @@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test; import org.opentripplanner.routing.api.request.request.filter.TransitGroupSelect; import org.opentripplanner.transit.model.basic.TransitMode; -import org.opentripplanner.transit.model.network.RoutingTripPattern; +import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.RegularStop; class PriorityGroupConfiguratorTest { @@ -60,11 +60,11 @@ class PriorityGroupConfiguratorTest { "10:00 10:10" ); - private final RoutingTripPattern railR1 = routeR1.getTripPattern().getRoutingTripPattern(); - private final RoutingTripPattern busB2 = routeB2.getTripPattern().getRoutingTripPattern(); - private final RoutingTripPattern railR3 = routeR3.getTripPattern().getRoutingTripPattern(); - private final RoutingTripPattern ferryF3 = routeF3.getTripPattern().getRoutingTripPattern(); - private final RoutingTripPattern busB3 = routeB3.getTripPattern().getRoutingTripPattern(); + private final TripPattern railR1 = routeR1.getTripPattern(); + private final TripPattern busB2 = routeB2.getTripPattern(); + private final TripPattern railR3 = routeR3.getTripPattern(); + private final TripPattern ferryF3 = routeF3.getTripPattern(); + private final TripPattern busB3 = routeB3.getTripPattern(); @Test void emptyConfigurationShouldReturnGroupZero() { diff --git a/src/test/java/org/opentripplanner/standalone/server/EtagRequestFilterTest.java b/src/test/java/org/opentripplanner/standalone/server/EtagRequestFilterTest.java index 1451a218852..5adf8264d8e 100644 --- a/src/test/java/org/opentripplanner/standalone/server/EtagRequestFilterTest.java +++ b/src/test/java/org/opentripplanner/standalone/server/EtagRequestFilterTest.java @@ -8,7 +8,6 @@ import java.nio.charset.StandardCharsets; import java.util.stream.Stream; import javax.annotation.Nonnull; -import org.glassfish.jersey.internal.MapPropertiesDelegate; import org.glassfish.jersey.message.internal.OutboundJaxrsResponse; import org.glassfish.jersey.message.internal.OutboundMessageContext; import org.glassfish.jersey.message.internal.Statuses; @@ -17,6 +16,7 @@ import org.jets3t.service.utils.Mimetypes; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; +import org.opentripplanner.test.support.HttpForTest; import org.opentripplanner.test.support.VariableSource; class EtagRequestFilterTest { @@ -44,7 +44,7 @@ void writeEtag( byte[] entity, String expectedEtag ) throws IOException { - var request = request(method); + var request = HttpForTest.containerRequest(method); var response = response(status, request); var headers = response.getHeaders(); headers.add(EtagRequestFilter.HEADER_CONTENT_TYPE, responseContentType); @@ -65,7 +65,7 @@ void writeEtag( @VariableSource("ifNoneMatchCases") void ifNoneMatch(String ifNoneMatch, int expectedStatus, byte[] expectedEntity) throws IOException { - var request = request("GET"); + var request = HttpForTest.containerRequest("GET"); request.header(EtagRequestFilter.HEADER_IF_NONE_MATCH, ifNoneMatch); var response = response(200, request); var headers = response.getHeaders(); @@ -92,9 +92,4 @@ private static ContainerResponse response(int status, ContainerRequest request) private static byte[] bytes(String input) { return input.getBytes(StandardCharsets.UTF_8); } - - @Nonnull - private static ContainerRequest request(String method) { - return new ContainerRequest(null, null, method, null, new MapPropertiesDelegate(), null); - } } diff --git a/src/test/java/org/opentripplanner/test/support/HttpForTest.java b/src/test/java/org/opentripplanner/test/support/HttpForTest.java new file mode 100644 index 00000000000..7bbe272572d --- /dev/null +++ b/src/test/java/org/opentripplanner/test/support/HttpForTest.java @@ -0,0 +1,22 @@ +package org.opentripplanner.test.support; + +import java.net.URI; +import java.net.URISyntaxException; +import org.glassfish.jersey.internal.MapPropertiesDelegate; +import org.glassfish.jersey.server.ContainerRequest; + +public class HttpForTest { + + public static ContainerRequest containerRequest(String method) { + try { + URI uri = new URI("https://localhost:8080"); + return new ContainerRequest(uri, uri, method, null, new MapPropertiesDelegate(), null); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + public static ContainerRequest containerRequest() { + return containerRequest("GET"); + } +} diff --git a/src/test/java/org/opentripplanner/transit/model/site/GroupStopTest.java b/src/test/java/org/opentripplanner/transit/model/site/GroupStopTest.java index fa073404e39..17938131eef 100644 --- a/src/test/java/org/opentripplanner/transit/model/site/GroupStopTest.java +++ b/src/test/java/org/opentripplanner/transit/model/site/GroupStopTest.java @@ -44,7 +44,7 @@ void copy() { assertEquals(subject, copy); assertEquals(ID, copy.getId().getId()); - assertEquals(STOP_LOCATION, copy.getLocations().iterator().next()); + assertEquals(STOP_LOCATION, copy.getChildLocations().iterator().next()); assertEquals("v2", copy.getName().toString()); } diff --git a/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java b/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java index 642e192539c..7a11c35bace 100644 --- a/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java +++ b/src/test/java/org/opentripplanner/transit/speed_test/SpeedTest.java @@ -29,6 +29,7 @@ import org.opentripplanner.standalone.config.BuildConfig; import org.opentripplanner.standalone.config.ConfigModel; import org.opentripplanner.standalone.config.OtpConfigLoader; +import org.opentripplanner.standalone.config.routerconfig.VectorTileConfig; import org.opentripplanner.standalone.server.DefaultServerRequestContext; import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.TransitModel; @@ -111,7 +112,7 @@ public SpeedTest( graph, new DefaultTransitService(transitModel), timer.getRegistry(), - List::of, + VectorTileConfig.DEFAULT, TestServerContext.createWorldEnvelopeService(), TestServerContext.createRealtimeVehicleService(transitService), TestServerContext.createVehicleRentalService(), diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-fares.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-fares.json index cd6ed7970a5..9ee3d4706b3 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-fares.json +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-fares.json @@ -68,23 +68,6 @@ } } }, - { - "id" : "6bd27c98-c0ee-3f62-b606-bbacf2e1f41b", - "product" : { - "id" : "F:AB", - "name" : "regular", - "__typename" : "DefaultFareProduct", - "price" : { - "currency" : { - "digits" : 2, - "code" : "EUR" - }, - "amount" : 3.1 - }, - "riderCategory" : null, - "medium" : null - } - }, { "id" : "09bb5f2b-6af9-3355-8b5d-5e93a27ce280", "product" : { @@ -205,18 +188,7 @@ "type" : "regular", "cents" : 310, "currency" : "EUR", - "components" : [ - { - "currency" : "EUR", - "cents" : 310, - "fareId" : "F:AB", - "routes" : [ - { - "gtfsId" : "F:BUS" - } - ] - } - ] + "components" : [] } ] } diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index 9555f32c1e5..e62c3968924 100644 --- a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -141,6 +141,19 @@ "visibility" : "none" } }, + { + "id" : "area-stop", + "type" : "fill", + "source" : "vectorSource", + "source-layer" : "stops", + "minzoom" : 6, + "maxzoom" : 23, + "paint" : { + "fill-color" : "#22DD9E", + "fill-opacity" : 0.5, + "fill-outline-color" : "#140d0e" + } + }, { "id" : "regular-stop", "type" : "circle", diff --git a/src/test/resources/standalone/config/router-config.json b/src/test/resources/standalone/config/router-config.json index 863b9bec279..9293cfad8f4 100644 --- a/src/test/resources/standalone/config/router-config.json +++ b/src/test/resources/standalone/config/router-config.json @@ -80,11 +80,14 @@ "accessEgress": { "maxDuration": "45m", "maxDurationForMode": { - "BIKE_RENTAL": "20m" + "BIKE_RENTAL": "20m" }, "maxStopCount": 500, "penalty": { - "FLEXIBLE" : { "timePenalty": "2m + 1.1t", "costFactor": 1.7 } + "FLEXIBLE": { + "timePenalty": "2m + 1.1t", + "costFactor": 1.7 + } } }, "itineraryFilters": { @@ -103,8 +106,12 @@ "geoidElevation": false, "maxJourneyDuration": "36h", "unpreferred": { - "agencies": ["HSL:123"], - "routes": ["HSL:456"] + "agencies": [ + "HSL:123" + ], + "routes": [ + "HSL:456" + ] }, "unpreferredCost": "10m + 2.0 x", "streetRoutingTimeout": "5s", @@ -158,8 +165,15 @@ "PREFERRED": 0 }, "transferCacheRequests": [ - { "modes": "WALK" }, - { "modes": "WALK", "wheelchairAccessibility": { "enabled": true } } + { + "modes": "WALK" + }, + { + "modes": "WALK", + "wheelchairAccessibility": { + "enabled": true + } + } ] }, "vehicleRentalServiceDirectory": { @@ -174,61 +188,64 @@ "transmodelApi": { "hideFeedId": true }, - "vectorTileLayers": [ - { - "name": "stops", - "type": "Stop", - "mapper": "Digitransit", - "maxZoom": 20, - "minZoom": 14, - "cacheMaxSeconds": 600 - }, - { - "name": "stations", - "type": "Station", - "mapper": "Digitransit", - "maxZoom": 20, - "minZoom": 12, - "cacheMaxSeconds": 600 - }, - { - "name": "rentalPlaces", - // all rental places: stations and free-floating vehicles - "type": "VehicleRental", - "mapper": "Digitransit", - "maxZoom": 20, - "minZoom": 14, - "cacheMaxSeconds": 60, - "expansionFactor": 0.25 - }, - { - "name": "rentalVehicle", - // just free-floating vehicles - "type": "VehicleRentalVehicle", - "mapper": "Digitransit", - "maxZoom": 20, - "minZoom": 14, - "cacheMaxSeconds": 60 - }, - { - "name": "rentalStation", - // just rental stations - "type": "VehicleRentalStation", - "mapper": "Digitransit", - "maxZoom": 20, - "minZoom": 14, - "cacheMaxSeconds": 600 - }, - { - "name": "vehicleParking", - "type": "VehicleParking", - "mapper": "Digitransit", - "maxZoom": 20, - "minZoom": 14, - "cacheMaxSeconds": 60, - "expansionFactor": 0.25 - } - ], + "vectorTiles": { + "basePath": "/otp_ct/vectorTiles", + "layers": [ + { + "name": "stops", + "type": "Stop", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 600 + }, + { + "name": "stations", + "type": "Station", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 12, + "cacheMaxSeconds": 600 + }, + { + "name": "rentalPlaces", + // all rental places: stations and free-floating vehicles + "type": "VehicleRental", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 60, + "expansionFactor": 0.25 + }, + { + "name": "rentalVehicle", + // just free-floating vehicles + "type": "VehicleRentalVehicle", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 60 + }, + { + "name": "rentalStation", + // just rental stations + "type": "VehicleRentalStation", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 600 + }, + { + "name": "vehicleParking", + "type": "VehicleParking", + "mapper": "Digitransit", + "maxZoom": 20, + "minZoom": 14, + "cacheMaxSeconds": 60, + "expansionFactor": 0.25 + } + ] + }, "timetableUpdates": { "purgeExpiredData": false, "maxSnapshotFrequency": "2s" @@ -324,7 +341,9 @@ "Header-Name": "Header-Value" }, "fuzzyTripMatching": false, - "features": ["position"] + "features": [ + "position" + ] }, // Siri-ET over HTTP { @@ -368,8 +387,10 @@ "clientSecret": "very-secret", "wheelchairAccessibleProductId": "545de0c4-659f-49c6-be65-0d5e448dffd5", "bannedProductIds": [ - "1196d0dd-423b-4a81-a1d8-615367d3a365", "f58761e5-8dd5-4940-a472-872f1236c596" + "1196d0dd-423b-4a81-a1d8-615367d3a365", + "f58761e5-8dd5-4940-a472-872f1236c596" ] } ] } +