diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml index b69d998eca7..37e37a84945 100644 --- a/.github/workflows/cibuild.yml +++ b/.github/workflows/cibuild.yml @@ -121,7 +121,7 @@ jobs: - name: Build GTFS GraphQL API documentation run: | - npm install -g @magidoc/cli@6.0.0 + npm install -g @magidoc/cli@6.1.0 magidoc generate --stacktrace - name: Deploy compiled HTML to Github pages diff --git a/DEVELOPMENT_DECISION_RECORDS.md b/DEVELOPMENT_DECISION_RECORDS.md index 10b9e005809..44aa031954f 100644 --- a/DEVELOPMENT_DECISION_RECORDS.md +++ b/DEVELOPMENT_DECISION_RECORDS.md @@ -16,7 +16,7 @@ tests. Expect to include some code cleanup as part of all PRs. ## Follow-Naming-Conventions Use established terminology from GTFS, NeTEx or the existing OTP code. Make sure the code is easy -to read and understand. [Follow naming conventions](CODE_CONVENTIONS.md#naming-conventions) . +to read and understand. [Follow naming conventions](doc/dev/decisionrecords/NamingConventions.md#naming-conventions) . ## Write-Code-Documentation - Use JavaDoc @@ -32,7 +32,7 @@ notes on `private` members and as inline comments. **See also** - [Developers-Guide > Code comments](doc/user/Developers-Guide.md#code-comments). - - [Codestyle > Javadoc Guidlines](doc/dev/decisionrecords/Codestyle.md#javadoc-guidlines) - JavaDoc checklist + - [Codestyle > Javadoc Guidelines](doc/dev/decisionrecords/Codestyle.md#javadoc-guidlines) - JavaDoc checklist ## Document-Config-and-APIs diff --git a/client/.env.development b/client/.env.development index 1cb7d9235e3..e3b3585a5eb 100644 --- a/client/.env.development +++ b/client/.env.development @@ -1,3 +1,3 @@ VITE_API_URL=http://localhost:8080/otp/transmodel/v3 VITE_DEBUG_STYLE_URL=http://localhost:8080/otp/routers/default/inspector/vectortile/style.json -VITE_GRAPHIQL_URL=http://localhost:8080/graphiql?flavor=transmodel \ No newline at end of file +VITE_GRAPHIQL_URL=http://localhost:8080/graphiql?flavor=transmodel diff --git a/client/package-lock.json b/client/package-lock.json index 5619317e0bd..00490ce16d6 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "@googlemaps/polyline-codec": "1.0.28", + "@js-temporal/polyfill": "0.4.4", "bootstrap": "5.3.3", "graphql": "16.9.0", "graphql-request": "7.1.0", @@ -2958,6 +2959,18 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@js-temporal/polyfill": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@js-temporal/polyfill/-/polyfill-0.4.4.tgz", + "integrity": "sha512-2X6bvghJ/JAoZO52lbgyAPFj8uCflhTo2g7nkFzEQdXd/D8rEeD4HtmTEpmtGCva260fcd66YNXBOYdnmHqSOg==", + "dependencies": { + "jsbi": "^4.3.0", + "tslib": "^2.4.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@kamilkisiela/fast-url-parser": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@kamilkisiela/fast-url-parser/-/fast-url-parser-1.1.4.tgz", @@ -8269,6 +8282,11 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.3.0.tgz", + "integrity": "sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==" + }, "node_modules/jsdom": { "version": "25.0.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.0.tgz", diff --git a/client/package.json b/client/package.json index 2cd0d1f8937..0e822b4a641 100644 --- a/client/package.json +++ b/client/package.json @@ -18,6 +18,7 @@ }, "dependencies": { "@googlemaps/polyline-codec": "1.0.28", + "@js-temporal/polyfill": "0.4.4", "bootstrap": "5.3.3", "graphql": "16.9.0", "graphql-request": "7.1.0", diff --git a/client/src/components/ItineraryList/ItineraryHeaderContent.tsx b/client/src/components/ItineraryList/ItineraryHeaderContent.tsx index cf3ca227b63..fdfea81e7e4 100644 --- a/client/src/components/ItineraryList/ItineraryHeaderContent.tsx +++ b/client/src/components/ItineraryList/ItineraryHeaderContent.tsx @@ -1,8 +1,9 @@ import { TripPattern } from '../../gql/graphql.ts'; import { TIME_BOX_WIDTH, useHeaderContentStyleCalculations } from './useHeaderContentStyleCalculations.ts'; import { ItineraryHeaderLegContent } from './ItineraryHeaderLegContent.tsx'; -import { useMemo } from 'react'; +import { useContext, useMemo } from 'react'; import { formatTime } from '../../util/formatTime.ts'; +import { TimeZoneContext } from '../../hooks/TimeZoneContext.ts'; export function ItineraryHeaderContent({ tripPattern, @@ -24,14 +25,16 @@ export function ItineraryHeaderContent({ latestEndTime, ); + const timeZone = useContext(TimeZoneContext); + const formattedStartTime = useMemo( - () => formatTime(tripPattern.expectedStartTime, 'short'), - [tripPattern.expectedStartTime], + () => formatTime(tripPattern.expectedStartTime, timeZone, 'short'), + [tripPattern.expectedStartTime, timeZone], ); const formattedEndTime = useMemo( - () => formatTime(tripPattern.expectedEndTime, 'short'), - [tripPattern.expectedEndTime], + () => formatTime(tripPattern.expectedEndTime, timeZone, 'short'), + [tripPattern.expectedEndTime, timeZone], ); return ( diff --git a/client/src/components/ItineraryList/ItineraryListContainer.tsx b/client/src/components/ItineraryList/ItineraryListContainer.tsx index feaf29aa514..b474d2eb5ec 100644 --- a/client/src/components/ItineraryList/ItineraryListContainer.tsx +++ b/client/src/components/ItineraryList/ItineraryListContainer.tsx @@ -5,6 +5,8 @@ import { ItineraryHeaderContent } from './ItineraryHeaderContent.tsx'; import { useEarliestAndLatestTimes } from './useEarliestAndLatestTimes.ts'; import { ItineraryDetails } from './ItineraryDetails.tsx'; import { ItineraryPaginationControl } from './ItineraryPaginationControl.tsx'; +import { useContext } from 'react'; +import { TimeZoneContext } from '../../hooks/TimeZoneContext.ts'; export function ItineraryListContainer({ tripQueryResult, @@ -21,6 +23,7 @@ export function ItineraryListContainer({ }) { const [earliestStartTime, latestEndTime] = useEarliestAndLatestTimes(tripQueryResult); const { containerRef, containerWidth } = useContainerWidth(); + const timeZone = useContext(TimeZoneContext); return (
@@ -56,6 +59,9 @@ export function ItineraryListContainer({ ))} +
+ All times in {timeZone} +
); } diff --git a/client/src/components/ItineraryList/ItineraryPaginationControl.tsx b/client/src/components/ItineraryList/ItineraryPaginationControl.tsx index ecc1ffd45db..bf74c83fbca 100644 --- a/client/src/components/ItineraryList/ItineraryPaginationControl.tsx +++ b/client/src/components/ItineraryList/ItineraryPaginationControl.tsx @@ -14,6 +14,8 @@ export function ItineraryPaginationControl({ return (
{' '} -
+ ); } + export default GraphiQLRouteButton; diff --git a/client/src/components/SearchBar/ItineraryFilterDebugSelect.tsx b/client/src/components/SearchBar/ItineraryFilterDebugSelect.tsx index 636ba551541..6f479290947 100644 --- a/client/src/components/SearchBar/ItineraryFilterDebugSelect.tsx +++ b/client/src/components/SearchBar/ItineraryFilterDebugSelect.tsx @@ -11,11 +11,12 @@ export function ItineraryFilterDebugSelect({ return ( - Itinerary filter debug + Filter debug { setTripQueryVariables({ ...tripQueryVariables, diff --git a/client/src/components/SearchBar/LocationInputField.tsx b/client/src/components/SearchBar/LocationInputField.tsx index ffa66702e81..bfa707776f1 100644 --- a/client/src/components/SearchBar/LocationInputField.tsx +++ b/client/src/components/SearchBar/LocationInputField.tsx @@ -13,6 +13,7 @@ export function LocationInputField({ location, id, label }: { location: Location id={id} size="sm" placeholder="[Click in map]" + className="input-medium" // Intentionally empty for now, but needed because of // https://react.dev/reference/react-dom/components/input#controlling-an-input-with-a-state-variable onChange={() => {}} diff --git a/client/src/components/SearchBar/NumTripPatternsInput.tsx b/client/src/components/SearchBar/NumTripPatternsInput.tsx index b77e70adb81..360ce1c2c73 100644 --- a/client/src/components/SearchBar/NumTripPatternsInput.tsx +++ b/client/src/components/SearchBar/NumTripPatternsInput.tsx @@ -11,7 +11,7 @@ export function NumTripPatternsInput({ return ( - Number of trip patterns + Num. results setTripQueryVariables({ diff --git a/client/src/components/SearchBar/SearchBar.tsx b/client/src/components/SearchBar/SearchBar.tsx index dfcbc6ac36e..7b1ee58b902 100644 --- a/client/src/components/SearchBar/SearchBar.tsx +++ b/client/src/components/SearchBar/SearchBar.tsx @@ -1,9 +1,8 @@ -import { Button, Spinner } from 'react-bootstrap'; +import { Button, ButtonGroup, Spinner } from 'react-bootstrap'; import { ServerInfo, TripQueryVariables } from '../../gql/graphql.ts'; import { LocationInputField } from './LocationInputField.tsx'; import { DepartureArrivalSelect } from './DepartureArrivalSelect.tsx'; -import { TimeInputField } from './TimeInputField.tsx'; -import { DateInputField } from './DateInputField.tsx'; +import { DateTimeInputField } from './DateTimeInputField.tsx'; import { SearchWindowInput } from './SearchWindowInput.tsx'; import { AccessSelect } from './AccessSelect.tsx'; import { EgressSelect } from './EgressSelect.tsx'; @@ -16,6 +15,7 @@ import { ServerInfoTooltip } from './ServerInfoTooltip.tsx'; import { useRef, useState } from 'react'; import logo from '../../static/img/otp-logo.svg'; import GraphiQLRouteButton from './GraphiQLRouteButton.tsx'; +import WheelchairAccessibleCheckBox from './WheelchairAccessibleCheckBox.tsx'; type SearchBarProps = { onRoute: () => void; @@ -40,8 +40,7 @@ export function SearchBar({ onRoute, tripQueryVariables, setTripQueryVariables, - - + @@ -52,17 +51,24 @@ export function SearchBar({ onRoute, tripQueryVariables, setTripQueryVariables, tripQueryVariables={tripQueryVariables} setTripQueryVariables={setTripQueryVariables} /> + +
- + + + +
- ); } diff --git a/client/src/components/SearchBar/SearchWindowInput.tsx b/client/src/components/SearchBar/SearchWindowInput.tsx index 5442784de8e..a04a08bed04 100644 --- a/client/src/components/SearchBar/SearchWindowInput.tsx +++ b/client/src/components/SearchBar/SearchWindowInput.tsx @@ -19,6 +19,7 @@ export function SearchWindowInput({ size="sm" placeholder="(in minutes)" min={1} + className="input-small" value={tripQueryVariables.searchWindow || ''} onChange={(event) => setTripQueryVariables({ diff --git a/client/src/components/SearchBar/TimeInputField.tsx b/client/src/components/SearchBar/TimeInputField.tsx deleted file mode 100644 index 71bb7325340..00000000000 --- a/client/src/components/SearchBar/TimeInputField.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Form } from 'react-bootstrap'; -import { TripQueryVariables } from '../../gql/graphql.ts'; -import { ChangeEvent, useCallback, useMemo } from 'react'; - -export function TimeInputField({ - tripQueryVariables, - setTripQueryVariables, -}: { - tripQueryVariables: TripQueryVariables; - setTripQueryVariables: (tripQueryVariables: TripQueryVariables) => void; -}) { - const current = useMemo( - () => new Date(tripQueryVariables.dateTime).toTimeString().split(' ')[0], - [tripQueryVariables.dateTime], - ); - - const onChange = useCallback( - (event: ChangeEvent) => { - const timeComponents = event.target.value.split(':'); - const newDate = new Date(tripQueryVariables.dateTime); - newDate.setHours(Number(timeComponents[0]), Number(timeComponents[1]), Number(timeComponents[2])); - - setTripQueryVariables({ - ...tripQueryVariables, - dateTime: newDate.toISOString(), - }); - }, - [tripQueryVariables, setTripQueryVariables], - ); - - return ( - - - Time - - - - ); -} diff --git a/client/src/components/SearchBar/WheelchairAccessibleCheckBox.tsx b/client/src/components/SearchBar/WheelchairAccessibleCheckBox.tsx new file mode 100644 index 00000000000..b677e19049f --- /dev/null +++ b/client/src/components/SearchBar/WheelchairAccessibleCheckBox.tsx @@ -0,0 +1,35 @@ +import { Form } from 'react-bootstrap'; +import wheelchairIcon from '../../static/img/wheelchair.svg'; +import { TripQueryVariables } from '../../gql/graphql.ts'; + +export default function WheelchairAccessibleCheckBox({ + tripQueryVariables, + setTripQueryVariables, +}: { + tripQueryVariables: TripQueryVariables; + setTripQueryVariables: (tripQueryVariables: TripQueryVariables) => void; +}) { + return ( + + + Wheelchair Accessible Trip + + { + setTripQueryVariables({ + ...tripQueryVariables, + wheelchairAccessible: e.target.checked, + }); + }} + > + + ); +} diff --git a/client/src/hooks/TimeZoneContext.ts b/client/src/hooks/TimeZoneContext.ts new file mode 100644 index 00000000000..6a40921ebae --- /dev/null +++ b/client/src/hooks/TimeZoneContext.ts @@ -0,0 +1,3 @@ +import { createContext } from 'react'; + +export const TimeZoneContext = createContext('UTC'); diff --git a/client/src/hooks/useServerInfo.ts b/client/src/hooks/useServerInfo.ts index 23ee23fc283..117c1357360 100644 --- a/client/src/hooks/useServerInfo.ts +++ b/client/src/hooks/useServerInfo.ts @@ -13,6 +13,7 @@ const query = graphql(` routerConfigVersion gitCommit gitBranch + internalTransitModelTimeZone } } `); diff --git a/client/src/screens/App.tsx b/client/src/screens/App.tsx index 3e5744e5ad6..1b6b86b7a81 100644 --- a/client/src/screens/App.tsx +++ b/client/src/screens/App.tsx @@ -6,39 +6,43 @@ import { useState } from 'react'; import { useTripQuery } from '../hooks/useTripQuery.ts'; import { useServerInfo } from '../hooks/useServerInfo.ts'; import { useTripQueryVariables } from '../hooks/useTripQueryVariables.ts'; +import { TimeZoneContext } from '../hooks/TimeZoneContext.ts'; export function App() { + const serverInfo = useServerInfo(); const { tripQueryVariables, setTripQueryVariables } = useTripQueryVariables(); const [tripQueryResult, loading, callback] = useTripQuery(tripQueryVariables); - const serverInfo = useServerInfo(); const [selectedTripPatternIndex, setSelectedTripPatternIndex] = useState(0); + const timeZone = serverInfo?.internalTransitModelTimeZone || Intl.DateTimeFormat().resolvedOptions().timeZone; return (
- - - - - + - + + + + + +
); } diff --git a/client/src/static/img/graphql-solid.svg b/client/src/static/img/graphql-solid.svg new file mode 100644 index 00000000000..32d6e5e0f00 --- /dev/null +++ b/client/src/static/img/graphql-solid.svg @@ -0,0 +1,4 @@ + + + + diff --git a/client/src/static/img/graphql.svg b/client/src/static/img/graphql.svg new file mode 100644 index 00000000000..ef85915ffaa --- /dev/null +++ b/client/src/static/img/graphql.svg @@ -0,0 +1,4 @@ + + + + diff --git a/client/src/static/img/wheelchair.svg b/client/src/static/img/wheelchair.svg new file mode 100644 index 00000000000..d8cf96ca995 --- /dev/null +++ b/client/src/static/img/wheelchair.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/client/src/static/query/tripQuery.tsx b/client/src/static/query/tripQuery.tsx index ef8183b0832..57cca5d4056 100644 --- a/client/src/static/query/tripQuery.tsx +++ b/client/src/static/query/tripQuery.tsx @@ -11,6 +11,7 @@ export const query = graphql(` $searchWindow: Int $modes: Modes $itineraryFiltersDebug: ItineraryFilterDebugProfile + $wheelchairAccessible: Boolean $pageCursor: String ) { trip( @@ -22,6 +23,7 @@ export const query = graphql(` searchWindow: $searchWindow modes: $modes itineraryFilters: { debug: $itineraryFiltersDebug } + wheelchairAccessible: $wheelchairAccessible pageCursor: $pageCursor ) { previousPageCursor diff --git a/client/src/style.css b/client/src/style.css index 1a24ac2c072..86310fd857d 100644 --- a/client/src/style.css +++ b/client/src/style.css @@ -7,7 +7,7 @@ margin-right: 14px; } -@media (min-width: 2160px) { +@media (min-width: 1895px) { .top-content { height: 75px; } @@ -17,7 +17,7 @@ } } -@media (max-width: 2159px) { +@media (max-width: 1896px) { .top-content { height: 150px; } @@ -50,16 +50,34 @@ margin-right: 1rem; } +.search-bar input.input-small { + max-width: 100px; +} + +.search-bar input.input-medium { + max-width: 130px; +} + .search-bar-route-button-wrapper { height: 5rem; padding-top: 25px; } +.search-bar-route-button-wrapper a.btn img { + margin-top: -2px; +} + .itinerary-list-container { width: 36rem; overflow-y: auto; } +.itinerary-list-container .time-zone-info { + margin: 10px 20px; + font-size: 12px; + text-align: right; +} + .itinerary-header-wrapper { position: relative; background: #0a53be; diff --git a/client/src/util/formatTime.ts b/client/src/util/formatTime.ts index 1849640fe3f..1818ced5cd1 100644 --- a/client/src/util/formatTime.ts +++ b/client/src/util/formatTime.ts @@ -4,10 +4,11 @@ * If style argument is provided formatted with ('medium') or without ('short') seconds, * otherwise seconds are shown if not 0. */ -export function formatTime(dateTime: string, style?: 'short' | 'medium') { +export function formatTime(dateTime: string, timeZone: string, style?: 'short' | 'medium') { const parsed = new Date(dateTime); return parsed.toLocaleTimeString('en-US', { timeStyle: style ? style : parsed.getSeconds() === 0 ? 'short' : 'medium', hourCycle: 'h24', + timeZone: timeZone, }); } diff --git a/doc/templates/BuildConfiguration.md b/doc/templates/BuildConfiguration.md index 7e768e30eb1..77b03dae500 100644 --- a/doc/templates/BuildConfiguration.md +++ b/doc/templates/BuildConfiguration.md @@ -2,7 +2,7 @@ NOTE! Part of this document is generated. Make sure you edit the template, not the generated doc. - Template directory is: /doc/templates - - Generated directory is: /docs + - Generated directory is: /doc/user --> # Graph Build Configuration diff --git a/doc/templates/Configuration.md b/doc/templates/Configuration.md index 6f6f4fc9960..45b2c36c67b 100644 --- a/doc/templates/Configuration.md +++ b/doc/templates/Configuration.md @@ -2,7 +2,7 @@ NOTE! Part of this document is generated. Make sure you edit the template, not the generated doc. - Template directory is: /doc/templates - - Generated directory is: /docs + - Generated directory is: /doc/user --> # Configuring OpenTripPlanner diff --git a/doc/templates/GraphQL-Tutorial.md b/doc/templates/GraphQL-Tutorial.md index 11a2e304119..2a78be65cc2 100644 --- a/doc/templates/GraphQL-Tutorial.md +++ b/doc/templates/GraphQL-Tutorial.md @@ -2,7 +2,7 @@ NOTE! Part of this document is generated. Make sure you edit the template, not the generated doc. - Template directory is: /doc/templates - - Generated directory is: /docs + - Generated directory is: /doc/user --> # GraphQL tutorial diff --git a/doc/templates/Netex-Tutorial.md b/doc/templates/Netex-Tutorial.md new file mode 100644 index 00000000000..2afa997ee73 --- /dev/null +++ b/doc/templates/Netex-Tutorial.md @@ -0,0 +1,80 @@ +# NeTEx & SIRI tutorial + +One important new feature of OTP2 is the ability to +load [NeTEx](https://en.wikipedia.org/wiki/NeTEx) and [SIRI](https://en.wikipedia.org/wiki/Service_Interface_for_Real_Time_Information) +data. NeTEx is a European [specification for transit data exchange](http://netex-cen.eu), comparable in purpose to +GTFS but broader in scope. + +First of all, you need to download a [bundled jar of OTP](Getting-OTP.md). + +Secondly, you will use the [Norwegian NeTEx file](https://developer.entur.org/pages-intro-files) as +well as the [Norwegian OSM data](http://download.geofabrik.de/europe/norway.html), but OTP can download the NeTEx one for you. + +## Configuring the build + +Create a working directory and place the OTP jar file in it and call it `otp.jar.` + +Since we download the OSM data from a free source, we don't want to put undue stress on the server. +Therefore we download it before building the graph, not during. + +``` +curl https://download.geofabrik.de/europe/norway-latest.osm.pbf -o norway.osm.pbf +``` + +Now create a file called `build-config.json` in the same folder and fill it with the following +content: + + + +Note the special section specifying how to find NeTEx XML files within the single ZIP archive that +OTP downloads. + +Now you can instruct OTP to build a graph from this configuration file: + +`java -Xmx16G -jar otp.jar --build --save .` + +This should produce a file `graph.obj` in the same directory as your `build-config.json`. + +Building the Norway graph requires downloading about 250MB of input data so stay patient at the beginning +particularly on a slow internet connection. +The actual build takes approximately 10 minutes (without elevation data, as is configured above), +and can be done within 16GB of heap memory (JVM switch `-Xmx16G`). The Graph file it produces is +about 1.1 GB. The server will take about 30 seconds to load this graph and start up, and will +consume about 6GB of heap memory under light use. + +You can then start up an OTP server with a command like this: + +`java -Xmx6G -jar otp.jar --load .` + +Once the server is started up, go to `http://localhost:8080` in a browser to try out your server +using OTP's built in testing web client. Try some long trips like Oslo to Bergen and see if you can +get long distance trains and flights as alternatives. You might need to increase the walking limit +above its very low default value. + +## Adding SIRI real time Data + +Another important feature in OTP version 2 is the ability to +use [SIRI real-time data](https://en.wikipedia.org/wiki/Service_Interface_for_Real_Time_Information). +Within the EU data standards, SIRI is analogous to GTFS-RT: a way to apply real-time updates on top +of schedule data. While technically a distinct specification from NeTEx, both NeTEx and SIRI use the +Transmodel vocabulary, allowing SIRI messages to reference entities in NeTEx schedule data. Like +GTFS-RT, SIRI is consumed by OTP2 using "graph updaters" which are configured in +the `router-config.json` file, which is placed in the same directory as the `graph.obj` file and +loaded at server startup. + + + +After saving the file in the working directory, restart OTP. + +The updaters fetch two different kinds of SIRI data: + +- Situation Exchange (SX, text notices analogous to GTFS-RT Alerts) +- Estimated Timetable (ET, predicted arrival times analogous to GTFS-RT TripUpdates) + +These updaters can handle differential updates, but they use a polling approach rather than the +message-oriented streaming approach of the GTFS-RT Websocket updater. The server keeps track of +clients, sending only the things that have changed since the last polling operation. + +Note that between these SIRI updaters and the GTFS-RT Websocket updater, we now have both polling +and streaming examples of GTFS-RT "incrementality" semantics, so should be able to finalize that +part of the specification. \ No newline at end of file diff --git a/doc/templates/RouteRequest.md b/doc/templates/RouteRequest.md index a452e1d1480..9b7cd6fd58f 100644 --- a/doc/templates/RouteRequest.md +++ b/doc/templates/RouteRequest.md @@ -2,7 +2,7 @@ NOTE! Part of this document is generated. Make sure you edit the template, not the generated doc. - Template directory is: /doc/templates - - Generated directory is: /docs + - Generated directory is: /doc/user --> # Route Request diff --git a/doc/templates/RouterConfiguration.md b/doc/templates/RouterConfiguration.md index b6c6ccf9c4b..87e4c1693cc 100644 --- a/doc/templates/RouterConfiguration.md +++ b/doc/templates/RouterConfiguration.md @@ -2,7 +2,7 @@ NOTE! Part of this document is generated. Make sure you edit the template, not the generated doc. - Template directory is: /doc/templates - - Generated directory is: /docs + - Generated directory is: /doc/user --> # Router configuration diff --git a/doc/templates/StopConsolidation.md b/doc/templates/StopConsolidation.md index 6817ee47d4c..70866882bd1 100644 --- a/doc/templates/StopConsolidation.md +++ b/doc/templates/StopConsolidation.md @@ -2,7 +2,7 @@ NOTE! Part of this document is generated. Make sure you edit the template, not the generated doc. - Template directory is: /doc/templates - - Generated directory is: /docs + - Generated directory is: /doc/user --> # Stop consolidation diff --git a/doc/templates/UpdaterConfig.md b/doc/templates/UpdaterConfig.md index 440cd96f733..aab5631e6e2 100644 --- a/doc/templates/UpdaterConfig.md +++ b/doc/templates/UpdaterConfig.md @@ -2,7 +2,7 @@ NOTE! Part of this document is generated. Make sure you edit the template, not the generated doc. - Template directory is: /doc/templates - - Generated directory is: /docs + - Generated directory is: /doc/user --> diff --git a/doc/templates/VehicleParking.md b/doc/templates/VehicleParking.md index 721cbc2657a..0eb561248a1 100644 --- a/doc/templates/VehicleParking.md +++ b/doc/templates/VehicleParking.md @@ -3,7 +3,7 @@ ## Contact Info - For HSL Park and Ride updater: Digitransit team, HSL, Helsinki, Finland -- For Bikely, NOI and Bikeep updater: Leonard Ehrenfried, [mail@leonard.io](mailto:mail@leonard.io) +- For Bikely, Bikeep and SIRI-FM updater: Leonard Ehrenfried, [mail@leonard.io](mailto:mail@leonard.io) ## Documentation @@ -16,7 +16,7 @@ Currently contains the following updaters: - [HSL Park and Ride](https://p.hsl.fi/docs/index.html) - [ParkAPI](https://github.com/offenesdresden/ParkAPI) - [Bikely](https://www.safebikely.com/) -- [NOI Open Data Hub](https://opendatahub.com/) +- SIRI-FM ### Configuration @@ -40,10 +40,6 @@ All updaters have the following parameters in common: -## NOI Open Data Hub - - - ## Bikeep diff --git a/doc/user/BuildConfiguration.md b/doc/user/BuildConfiguration.md index b311991120e..f5281db0f4e 100644 --- a/doc/user/BuildConfiguration.md +++ b/doc/user/BuildConfiguration.md @@ -2,7 +2,7 @@ NOTE! Part of this document is generated. Make sure you edit the template, not the generated doc. - Template directory is: /doc/templates - - Generated directory is: /docs + - Generated directory is: /doc/user --> # Graph Build Configuration @@ -17,102 +17,103 @@ Sections follow that describe particular settings in more depth. -| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | -|--------------------------------------------------------------------------|:-----------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------:|-----------------------------------|:-----:| -| [areaVisibility](#areaVisibility) | `boolean` | Perform visibility calculations. | *Optional* | `false` | 1.5 | -| [buildReportDir](#buildReportDir) | `uri` | URI to the directory where the graph build report should be written to. | *Optional* | | 2.0 | -| [configVersion](#configVersion) | `string` | Deployment version of the *build-config.json*. | *Optional* | | 2.1 | -| [dataImportReport](#dataImportReport) | `boolean` | Generate nice HTML report of Graph errors/warnings | *Optional* | `false` | 2.0 | -| [distanceBetweenElevationSamples](#distanceBetweenElevationSamples) | `double` | The distance between elevation samples in meters. | *Optional* | `10.0` | 2.0 | -| embedRouterConfig | `boolean` | Embed the Router config in the graph, which allows it to be sent to a server fully configured over the wire. | *Optional* | `true` | 2.0 | -| [graph](#graph) | `uri` | URI to the graph object file for reading and writing. | *Optional* | | 2.0 | -| [gsCredentials](#gsCredentials) | `string` | Local file system path to Google Cloud Platform service accounts credentials file. | *Optional* | | 2.0 | -| [includeEllipsoidToGeoidDifference](#includeEllipsoidToGeoidDifference) | `boolean` | Include the Ellipsoid to Geoid difference in the calculations of every point along every StreetWithElevationEdge. | *Optional* | `false` | 2.0 | -| maxAreaNodes | `integer` | Visibility calculations for an area will not be done if there are more nodes than this limit. | *Optional* | `150` | 2.1 | -| [maxDataImportIssuesPerFile](#maxDataImportIssuesPerFile) | `integer` | When to split the import report. | *Optional* | `1000` | 2.0 | -| maxElevationPropagationMeters | `integer` | The maximum distance to propagate elevation to vertices which have no elevation. | *Optional* | `2000` | 1.5 | -| [maxStopToShapeSnapDistance](#maxStopToShapeSnapDistance) | `double` | Maximum distance between route shapes and their stops. | *Optional* | `150.0` | 2.1 | -| maxTransferDuration | `duration` | Transfers up to this duration with the default walk speed value will be pre-calculated and included in the Graph. | *Optional* | `"PT30M"` | 2.1 | -| [multiThreadElevationCalculations](#multiThreadElevationCalculations) | `boolean` | Configuring multi-threading during elevation calculations. | *Optional* | `false` | 2.0 | -| [osmCacheDataInMem](#osmCacheDataInMem) | `boolean` | If OSM data should be cached in memory during processing. | *Optional* | `false` | 2.0 | -| [osmNaming](#osmNaming) | `enum` | A custom OSM namer to use. | *Optional* | `"default"` | 1.5 | -| platformEntriesLinking | `boolean` | Link unconnected entries to public transport platforms. | *Optional* | `false` | 2.0 | -| [readCachedElevations](#readCachedElevations) | `boolean` | Whether to read cached elevation data. | *Optional* | `true` | 2.0 | -| staticBikeParkAndRide | `boolean` | Whether we should create bike P+R stations from OSM data. | *Optional* | `false` | 1.5 | -| staticParkAndRide | `boolean` | Whether we should create car P+R stations from OSM data. | *Optional* | `true` | 1.5 | -| stopConsolidationFile | `uri` | Name of the CSV-formatted file in the build directory which contains the configuration for stop consolidation. | *Optional* | | 2.5 | -| [streetGraph](#streetGraph) | `uri` | URI to the street graph object file for reading and writing. | *Optional* | | 2.0 | -| [subwayAccessTime](#subwayAccessTime) | `double` | Minutes necessary to reach stops served by trips on routes of route_type=1 (subway) from the street. | *Optional* | `2.0` | 1.5 | -| [transitModelTimeZone](#transitModelTimeZone) | `time-zone` | Time zone for the graph. | *Optional* | | 2.2 | -| [transitServiceEnd](#transitServiceEnd) | `duration` | Limit the import of transit services to the given end date. | *Optional* | `"P3Y"` | 2.0 | -| [transitServiceStart](#transitServiceStart) | `duration` | Limit the import of transit services to the given START date. | *Optional* | `"-P1Y"` | 2.0 | -| [writeCachedElevations](#writeCachedElevations) | `boolean` | Reusing elevation data from previous builds | *Optional* | `false` | 2.0 | -| [boardingLocationTags](#boardingLocationTags) | `string[]` | What OSM tags should be looked on for the source of matching stops to platforms and stops. | *Optional* | | 2.2 | -| [dataOverlay](sandbox/DataOverlay.md) | `object` | Config for the DataOverlay Sandbox module | *Optional* | | 2.2 | -| [dem](#dem) | `object[]` | Specify parameters for DEM extracts. | *Optional* | | 2.2 | -|       [elevationUnitMultiplier](#dem_0_elevationUnitMultiplier) | `double` | Specify a multiplier to convert elevation units from source to meters. Overrides the value specified in `demDefaults`. | *Optional* | `1.0` | 2.3 | -|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | -| demDefaults | `object` | Default properties for DEM extracts. | *Optional* | | 2.3 | -|    [elevationUnitMultiplier](#demDefaults_elevationUnitMultiplier) | `double` | Specify a multiplier to convert elevation units from source to meters. | *Optional* | `1.0` | 2.3 | -| [elevationBucket](#elevationBucket) | `object` | Used to download NED elevation tiles from the given AWS S3 bucket. | *Optional* | | na | -| [emissions](sandbox/Emissions.md) | `object` | Emissions configuration. | *Optional* | | 2.5 | -| [fares](sandbox/Fares.md) | `object` | Fare configuration. | *Optional* | | 2.0 | -| gtfsDefaults | `object` | The gtfsDefaults section allows you to specify default properties for GTFS files. | *Optional* | | 2.3 | -|    blockBasedInterlining | `boolean` | Whether to create stay-seated transfers in between two trips with the same block id. | *Optional* | `true` | 2.3 | -|    [discardMinTransferTimes](#gd_discardMinTransferTimes) | `boolean` | Should minimum transfer times in GTFS files be discarded. | *Optional* | `false` | 2.3 | -|    maxInterlineDistance | `integer` | Maximal distance between stops in meters that will connect consecutive trips that are made with same vehicle. | *Optional* | `200` | 2.3 | -|    removeRepeatedStops | `boolean` | Should consecutive identical stops be merged into one stop time entry. | *Optional* | `true` | 2.3 | -|    [stationTransferPreference](#gd_stationTransferPreference) | `enum` | Should there be some preference or aversion for transfers at stops that are part of a station. | *Optional* | `"allowed"` | 2.3 | -| islandPruning | `object` | Settings for fixing street graph connectivity errors | *Optional* | | 2.3 | -|    [adaptivePruningDistance](#islandPruning_adaptivePruningDistance) | `integer` | Search distance for analyzing islands in pruning. | *Optional* | `250` | 2.3 | -|    [adaptivePruningFactor](#islandPruning_adaptivePruningFactor) | `double` | Defines how much pruning thresholds grow maximally by distance. | *Optional* | `50.0` | 2.3 | -|    [islandWithStopsMaxSize](#islandPruning_islandWithStopsMaxSize) | `integer` | When a graph island with stops in it should be pruned. | *Optional* | `2` | 2.3 | -|    [islandWithoutStopsMaxSize](#islandPruning_islandWithoutStopsMaxSize) | `integer` | When a graph island without stops should be pruned. | *Optional* | `10` | 2.3 | -| [localFileNamePatterns](#localFileNamePatterns) | `object` | Patterns for matching OTP file types in the base directory | *Optional* | | 2.0 | -|    [dem](#lfp_dem) | `regexp` | Pattern for matching elevation DEM files. | *Optional* | `"(?i)\.tiff?$"` | 2.0 | -|    [gtfs](#lfp_gtfs) | `regexp` | Patterns for matching GTFS zip-files or directories. | *Optional* | `"(?i)gtfs"` | 2.0 | -|    [netex](#lfp_netex) | `regexp` | Patterns for matching NeTEx zip files or directories. | *Optional* | `"(?i)netex"` | 2.0 | -|    [osm](#lfp_osm) | `regexp` | Pattern for matching Open Street Map input files. | *Optional* | `"(?i)(\.pbf¦\.osm¦\.osm\.xml)$"` | 2.0 | -| netexDefaults | `object` | The netexDefaults section allows you to specify default properties for NeTEx files. | *Optional* | | 2.2 | -|    feedId | `string` | This field is used to identify the specific NeTEx feed. It is used instead of the feed_id field in GTFS file feed_info.txt. | *Optional* | `"NETEX"` | 2.2 | -|    [groupFilePattern](#nd_groupFilePattern) | `regexp` | Pattern for matching group NeTEx files. | *Optional* | `"(\w{3})-.*\.xml"` | 2.0 | -|    ignoreFareFrame | `boolean` | Ignore contents of the FareFrame | *Optional* | `false` | 2.3 | -|    [ignoreFilePattern](#nd_ignoreFilePattern) | `regexp` | Pattern for matching ignored files in a NeTEx bundle. | *Optional* | `"$^"` | 2.0 | -|    ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `true` | 2.6 | -|    noTransfersOnIsolatedStops | `boolean` | Whether we should allow transfers to and from StopPlaces marked with LimitedUse.ISOLATED | *Optional* | `false` | 2.2 | -|    [sharedFilePattern](#nd_sharedFilePattern) | `regexp` | Pattern for matching shared NeTEx files in a NeTEx bundle. | *Optional* | `"shared-data\.xml"` | 2.0 | -|    [sharedGroupFilePattern](#nd_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | -|    [ferryIdsNotAllowedForBicycle](#nd_ferryIdsNotAllowedForBicycle) | `string[]` | List ferries which do not allow bikes. | *Optional* | | 2.0 | -| [osm](#osm) | `object[]` | Configure properties for a given OpenStreetMap feed. | *Optional* | | 2.2 | -|       [osmTagMapping](#osm_0_osmTagMapping) | `enum` | The named set of mapping rules applied when parsing OSM tags. Overrides the value specified in `osmDefaults`. | *Optional* | `"default"` | 2.2 | -|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | -|       timeZone | `time-zone` | The timezone used to resolve opening hours in OSM data. Overrides the value specified in `osmDefaults`. | *Optional* | | 2.2 | -| osmDefaults | `object` | Default properties for OpenStreetMap feeds. | *Optional* | | 2.2 | -|    [osmTagMapping](#od_osmTagMapping) | `enum` | The named set of mapping rules applied when parsing OSM tags. | *Optional* | `"default"` | 2.2 | -|    timeZone | `time-zone` | The timezone used to resolve opening hours in OSM data. | *Optional* | | 2.2 | -| [transferRequests](RouteRequest.md) | `object[]` | Routing requests to use for pre-calculating stop-to-stop transfers. | *Optional* | | 2.1 | -| [transitFeeds](#transitFeeds) | `object[]` | Scan for transit data files | *Optional* | | 2.2 | -|    { object } | `object` | Nested object in array. The object type is determined by the parameters. | *Optional* | | 2.2 | -|       type = "gtfs" | `enum` | The feed input format. | *Required* | | 2.2 | -|       blockBasedInterlining | `boolean` | Whether to create stay-seated transfers in between two trips with the same block id. Overrides the value specified in `gtfsDefaults`. | *Optional* | `true` | 2.3 | -|       [discardMinTransferTimes](#tf_0_discardMinTransferTimes) | `boolean` | Should minimum transfer times in GTFS files be discarded. Overrides the value specified in `gtfsDefaults`. | *Optional* | `false` | 2.3 | -|       feedId | `string` | The unique ID for this feed. This overrides any feed ID defined within the feed itself. | *Optional* | | 2.2 | -|       maxInterlineDistance | `integer` | Maximal distance between stops in meters that will connect consecutive trips that are made with same vehicle. Overrides the value specified in `gtfsDefaults`. | *Optional* | `200` | 2.3 | -|       removeRepeatedStops | `boolean` | Should consecutive identical stops be merged into one stop time entry. Overrides the value specified in `gtfsDefaults`. | *Optional* | `true` | 2.3 | -|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | -|       [stationTransferPreference](#tf_0_stationTransferPreference) | `enum` | Should there be some preference or aversion for transfers at stops that are part of a station. Overrides the value specified in `gtfsDefaults`. | *Optional* | `"allowed"` | 2.3 | -|    { object } | `object` | Nested object in array. The object type is determined by the parameters. | *Optional* | | 2.2 | -|       type = "netex" | `enum` | The feed input format. | *Required* | | 2.2 | -|       feedId | `string` | This field is used to identify the specific NeTEx feed. It is used instead of the feed_id field in GTFS file feed_info.txt. | *Required* | | 2.2 | -|       [groupFilePattern](#tf_1_groupFilePattern) | `regexp` | Pattern for matching group NeTEx files. | *Optional* | `"(\w{3})-.*\.xml"` | 2.0 | -|       ignoreFareFrame | `boolean` | Ignore contents of the FareFrame | *Optional* | `false` | 2.3 | -|       [ignoreFilePattern](#tf_1_ignoreFilePattern) | `regexp` | Pattern for matching ignored files in a NeTEx bundle. | *Optional* | `"$^"` | 2.0 | -|       ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `true` | 2.6 | -|       noTransfersOnIsolatedStops | `boolean` | Whether we should allow transfers to and from StopPlaces marked with LimitedUse.ISOLATED | *Optional* | `false` | 2.2 | -|       [sharedFilePattern](#tf_1_sharedFilePattern) | `regexp` | Pattern for matching shared NeTEx files in a NeTEx bundle. | *Optional* | `"shared-data\.xml"` | 2.0 | -|       [sharedGroupFilePattern](#tf_1_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | -|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | -|       [ferryIdsNotAllowedForBicycle](#tf_1_ferryIdsNotAllowedForBicycle) | `string[]` | List ferries which do not allow bikes. | *Optional* | | 2.0 | +| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | +|--------------------------------------------------------------------------|:------------------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------:|-----------------------------------|:-----:| +| [areaVisibility](#areaVisibility) | `boolean` | Perform visibility calculations. | *Optional* | `false` | 1.5 | +| [buildReportDir](#buildReportDir) | `uri` | URI to the directory where the graph build report should be written to. | *Optional* | | 2.0 | +| [configVersion](#configVersion) | `string` | Deployment version of the *build-config.json*. | *Optional* | | 2.1 | +| [dataImportReport](#dataImportReport) | `boolean` | Generate nice HTML report of Graph errors/warnings | *Optional* | `false` | 2.0 | +| [distanceBetweenElevationSamples](#distanceBetweenElevationSamples) | `double` | The distance between elevation samples in meters. | *Optional* | `10.0` | 2.0 | +| embedRouterConfig | `boolean` | Embed the Router config in the graph, which allows it to be sent to a server fully configured over the wire. | *Optional* | `true` | 2.0 | +| [graph](#graph) | `uri` | URI to the graph object file for reading and writing. | *Optional* | | 2.0 | +| [gsCredentials](#gsCredentials) | `string` | Local file system path to Google Cloud Platform service accounts credentials file. | *Optional* | | 2.0 | +| [includeEllipsoidToGeoidDifference](#includeEllipsoidToGeoidDifference) | `boolean` | Include the Ellipsoid to Geoid difference in the calculations of every point along every StreetWithElevationEdge. | *Optional* | `false` | 2.0 | +| maxAreaNodes | `integer` | Visibility calculations for an area will not be done if there are more nodes than this limit. | *Optional* | `150` | 2.1 | +| [maxDataImportIssuesPerFile](#maxDataImportIssuesPerFile) | `integer` | When to split the import report. | *Optional* | `1000` | 2.0 | +| maxElevationPropagationMeters | `integer` | The maximum distance to propagate elevation to vertices which have no elevation. | *Optional* | `2000` | 1.5 | +| [maxStopToShapeSnapDistance](#maxStopToShapeSnapDistance) | `double` | Maximum distance between route shapes and their stops. | *Optional* | `150.0` | 2.1 | +| maxTransferDuration | `duration` | Transfers up to this duration with the default walk speed value will be pre-calculated and included in the Graph. | *Optional* | `"PT30M"` | 2.1 | +| [multiThreadElevationCalculations](#multiThreadElevationCalculations) | `boolean` | Configuring multi-threading during elevation calculations. | *Optional* | `false` | 2.0 | +| [osmCacheDataInMem](#osmCacheDataInMem) | `boolean` | If OSM data should be cached in memory during processing. | *Optional* | `false` | 2.0 | +| [osmNaming](#osmNaming) | `enum` | A custom OSM namer to use. | *Optional* | `"default"` | 1.5 | +| platformEntriesLinking | `boolean` | Link unconnected entries to public transport platforms. | *Optional* | `false` | 2.0 | +| [readCachedElevations](#readCachedElevations) | `boolean` | Whether to read cached elevation data. | *Optional* | `true` | 2.0 | +| staticBikeParkAndRide | `boolean` | Whether we should create bike P+R stations from OSM data. | *Optional* | `false` | 1.5 | +| staticParkAndRide | `boolean` | Whether we should create car P+R stations from OSM data. | *Optional* | `true` | 1.5 | +| stopConsolidationFile | `uri` | Name of the CSV-formatted file in the build directory which contains the configuration for stop consolidation. | *Optional* | | 2.5 | +| [streetGraph](#streetGraph) | `uri` | URI to the street graph object file for reading and writing. | *Optional* | | 2.0 | +| [subwayAccessTime](#subwayAccessTime) | `double` | Minutes necessary to reach stops served by trips on routes of route_type=1 (subway) from the street. | *Optional* | `2.0` | 1.5 | +| [transitModelTimeZone](#transitModelTimeZone) | `time-zone` | Time zone for the graph. | *Optional* | | 2.2 | +| [transitServiceEnd](#transitServiceEnd) | `duration` | Limit the import of transit services to the given end date. | *Optional* | `"P3Y"` | 2.0 | +| [transitServiceStart](#transitServiceStart) | `duration` | Limit the import of transit services to the given START date. | *Optional* | `"-P1Y"` | 2.0 | +| [writeCachedElevations](#writeCachedElevations) | `boolean` | Reusing elevation data from previous builds | *Optional* | `false` | 2.0 | +| [boardingLocationTags](#boardingLocationTags) | `string[]` | What OSM tags should be looked on for the source of matching stops to platforms and stops. | *Optional* | | 2.2 | +| [dataOverlay](sandbox/DataOverlay.md) | `object` | Config for the DataOverlay Sandbox module | *Optional* | | 2.2 | +| [dem](#dem) | `object[]` | Specify parameters for DEM extracts. | *Optional* | | 2.2 | +|       [elevationUnitMultiplier](#dem_0_elevationUnitMultiplier) | `double` | Specify a multiplier to convert elevation units from source to meters. Overrides the value specified in `demDefaults`. | *Optional* | `1.0` | 2.3 | +|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | +| demDefaults | `object` | Default properties for DEM extracts. | *Optional* | | 2.3 | +|    [elevationUnitMultiplier](#demDefaults_elevationUnitMultiplier) | `double` | Specify a multiplier to convert elevation units from source to meters. | *Optional* | `1.0` | 2.3 | +| [elevationBucket](#elevationBucket) | `object` | Used to download NED elevation tiles from the given AWS S3 bucket. | *Optional* | | na | +| [emissions](sandbox/Emissions.md) | `object` | Emissions configuration. | *Optional* | | 2.5 | +| [fares](sandbox/Fares.md) | `object` | Fare configuration. | *Optional* | | 2.0 | +| gtfsDefaults | `object` | The gtfsDefaults section allows you to specify default properties for GTFS files. | *Optional* | | 2.3 | +|    blockBasedInterlining | `boolean` | Whether to create stay-seated transfers in between two trips with the same block id. | *Optional* | `true` | 2.3 | +|    [discardMinTransferTimes](#gd_discardMinTransferTimes) | `boolean` | Should minimum transfer times in GTFS files be discarded. | *Optional* | `false` | 2.3 | +|    maxInterlineDistance | `integer` | Maximal distance between stops in meters that will connect consecutive trips that are made with same vehicle. | *Optional* | `200` | 2.3 | +|    removeRepeatedStops | `boolean` | Should consecutive identical stops be merged into one stop time entry. | *Optional* | `true` | 2.3 | +|    [stationTransferPreference](#gd_stationTransferPreference) | `enum` | Should there be some preference or aversion for transfers at stops that are part of a station. | *Optional* | `"allowed"` | 2.3 | +| islandPruning | `object` | Settings for fixing street graph connectivity errors | *Optional* | | 2.3 | +|    [adaptivePruningDistance](#islandPruning_adaptivePruningDistance) | `integer` | Search distance for analyzing islands in pruning. | *Optional* | `250` | 2.3 | +|    [adaptivePruningFactor](#islandPruning_adaptivePruningFactor) | `double` | Defines how much pruning thresholds grow maximally by distance. | *Optional* | `50.0` | 2.3 | +|    [islandWithStopsMaxSize](#islandPruning_islandWithStopsMaxSize) | `integer` | When a graph island with stops in it should be pruned. | *Optional* | `2` | 2.3 | +|    [islandWithoutStopsMaxSize](#islandPruning_islandWithoutStopsMaxSize) | `integer` | When a graph island without stops should be pruned. | *Optional* | `10` | 2.3 | +| [localFileNamePatterns](#localFileNamePatterns) | `object` | Patterns for matching OTP file types in the base directory | *Optional* | | 2.0 | +|    [dem](#lfp_dem) | `regexp` | Pattern for matching elevation DEM files. | *Optional* | `"(?i)\.tiff?$"` | 2.0 | +|    [gtfs](#lfp_gtfs) | `regexp` | Patterns for matching GTFS zip-files or directories. | *Optional* | `"(?i)gtfs"` | 2.0 | +|    [netex](#lfp_netex) | `regexp` | Patterns for matching NeTEx zip files or directories. | *Optional* | `"(?i)netex"` | 2.0 | +|    [osm](#lfp_osm) | `regexp` | Pattern for matching Open Street Map input files. | *Optional* | `"(?i)(\.pbf¦\.osm¦\.osm\.xml)$"` | 2.0 | +| netexDefaults | `object` | The netexDefaults section allows you to specify default properties for NeTEx files. | *Optional* | | 2.2 | +|    feedId | `string` | This field is used to identify the specific NeTEx feed. It is used instead of the feed_id field in GTFS file feed_info.txt. | *Optional* | `"NETEX"` | 2.2 | +|    [groupFilePattern](#nd_groupFilePattern) | `regexp` | Pattern for matching group NeTEx files. | *Optional* | `"(\w{3})-.*\.xml"` | 2.0 | +|    ignoreFareFrame | `boolean` | Ignore contents of the FareFrame | *Optional* | `false` | 2.3 | +|    [ignoreFilePattern](#nd_ignoreFilePattern) | `regexp` | Pattern for matching ignored files in a NeTEx bundle. | *Optional* | `"$^"` | 2.0 | +|    ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `true` | 2.6 | +|    noTransfersOnIsolatedStops | `boolean` | Whether we should allow transfers to and from StopPlaces marked with LimitedUse.ISOLATED | *Optional* | `false` | 2.2 | +|    [sharedFilePattern](#nd_sharedFilePattern) | `regexp` | Pattern for matching shared NeTEx files in a NeTEx bundle. | *Optional* | `"shared-data\.xml"` | 2.0 | +|    [sharedGroupFilePattern](#nd_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | +|    [ferryIdsNotAllowedForBicycle](#nd_ferryIdsNotAllowedForBicycle) | `string[]` | List ferries which do not allow bikes. | *Optional* | | 2.0 | +| [osm](#osm) | `object[]` | Configure properties for a given OpenStreetMap feed. | *Optional* | | 2.2 | +|       [osmTagMapping](#osm_0_osmTagMapping) | `enum` | The named set of mapping rules applied when parsing OSM tags. Overrides the value specified in `osmDefaults`. | *Optional* | `"default"` | 2.2 | +|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | +|       timeZone | `time-zone` | The timezone used to resolve opening hours in OSM data. Overrides the value specified in `osmDefaults`. | *Optional* | | 2.2 | +| osmDefaults | `object` | Default properties for OpenStreetMap feeds. | *Optional* | | 2.2 | +|    [osmTagMapping](#od_osmTagMapping) | `enum` | The named set of mapping rules applied when parsing OSM tags. | *Optional* | `"default"` | 2.2 | +|    timeZone | `time-zone` | The timezone used to resolve opening hours in OSM data. | *Optional* | | 2.2 | +| [transferRequests](RouteRequest.md) | `object[]` | Routing requests to use for pre-calculating stop-to-stop transfers. | *Optional* | | 2.1 | +| [transitFeeds](#transitFeeds) | `object[]` | Scan for transit data files | *Optional* | | 2.2 | +|    { object } | `object` | Nested object in array. The object type is determined by the parameters. | *Optional* | | 2.2 | +|       type = "gtfs" | `enum` | The feed input format. | *Required* | | 2.2 | +|       blockBasedInterlining | `boolean` | Whether to create stay-seated transfers in between two trips with the same block id. Overrides the value specified in `gtfsDefaults`. | *Optional* | `true` | 2.3 | +|       [discardMinTransferTimes](#tf_0_discardMinTransferTimes) | `boolean` | Should minimum transfer times in GTFS files be discarded. Overrides the value specified in `gtfsDefaults`. | *Optional* | `false` | 2.3 | +|       feedId | `string` | The unique ID for this feed. This overrides any feed ID defined within the feed itself. | *Optional* | | 2.2 | +|       maxInterlineDistance | `integer` | Maximal distance between stops in meters that will connect consecutive trips that are made with same vehicle. Overrides the value specified in `gtfsDefaults`. | *Optional* | `200` | 2.3 | +|       removeRepeatedStops | `boolean` | Should consecutive identical stops be merged into one stop time entry. Overrides the value specified in `gtfsDefaults`. | *Optional* | `true` | 2.3 | +|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | +|       [stationTransferPreference](#tf_0_stationTransferPreference) | `enum` | Should there be some preference or aversion for transfers at stops that are part of a station. Overrides the value specified in `gtfsDefaults`. | *Optional* | `"allowed"` | 2.3 | +|    { object } | `object` | Nested object in array. The object type is determined by the parameters. | *Optional* | | 2.2 | +|       type = "netex" | `enum` | The feed input format. | *Required* | | 2.2 | +|       feedId | `string` | This field is used to identify the specific NeTEx feed. It is used instead of the feed_id field in GTFS file feed_info.txt. | *Required* | | 2.2 | +|       [groupFilePattern](#tf_1_groupFilePattern) | `regexp` | Pattern for matching group NeTEx files. | *Optional* | `"(\w{3})-.*\.xml"` | 2.0 | +|       ignoreFareFrame | `boolean` | Ignore contents of the FareFrame | *Optional* | `false` | 2.3 | +|       [ignoreFilePattern](#tf_1_ignoreFilePattern) | `regexp` | Pattern for matching ignored files in a NeTEx bundle. | *Optional* | `"$^"` | 2.0 | +|       ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `true` | 2.6 | +|       noTransfersOnIsolatedStops | `boolean` | Whether we should allow transfers to and from StopPlaces marked with LimitedUse.ISOLATED | *Optional* | `false` | 2.2 | +|       [sharedFilePattern](#tf_1_sharedFilePattern) | `regexp` | Pattern for matching shared NeTEx files in a NeTEx bundle. | *Optional* | `"shared-data\.xml"` | 2.0 | +|       [sharedGroupFilePattern](#tf_1_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | +|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | +|       [ferryIdsNotAllowedForBicycle](#tf_1_ferryIdsNotAllowedForBicycle) | `string[]` | List ferries which do not allow bikes. | *Optional* | | 2.0 | +| [transitRouteToStationCentroid](#transitRouteToStationCentroid) | `feed-scoped-id[]` | List stations that should route to centroid. | *Optional* | | 2.7 | @@ -1067,6 +1068,26 @@ For this reason we allow bicycles on ferries by default and allow to override th case where this is not the case. +

transitRouteToStationCentroid

+ +**Since version:** `2.7` ∙ **Type:** `feed-scoped-id[]` ∙ **Cardinality:** `Optional` +**Path:** / + +List stations that should route to centroid. + +This field contains a list of station ids for which street legs will start/end at the station +centroid instead of the child stops. + +When searching from/to a station the default behaviour is to route from/to any of the stations child +stops. This can cause strange results for stations that have stops spread over a large area. + +For some stations you might instead wish to use the centroid of the station as the +origin/destination. In this case the centroid will be used both for direct street search and for +access/egress street search where the station is used as the start/end of the access/egress. But +transit that starts/ends at the station will work as usual without any additional street leg from/to +the centroid. + + diff --git a/doc/user/Changelog.md b/doc/user/Changelog.md index 0b394015088..2167361dcac 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -7,6 +7,12 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - Extra leg when transferring at the same stop [#5984](https://github.com/opentripplanner/OpenTripPlanner/pull/5984) - Filter vector tiles stops by current service week [#6003](https://github.com/opentripplanner/OpenTripPlanner/pull/6003) +- Add a matcher API for filters in the transit service used for datedServiceJourneyQuery [#5713](https://github.com/opentripplanner/OpenTripPlanner/pull/5713) +- Refetch transit leg with a leg query of GTFS GraphQL API [#6045](https://github.com/opentripplanner/OpenTripPlanner/pull/6045) +- Remove deprecated support for GTFS flex stop areas [#6074](https://github.com/opentripplanner/OpenTripPlanner/pull/6074) +- Don't use elevation data directly for ways with cutting=*, location=underground or indoor=yes tags in the default mapper [#6093](https://github.com/opentripplanner/OpenTripPlanner/pull/6093) +- Un-deprecate GTFS API's `planConnection`, deprecate `plan` [#6110](https://github.com/opentripplanner/OpenTripPlanner/pull/6110) +- Support for routing to Station centroid instead of child stops [#6047](https://github.com/opentripplanner/OpenTripPlanner/pull/6047) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.6.0 (2024-09-18) diff --git a/doc/user/Configuration.md b/doc/user/Configuration.md index 58cc31c0213..bca974f8617 100644 --- a/doc/user/Configuration.md +++ b/doc/user/Configuration.md @@ -2,7 +2,7 @@ NOTE! Part of this document is generated. Make sure you edit the template, not the generated doc. - Template directory is: /doc/templates - - Generated directory is: /docs + - Generated directory is: /doc/user --> # Configuring OpenTripPlanner diff --git a/doc/user/Container-Image.md b/doc/user/Container-Image.md index ed2441f5ac1..0c2a03b2b0f 100644 --- a/doc/user/Container-Image.md +++ b/doc/user/Container-Image.md @@ -20,9 +20,15 @@ curl -L https://download.geofabrik.de/europe/germany/berlin-latest.osm.pbf -o be # download GTFS curl -L https://vbb.de/vbbgtfs -o berlin/vbb-gtfs.zip # build graph and save it onto the host system via the volume -docker run --rm -v "$(pwd)/berlin:/var/opentripplanner" docker.io/opentripplanner/opentripplanner:latest --build --save +docker run --rm \ + -e JAVA_TOOL_OPTIONS='-Xmx8g' \ + -v "$(pwd)/berlin:/var/opentripplanner" \ + docker.io/opentripplanner/opentripplanner:latest --build --save # load and serve graph -docker run -it --rm -p 8080:8080 -v "$(pwd)/berlin:/var/opentripplanner" docker.io/opentripplanner/opentripplanner:latest --load --serve +docker run -it --rm -p 8080:8080 \ + -e JAVA_TOOL_OPTIONS='-Xmx8g' \ + -v "$(pwd)/berlin:/var/opentripplanner" \ + docker.io/opentripplanner/opentripplanner:latest --load --serve ``` Now open [http://localhost:8080](http://localhost:8080) to see your running OTP instance. diff --git a/doc/user/Developers-Guide.md b/doc/user/Developers-Guide.md index 368a12edb62..889cfb0d10b 100644 --- a/doc/user/Developers-Guide.md +++ b/doc/user/Developers-Guide.md @@ -56,7 +56,7 @@ There are several ways to get involved: * Join the [Gitter chat room](https://gitter.im/opentripplanner/OpenTripPlanner) and the [user mailing list](http://groups.google.com/group/opentripplanner-users). -* Fix typos and improve the documentation within the `/docs` directory of the project (details +* Fix typos and improve the documentation within the `/doc/user` directory of the project (details below). * [File a bug or new feature request](http://github.com/openplans/OpenTripPlanner/issues/new). @@ -133,7 +133,7 @@ control to be applied to documentation as well as program source code. All pull how OTP is used or configured should include changes to the documentation alongside code modifications. -The documentation files are in Markdown format and are in the `/docs` directory under the root of +The documentation files are in Markdown format and are in the `/doc/user` directory under the root of the project. On every push to the `dev-2.x` branch the documentation will be rebuilt and deployed as static pages to our subdomain of [Github Pages](https://github.com/opentripplanner/docs). MkDocs is a Python program and should run on any major platform. @@ -143,7 +143,7 @@ how to generate a live local preview of the documentation while you're writing i In short: ``` -$ pip install -r docs/requirements.txt +$ pip install -r doc/user/requirements.txt $ mkdocs serve ``` @@ -205,7 +205,8 @@ so they are a bit easier to maintain that way. The primary audience is also acti that have the code checked out locally. - [Architecture](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/ARCHITECTURE.md) - - [Code Conventions](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/CODE_CONVENTIONS.md) + - [Code Style](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/doc/dev/decisionrecords/Codestyle.md) + - [Naming Conventions](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/doc/dev/decisionrecords/NamingConventions.md) - [Development Decision Records](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/DEVELOPMENT_DECISION_RECORDS.md) diff --git a/doc/user/Frontends.md b/doc/user/Frontends.md index b2f0b9d0b2e..8bb32273149 100644 --- a/doc/user/Frontends.md +++ b/doc/user/Frontends.md @@ -56,14 +56,16 @@ specified in terms of latitude and longitude coordinates. The debug frontend and Many different production OTP frontends exist. Any number of agencies and consultancies may have built new frontends, whether in Javascript or as native mobile apps, without the OpenTripPlanner development team even being aware of them. That said, there are two main Javascript-based web user interfaces that are generally recommended by members of the OTP development team who also work on professional, public-facing OpenTripPlanner deployments: + - The [Digitransit UI](https://github.com/HSLdevcom/digitransit-ui), part of the Finnish Digitransit project. - The [OpenTripPlanner React UI](https://github.com/opentripplanner/otp-react-redux), developed and maintained by [Arcadis IBI](https://www.ibigroup.com)'s [transit routing team](https://www.ibigroup.com/ibi-products/transit-routing/). **Digitransit** is an open-source public transportation project, originally created in Finland to replace existing nationwide and regional journey planning solutions in 2014. Digitransit has since been used around the world in other projects, for example in Germany. It is a joint project of Helsinki Regional Transport Authority (HSL), Fintraffic, and Waltti Solutions. **Arcadis IBI** has for several years actively developed, deployed, and maintained instances of the React-based OTP UI, systematically contributing their improvements back to the original repositories under the OpenTripPlanner organization: -- https://github.com/opentripplanner/otp-ui -- https://github.com/opentripplanner/otp-react-redux + +- [https://github.com/opentripplanner/otp-ui](https://github.com/opentripplanner/otp-ui) +- [https://github.com/opentripplanner/otp-react-redux](https://github.com/opentripplanner/otp-react-redux) Both major frontend projects mentioned above support internationalization and have several translations already available. diff --git a/doc/user/Netex-Norway.md b/doc/user/Netex-Norway.md deleted file mode 100644 index 0a447237592..00000000000 --- a/doc/user/Netex-Norway.md +++ /dev/null @@ -1,148 +0,0 @@ -# Using European Data Standards - -## Building with Netex Data - -One important new feature of OTP2 is the ability to -load [Netex](https://en.wikipedia.org/wiki/NeTEx) data. Netex is a -European [specification for transit data exchange](http://netex-cen.eu), comparable in purpose to -GTFS but broader in scope. An EU directive aims to have all EU countries sharing Netex data by the -end of 2019. - -Different countries are currently using different incompatible "profiles" of Netex, but an effort is -underway to converge on a single European standard profile. This is based in large part on the -Norwegian profile, and Norway's national passenger information and ticketing agency Entur has -contributed the OTP2 Netex loading code. Therefore if you'd like to try loading Netex data, Norway -is a good place to start. - -The Norwegian Netex data can be downloaded from -the [Entur developer pages](https://developer.entur.org/pages-intro-files). There is a column of -Netex download links partway down the page, and the first row is for all of Norway. - -Full OSM data for Norway can be downloaded from -the [Geofabrik Norway downloads page](http://download.geofabrik.de/europe/norway.html). Get -the `norway-latest.osm.pbf` file, which can then be filtered to remove buildings and other unused -data before loading into OTP using a command like the one below. This filtering step can be skipped -if you don't have the necessary Osmium tools installed. - -`osmium tags-filter norway-latest.osm.pbf w/highway w/public_transport=platform w/railway=platform w/park_ride=yes r/type=restriction -o norway-filtered.osm.pbf -f pbf,add_metadata=false,pbf_dense_nodes=true` - -Be sure to move the original unfiltered file out of your graph inputs directory (or rename it with a -suffix like `norway-latest.osm.pbf.ignore`) otherwise OTP2 will try to include both the filtered and -unfiltered OSM data in your graph. - -The `build-config.json` for a Norwegian graph using Netex data looks like this: - -```json -{ - "areaVisibility": true, - "platformEntriesLinking": true, - "islandWithoutStopsMaxSize": 5, - "islandWithStopsMaxSize": 5, - "dataImportReport": true, - "netexDefaults" : { - "moduleFilePattern" : ".*-netex\\.zip", - "sharedFilePattern": "_stops.xml", - "sharedGroupFilePattern": "_(\\w{3})(_flexible)?_shared_data.xml", - "groupFilePattern": "(\\w{3})_.*\\.xml", - "feedId": "EN", - "ferryIdsNotAllowedForBicycle": [ - "NYC:Line:1", - "NYC:Line:012fc5c4-131b-4dfc-8160-4e49136e531a", - "NYC:Line:8bfef12a-ac98-4376-8a2a-eb5a336d107b" - ] - }, - "osm": [ - { - "source": "norway-latest.osm.pbf", - "osmTagMapping": "norway", - "timeZone": "Europe/Oslo" - } - ] -} -``` - -Note the special section specifying how to find Netex XML files within the single ZIP archive you -downloaded. - -Once you have the graph inputs (the OSM PBF file, the Netex ZIP file, and the `build-config.json`) -saved together in a directory, you can instruct OTP2 to build a graph from these inputs: - -`java -Xmx10G otp2.jar --build --save /path/to/graph/inputs` - -This should produce a file `graph.obj` in the same directory as your inputs. Building this Norway -graph takes approximately 16 minutes (without elevation data, as configured above), and can be done -within 10GB of heap memory (JVM switch `-Xmx10G`). Increasing that to 12 or 14GB might speed it up a -bit if you have the space. The Graph file it produces is just under 600MB. The server will take -about 30 seconds to load this Graph and start up, and will consume about 4GB of heap memory under -light use. - -You can then start up an OTP server with a command like this: - -`java -Xmx6G otp2.jar --load /path/to/graph` - -Once the server is started up, go to `http://localhost:8080` in a browser to try out your server -using OTP's built in testing web client. Try some long trips like Oslo to Bergen and see if you can -get long distance trains and flights as alternatives. You might need to increase the walking limit -above its very low default value. - -## Adding SIRI Real-time Data - -Another important feature in OTP2 is the ability to -use [SIRI real-time data](https://en.wikipedia.org/wiki/Service_Interface_for_Real_Time_Information). -Within the EU data standards, SIRI is analogous to GTFS-RT: a way to apply real-time updates on top -of schedule data. While technically a distinct specification from Netex, both Netex and SIRI use the -Transmodel vocabulary, allowing SIRI messages to reference entities in Netex schedule data. Like -GTFS-RT, SIRI is consumed by OTP2 using "graph updaters" which are configured in -the `router-config.json` file, which is placed in the same directory as the `graph.obj` file and -loaded at server startup. - -```json -{ - "updaters": [ - { - "type": "siri-sx-updater", - "frequency": "1m", - "url": "https://api.example.com/siri", - "feedId": "siri-sx", - "blockReadinessUntilInitialized": true - }, - { - "type": "siri-et-updater", - "frequency": "20s", - "previewIntervalMinutes": 180, - "url": "https://api.example.com/siri", - "feedId": "siri-et", - "blockReadinessUntilInitialized": true - }, - { - "type": "siri-vm-updater", - "frequency": "1m", - "url": "https://api.example.com/siri", - "feedId": "siri-vm", - "blockReadinessUntilInitialized": true - }, - { - "type": "raptor-transit-layer", - "updateIntervalSeconds": 20 - } - ] -} -``` - -The first three updaters fetch three different kinds of SIRI data: - -- Situation Exchange (SX, text notices analogous to GTFS-RT Alerts) -- Estimated Timetable (ET, predicted arrival times analogous to GTFS-RT TripUpdates) -- Vehicle Monitoring (VM, location and status of vehicles analogous to GTFS-RT VehiclePositions) - -These updaters can handle differential updates, but they use a polling approach rather than the -message-oriented streaming approach of the GTFS-RT Websocket updater. The server keeps track of -clients, sending only the things that have changed since the last polling operation. - -Note that between these SIRI updaters and the GTFS-RT Websocket updater, we now have both polling -and streaming examples of GTFS-RT "incrementality" semantics, so should be able to finalize that -part of the specification. - -The final updater regularly performs a copy of the real-time data into a format suitable for use by -OTP2's new Raptor router. Without this updater the real-time data will be received and cataloged, but -not visible to the router. diff --git a/doc/user/Netex-Tutorial.md b/doc/user/Netex-Tutorial.md new file mode 100644 index 00000000000..3021f9c9674 --- /dev/null +++ b/doc/user/Netex-Tutorial.md @@ -0,0 +1,133 @@ +# NeTEx & SIRI tutorial + +One important new feature of OTP2 is the ability to +load [NeTEx](https://en.wikipedia.org/wiki/NeTEx) and [SIRI](https://en.wikipedia.org/wiki/Service_Interface_for_Real_Time_Information) +data. NeTEx is a European [specification for transit data exchange](http://netex-cen.eu), comparable in purpose to +GTFS but broader in scope. + +First of all, you need to download a [bundled jar of OTP](Getting-OTP.md). + +Secondly, you will use the [Norwegian NeTEx file](https://developer.entur.org/pages-intro-files) as +well as the [Norwegian OSM data](http://download.geofabrik.de/europe/norway.html), but OTP can download the NeTEx one for you. + +## Configuring the build + +Create a working directory and place the OTP jar file in it and call it `otp.jar.` + +Since we download the OSM data from a free source, we don't want to put undue stress on the server. +Therefore we download it before building the graph, not during. + +``` +curl https://download.geofabrik.de/europe/norway-latest.osm.pbf -o norway.osm.pbf +``` + +Now create a file called `build-config.json` in the same folder and fill it with the following +content: + + + + +```JSON +// build-config.json +{ + "transitFeeds" : [ + { + "type" : "netex", + "feedId" : "NO", + "source" : "https://storage.googleapis.com/marduk-production/outbound/netex/rb_norway-aggregated-netex.zip", + "sharedFilePattern" : "_stops.xml", + "sharedGroupFilePattern" : "_(\\w{3})(_flexible)?_shared_data.xml", + "groupFilePattern" : "(\\w{3})_.*\\.xml" + } + ], + "osm" : [ + { + "source" : "norway.osm.pbf", + "osmTagMapping" : "norway", + "timeZone" : "Europe/Oslo" + } + ], + "osmCacheDataInMem" : true +} +``` + + + +Note the special section specifying how to find NeTEx XML files within the single ZIP archive that +OTP downloads. + +Now you can instruct OTP to build a graph from this configuration file: + +`java -Xmx16G -jar otp.jar --build --save .` + +This should produce a file `graph.obj` in the same directory as your `build-config.json`. + +Building the Norway graph requires downloading about 250MB of input data so stay patient at the beginning +particularly on a slow internet connection. +The actual build takes approximately 10 minutes (without elevation data, as is configured above), +and can be done within 16GB of heap memory (JVM switch `-Xmx16G`). The Graph file it produces is +about 1.1 GB. The server will take about 30 seconds to load this graph and start up, and will +consume about 6GB of heap memory under light use. + +You can then start up an OTP server with a command like this: + +`java -Xmx6G -jar otp.jar --load .` + +Once the server is started up, go to `http://localhost:8080` in a browser to try out your server +using OTP's built in testing web client. Try some long trips like Oslo to Bergen and see if you can +get long distance trains and flights as alternatives. You might need to increase the walking limit +above its very low default value. + +## Adding SIRI real time Data + +Another important feature in OTP version 2 is the ability to +use [SIRI real-time data](https://en.wikipedia.org/wiki/Service_Interface_for_Real_Time_Information). +Within the EU data standards, SIRI is analogous to GTFS-RT: a way to apply real-time updates on top +of schedule data. While technically a distinct specification from NeTEx, both NeTEx and SIRI use the +Transmodel vocabulary, allowing SIRI messages to reference entities in NeTEx schedule data. Like +GTFS-RT, SIRI is consumed by OTP2 using "graph updaters" which are configured in +the `router-config.json` file, which is placed in the same directory as the `graph.obj` file and +loaded at server startup. + + + + +```JSON +// router-config.json +{ + "updaters" : [ + { + "type" : "siri-sx-updater", + "frequency" : "1m", + "url" : "https://api.entur.io/realtime/v1/services", + "feedId" : "NO", + "blockReadinessUntilInitialized" : true + }, + { + "type" : "siri-et-updater", + "frequency" : "1m", + "previewInterval" : "1h30m", + "url" : "https://api.entur.io/realtime/v1/services", + "feedId" : "NO", + "blockReadinessUntilInitialized" : true + } + ] +} +``` + + + +After saving the file in the working directory, restart OTP. + +The updaters fetch two different kinds of SIRI data: + +- Situation Exchange (SX, text notices analogous to GTFS-RT Alerts) +- Estimated Timetable (ET, predicted arrival times analogous to GTFS-RT TripUpdates) + +These updaters can handle differential updates, but they use a polling approach rather than the +message-oriented streaming approach of the GTFS-RT Websocket updater. The server keeps track of +clients, sending only the things that have changed since the last polling operation. + +Note that between these SIRI updaters and the GTFS-RT Websocket updater, we now have both polling +and streaming examples of GTFS-RT "incrementality" semantics, so should be able to finalize that +part of the specification. \ No newline at end of file diff --git a/doc/user/RouteRequest.md b/doc/user/RouteRequest.md index 674ab238888..c00502b2726 100644 --- a/doc/user/RouteRequest.md +++ b/doc/user/RouteRequest.md @@ -2,7 +2,7 @@ NOTE! Part of this document is generated. Make sure you edit the template, not the generated doc. - Template directory is: /doc/templates - - Generated directory is: /docs + - Generated directory is: /doc/user --> # Route Request diff --git a/doc/user/RouterConfiguration.md b/doc/user/RouterConfiguration.md index 4e565cfe17d..711eca3fb87 100644 --- a/doc/user/RouterConfiguration.md +++ b/doc/user/RouterConfiguration.md @@ -2,7 +2,7 @@ NOTE! Part of this document is generated. Make sure you edit the template, not the generated doc. - Template directory is: /doc/templates - - Generated directory is: /docs + - Generated directory is: /doc/user --> # Router configuration @@ -778,12 +778,6 @@ Used to group requests when monitoring OTP. "Authorization" : "${BIKELY_AUTHORIZATION}" } }, - { - "type" : "vehicle-parking", - "feedId" : "noi", - "sourceType" : "noi-open-data-hub", - "url" : "https://parking.otp.opendatahub.com/parking/all.json" - }, { "type" : "stop-time-updater", "frequency" : "1m", diff --git a/doc/user/UpdaterConfig.md b/doc/user/UpdaterConfig.md index f3a0d982e68..cc5e1d75901 100644 --- a/doc/user/UpdaterConfig.md +++ b/doc/user/UpdaterConfig.md @@ -2,7 +2,7 @@ NOTE! Part of this document is generated. Make sure you edit the template, not the generated doc. - Template directory is: /doc/templates - - Generated directory is: /docs + - Generated directory is: /doc/user --> @@ -92,20 +92,20 @@ The information is downloaded in a single HTTP request and polled regularly. | Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | |-----------------------------------------------------------------------|:---------------:|----------------------------------------------------------------------------|:----------:|----------------------|:-----:| | type = "stop-time-updater" | `enum` | The type of the updater. | *Required* | | 1.5 | -| [backwardsDelayPropagationType](#u__6__backwardsDelayPropagationType) | `enum` | How backwards propagation should be handled. | *Optional* | `"required-no-data"` | 2.2 | +| [backwardsDelayPropagationType](#u__5__backwardsDelayPropagationType) | `enum` | How backwards propagation should be handled. | *Optional* | `"required-no-data"` | 2.2 | | feedId | `string` | Which feed the updates apply to. | *Required* | | 1.5 | | frequency | `duration` | How often the data should be downloaded. | *Optional* | `"PT1M"` | 1.5 | | fuzzyTripMatching | `boolean` | If the trips should be matched fuzzily. | *Optional* | `false` | 1.5 | -| [url](#u__6__url) | `string` | The URL of the GTFS-RT resource. | *Required* | | 1.5 | -| [headers](#u__6__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | +| [url](#u__5__url) | `string` | The URL of the GTFS-RT resource. | *Required* | | 1.5 | +| [headers](#u__5__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | ##### Parameter details -

backwardsDelayPropagationType

+

backwardsDelayPropagationType

**Since version:** `2.2` ∙ **Type:** `enum` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"required-no-data"` -**Path:** /updaters/[6] +**Path:** /updaters/[5] **Enum values:** `required-no-data` | `required` | `always` How backwards propagation should be handled. @@ -124,19 +124,19 @@ How backwards propagation should be handled. The updated times are exposed through APIs. -

url

+

url

**Since version:** `1.5` ∙ **Type:** `string` ∙ **Cardinality:** `Required` -**Path:** /updaters/[6] +**Path:** /updaters/[5] The URL of the GTFS-RT resource. `file:` URLs are also supported if you want to read a file from the local disk. -

headers

+

headers

**Since version:** `2.3` ∙ **Type:** `map of string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[6] +**Path:** /updaters/[5] HTTP headers to add to the request. Any header key, value can be inserted. @@ -178,7 +178,7 @@ This system powers the realtime updates in Helsinki and more information can be | Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | |-----------------------------------------------------------------------|:---------:|----------------------------------------------|:----------:|----------------------|:-----:| | type = "mqtt-gtfs-rt-updater" | `enum` | The type of the updater. | *Required* | | 1.5 | -| [backwardsDelayPropagationType](#u__7__backwardsDelayPropagationType) | `enum` | How backwards propagation should be handled. | *Optional* | `"required-no-data"` | 2.2 | +| [backwardsDelayPropagationType](#u__6__backwardsDelayPropagationType) | `enum` | How backwards propagation should be handled. | *Optional* | `"required-no-data"` | 2.2 | | feedId | `string` | The feed id to apply the updates to. | *Required* | | 2.0 | | fuzzyTripMatching | `boolean` | Whether to match trips fuzzily. | *Optional* | `false` | 2.0 | | qos | `integer` | QOS level. | *Optional* | `0` | 2.0 | @@ -188,10 +188,10 @@ This system powers the realtime updates in Helsinki and more information can be ##### Parameter details -

backwardsDelayPropagationType

+

backwardsDelayPropagationType

**Since version:** `2.2` ∙ **Type:** `enum` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"required-no-data"` -**Path:** /updaters/[7] +**Path:** /updaters/[6] **Enum values:** `required-no-data` | `required` | `always` How backwards propagation should be handled. @@ -247,24 +247,24 @@ The information is downloaded in a single HTTP request and polled regularly. | frequency | `duration` | How often the positions should be updated. | *Optional* | `"PT1M"` | 2.2 | | fuzzyTripMatching | `boolean` | Whether to match trips fuzzily. | *Optional* | `false` | 2.5 | | url | `uri` | The URL of GTFS-RT protobuf HTTP resource to download the positions from. | *Required* | | 2.2 | -| [features](#u__8__features) | `enum set` | Which features of GTFS RT vehicle positions should be loaded into OTP. | *Optional* | | 2.5 | -| [headers](#u__8__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | +| [features](#u__7__features) | `enum set` | Which features of GTFS RT vehicle positions should be loaded into OTP. | *Optional* | | 2.5 | +| [headers](#u__7__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | ##### Parameter details -

features

+

features

**Since version:** `2.5` ∙ **Type:** `enum set` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[8] +**Path:** /updaters/[7] **Enum values:** `position` | `stop-position` | `occupancy` Which features of GTFS RT vehicle positions should be loaded into OTP. -

headers

+

headers

**Since version:** `2.3` ∙ **Type:** `map of string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[8] +**Path:** /updaters/[7] HTTP headers to add to the request. Any header key, value can be inserted. diff --git a/doc/user/apis/GraphQL-Tutorial.md b/doc/user/apis/GraphQL-Tutorial.md index d65fbc144ba..5c4b59e864e 100644 --- a/doc/user/apis/GraphQL-Tutorial.md +++ b/doc/user/apis/GraphQL-Tutorial.md @@ -2,7 +2,7 @@ NOTE! Part of this document is generated. Make sure you edit the template, not the generated doc. - Template directory is: /doc/templates - - Generated directory is: /docs + - Generated directory is: /doc/user --> # GraphQL tutorial @@ -70,53 +70,61 @@ Most people want to get routing results out of OTP, so lets see the query for th ```graphql { - plan( - # these coordinates are in Portland, change this to YOUR origin - from: { lat: 45.5552, lon: -122.6534 } - # these coordinates are in Portland, change this to YOUR destination - to: { lat: 45.4908, lon: -122.5519 } + planConnection( + origin: { + # these coordinates are in Portland, change this to YOUR origin + location: { coordinate: { latitude: 45.5552, longitude: -122.6534 } } + } + destination: { + # these coordinates are in Portland, change this to YOUR destination + location: { coordinate: { latitude: 45.4908, longitude: -122.5519 } } + } # use the correct date and time of your request - date: "2023-02-15" - time: "11:37" + dateTime: { earliestDeparture: "2023-06-13T14:30-07:00" } # choose the transport modes you need - transportModes: [{ mode: WALK }, { mode: TRANSIT }] + modes: { + direct: [WALK] + transit: { transit: [{ mode: BUS }, { mode: RAIL }] } + } ) { - itineraries { - start - end - legs { - mode - from { - name - lat - lon - departure { - scheduledTime - estimated { - time - delay + edges { + node { + start + end + legs { + mode + from { + name + lat + lon + departure { + scheduledTime + estimated { + time + delay + } } } - } - to { - name - lat - lon - arrival { - scheduledTime - estimated { - time - delay + to { + name + lat + lon + arrival { + scheduledTime + estimated { + time + delay + } } } - } - route { - gtfsId - longName - shortName - } - legGeometry { - points + route { + gtfsId + longName + shortName + } + legGeometry { + points + } } } } diff --git a/doc/user/css/magidoc-overrides.css b/doc/user/css/magidoc-overrides.css new file mode 100644 index 00000000000..dbf0cb2523d --- /dev/null +++ b/doc/user/css/magidoc-overrides.css @@ -0,0 +1,11 @@ +/* + * This file contains custom CSS overrides for the GraphQL documentation available at + * https://docs.opentripplanner.org/api/dev-2.x/graphql-gtfs/ + */ + +/* + * Hide the deprecated queries from the left hand navigation panel. + */ +nav ul li .deprecated { + display: none; +} diff --git a/doc/user/features-explained/Netex-Siri-Compatibility.md b/doc/user/features-explained/Netex-Siri-Compatibility.md new file mode 100644 index 00000000000..731011be8e0 --- /dev/null +++ b/doc/user/features-explained/Netex-Siri-Compatibility.md @@ -0,0 +1,36 @@ +# NeTEx and SIRI compatibility + +NeTEx and SIRI are two European public transport data specifications that are comparable to GTFS and +GTFS-RT but have a broader scope. Support for both was added by Entur in OTP version 2 and you can +find examples of those in their [examples repo](https://github.com/entur/profile-examples). + +## Profiles + +### Nordic profile + +Different countries are currently using different incompatible "profiles" of NeTEx, but an effort is +underway to converge on a single European standard profile. This is based in large part on the +Nordic profile used by Entur. + +The Nordic profile is the only profile that has been thoroughly tested in production in OTP and is +used in Norway, Finland and Sweden. + +### EPIP + +The [European Passenger Information Profile](http://netex.uk/netex/doc/2019.05.07-v1.1_FinalDraft/prCEN_TS_16614-PI_Profile_FV_%28E%29-2019-Final-Draft-v3.pdf) +is an attempt to unify other country profiles and support in OTP is adequate, but it is difficult +to tell how much of EPIP is supported since it is a very large profile. The current status +of the support is tracked on [Github](https://github.com/opentripplanner/OpenTripPlanner/issues/3640). + +Sometimes it is difficult to tell if a file conforms to EPIP so to find out, you can run the following +commands: + +``` +git clone git@github.com:NeTEx-CEN/NeTEx-Profile-EPIP.git +xmllint --noout --schema NeTEx-Profile-EPIP/NeTEx_publication_EPIP.xsd your-filename.xml +``` + +### Other profiles + +It is the goal of the community to support both the Nordic profile and EPIP. If you have another +profile, we encourage to get in touch with the community to find a way forward. \ No newline at end of file diff --git a/doc/user/requirements.txt b/doc/user/requirements.txt index 4681f78c93f..ee0eb31d65c 100644 --- a/doc/user/requirements.txt +++ b/doc/user/requirements.txt @@ -1,4 +1,4 @@ -mkdocs==1.6.0 -mkdocs-material==9.5.27 +mkdocs==1.6.1 +mkdocs-material==9.5.39 mike@git+https://github.com/jimporter/mike.git@f0522f245e64687dd18384fbd86b721175711474 mkdocs-no-sitemap-plugin==0.0.1 diff --git a/doc/user/sandbox/StopConsolidation.md b/doc/user/sandbox/StopConsolidation.md index b36429b1f60..d0e18a9ce30 100644 --- a/doc/user/sandbox/StopConsolidation.md +++ b/doc/user/sandbox/StopConsolidation.md @@ -2,7 +2,7 @@ NOTE! Part of this document is generated. Make sure you edit the template, not the generated doc. - Template directory is: /doc/templates - - Generated directory is: /docs + - Generated directory is: /doc/user --> # Stop consolidation diff --git a/doc/user/sandbox/VehicleParking.md b/doc/user/sandbox/VehicleParking.md index db057bd9dbd..ccd56fc238c 100644 --- a/doc/user/sandbox/VehicleParking.md +++ b/doc/user/sandbox/VehicleParking.md @@ -3,7 +3,7 @@ ## Contact Info - For HSL Park and Ride updater: Digitransit team, HSL, Helsinki, Finland -- For Bikely, NOI and Bikeep updater: Leonard Ehrenfried, [mail@leonard.io](mailto:mail@leonard.io) +- For Bikely, Bikeep and SIRI-FM updater: Leonard Ehrenfried, [mail@leonard.io](mailto:mail@leonard.io) ## Documentation @@ -16,7 +16,7 @@ Currently contains the following updaters: - [HSL Park and Ride](https://p.hsl.fi/docs/index.html) - [ParkAPI](https://github.com/offenesdresden/ParkAPI) - [Bikely](https://www.safebikely.com/) -- [NOI Open Data Hub](https://opendatahub.com/) +- SIRI-FM ### Configuration @@ -61,7 +61,7 @@ This will end up in the API responses as the feed id of the parking lot. **Since version:** `2.2` ∙ **Type:** `enum` ∙ **Cardinality:** `Required` **Path:** /updaters/[2] -**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub` | `bikeep` | `siri-fm` +**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `bikeep` | `siri-fm` The source of the vehicle updates. @@ -131,7 +131,7 @@ This will end up in the API responses as the feed id of the parking lot. **Since version:** `2.2` ∙ **Type:** `enum` ∙ **Cardinality:** `Required` **Path:** /updaters/[3] -**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub` | `bikeep` | `siri-fm` +**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `bikeep` | `siri-fm` The source of the vehicle updates. @@ -216,7 +216,7 @@ This will end up in the API responses as the feed id of the parking lot. **Since version:** `2.2` ∙ **Type:** `enum` ∙ **Cardinality:** `Required` **Path:** /updaters/[4] -**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub` | `bikeep` | `siri-fm` +**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `bikeep` | `siri-fm` The source of the vehicle updates. @@ -251,67 +251,6 @@ HTTP headers to add to the request. Any header key, value can be inserted. -## NOI Open Data Hub - - - - -| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | -|---------------------------------|:---------------:|------------------------------------------------------------------------------|:----------:|---------------|:-----:| -| type = "vehicle-parking" | `enum` | The type of the updater. | *Required* | | 1.5 | -| [feedId](#u__5__feedId) | `string` | The id of the data source, which will be the prefix of the parking lot's id. | *Required* | | 2.2 | -| frequency | `duration` | How often to update the source. | *Optional* | `"PT1M"` | 2.6 | -| [sourceType](#u__5__sourceType) | `enum` | The source of the vehicle updates. | *Required* | | 2.2 | -| url | `uri` | URL of the locations endpoint. | *Required* | | 2.6 | -| [headers](#u__5__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.6 | - - -#### Details - -

feedId

- -**Since version:** `2.2` ∙ **Type:** `string` ∙ **Cardinality:** `Required` -**Path:** /updaters/[5] - -The id of the data source, which will be the prefix of the parking lot's id. - -This will end up in the API responses as the feed id of the parking lot. - -

sourceType

- -**Since version:** `2.2` ∙ **Type:** `enum` ∙ **Cardinality:** `Required` -**Path:** /updaters/[5] -**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub` | `bikeep` | `siri-fm` - -The source of the vehicle updates. - -

headers

- -**Since version:** `2.6` ∙ **Type:** `map of string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[5] - -HTTP headers to add to the request. Any header key, value can be inserted. - - - -##### Example configuration - -```JSON -// router-config.json -{ - "updaters" : [ - { - "type" : "vehicle-parking", - "feedId" : "noi", - "sourceType" : "noi-open-data-hub", - "url" : "https://parking.otp.opendatahub.com/parking/all.json" - } - ] -} -``` - - - ## Bikeep @@ -320,36 +259,36 @@ HTTP headers to add to the request. Any header key, value can be inserted. | Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | |----------------------------------|:---------------:|------------------------------------------------------------------------------|:----------:|---------------|:-----:| | type = "vehicle-parking" | `enum` | The type of the updater. | *Required* | | 1.5 | -| [feedId](#u__14__feedId) | `string` | The id of the data source, which will be the prefix of the parking lot's id. | *Required* | | 2.2 | +| [feedId](#u__13__feedId) | `string` | The id of the data source, which will be the prefix of the parking lot's id. | *Required* | | 2.2 | | frequency | `duration` | How often to update the source. | *Optional* | `"PT1M"` | 2.6 | -| [sourceType](#u__14__sourceType) | `enum` | The source of the vehicle updates. | *Required* | | 2.2 | +| [sourceType](#u__13__sourceType) | `enum` | The source of the vehicle updates. | *Required* | | 2.2 | | url | `uri` | URL of the locations endpoint. | *Required* | | 2.6 | -| [headers](#u__14__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.6 | +| [headers](#u__13__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.6 | #### Details -

feedId

+

feedId

**Since version:** `2.2` ∙ **Type:** `string` ∙ **Cardinality:** `Required` -**Path:** /updaters/[14] +**Path:** /updaters/[13] The id of the data source, which will be the prefix of the parking lot's id. This will end up in the API responses as the feed id of the parking lot. -

sourceType

+

sourceType

**Since version:** `2.2` ∙ **Type:** `enum` ∙ **Cardinality:** `Required` -**Path:** /updaters/[14] -**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub` | `bikeep` | `siri-fm` +**Path:** /updaters/[13] +**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `bikeep` | `siri-fm` The source of the vehicle updates. -

headers

+

headers

**Since version:** `2.6` ∙ **Type:** `map of string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[14] +**Path:** /updaters/[13] HTTP headers to add to the request. Any header key, value can be inserted. @@ -387,36 +326,36 @@ which requires SIRI 2.1. | Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | |----------------------------------|:---------------:|------------------------------------------------------------------------------|:----------:|---------------|:-----:| | type = "vehicle-parking" | `enum` | The type of the updater. | *Required* | | 1.5 | -| [feedId](#u__15__feedId) | `string` | The id of the data source, which will be the prefix of the parking lot's id. | *Required* | | 2.2 | +| [feedId](#u__14__feedId) | `string` | The id of the data source, which will be the prefix of the parking lot's id. | *Required* | | 2.2 | | frequency | `duration` | How often to update the source. | *Optional* | `"PT1M"` | 2.6 | -| [sourceType](#u__15__sourceType) | `enum` | The source of the vehicle updates. | *Required* | | 2.2 | -| [url](#u__15__url) | `uri` | URL of the SIRI-FM Light endpoint. | *Required* | | 2.6 | -| [headers](#u__15__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.6 | +| [sourceType](#u__14__sourceType) | `enum` | The source of the vehicle updates. | *Required* | | 2.2 | +| [url](#u__14__url) | `uri` | URL of the SIRI-FM Light endpoint. | *Required* | | 2.6 | +| [headers](#u__14__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.6 | #### Details -

feedId

+

feedId

**Since version:** `2.2` ∙ **Type:** `string` ∙ **Cardinality:** `Required` -**Path:** /updaters/[15] +**Path:** /updaters/[14] The id of the data source, which will be the prefix of the parking lot's id. This will end up in the API responses as the feed id of the parking lot. -

sourceType

+

sourceType

**Since version:** `2.2` ∙ **Type:** `enum` ∙ **Cardinality:** `Required` -**Path:** /updaters/[15] -**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub` | `bikeep` | `siri-fm` +**Path:** /updaters/[14] +**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `bikeep` | `siri-fm` The source of the vehicle updates. -

url

+

url

**Since version:** `2.6` ∙ **Type:** `uri` ∙ **Cardinality:** `Required` -**Path:** /updaters/[15] +**Path:** /updaters/[14] URL of the SIRI-FM Light endpoint. @@ -427,10 +366,10 @@ The contents must also conform to the [Italian SIRI profile](https://github.com/ which requires SIRI 2.1. -

headers

+

headers

**Since version:** `2.6` ∙ **Type:** `map of string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[15] +**Path:** /updaters/[14] HTTP headers to add to the request. Any header key, value can be inserted. diff --git a/doc/user/sandbox/siri/SiriAzureUpdater.md b/doc/user/sandbox/siri/SiriAzureUpdater.md index 7b29e802f21..c8e7f4d9255 100644 --- a/doc/user/sandbox/siri/SiriAzureUpdater.md +++ b/doc/user/sandbox/siri/SiriAzureUpdater.md @@ -25,14 +25,14 @@ To enable the SIRI updater you need to add it to the updaters section of the `ro | Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | |------------------------------------------------------------|:----------:|------------------------------------------------------------------|:----------:|---------------------|:-----:| | type = "siri-azure-et-updater" | `enum` | The type of the updater. | *Required* | | 1.5 | -| [authenticationType](#u__12__authenticationType) | `enum` | Which authentication type to use | *Optional* | `"sharedaccesskey"` | 2.5 | +| [authenticationType](#u__11__authenticationType) | `enum` | Which authentication type to use | *Optional* | `"sharedaccesskey"` | 2.5 | | autoDeleteOnIdle | `duration` | The time after which an inactive subscription is removed. | *Optional* | `"PT1H"` | 2.5 | -| [customMidnight](#u__12__customMidnight) | `integer` | Time on which time breaks into new day. | *Optional* | `0` | 2.2 | +| [customMidnight](#u__11__customMidnight) | `integer` | Time on which time breaks into new day. | *Optional* | `0` | 2.2 | | feedId | `string` | The ID of the feed to apply the updates to. | *Optional* | | 2.2 | -| [fullyQualifiedNamespace](#u__12__fullyQualifiedNamespace) | `string` | Service Bus fully qualified namespace used for authentication. | *Optional* | | 2.5 | +| [fullyQualifiedNamespace](#u__11__fullyQualifiedNamespace) | `string` | Service Bus fully qualified namespace used for authentication. | *Optional* | | 2.5 | | fuzzyTripMatching | `boolean` | Whether to apply fuzzyTripMatching on the updates | *Optional* | `false` | 2.2 | | prefetchCount | `integer` | The number of messages to fetch from the subscription at a time. | *Optional* | `10` | 2.5 | -| [servicebus-url](#u__12__servicebus_url) | `string` | Service Bus connection used for authentication. | *Optional* | | 2.2 | +| [servicebus-url](#u__11__servicebus_url) | `string` | Service Bus connection used for authentication. | *Optional* | | 2.2 | | topic | `string` | Service Bus topic to connect to. | *Optional* | | 2.2 | | history | `object` | Configuration for fetching historical data on startup | *Optional* | | 2.2 | |    fromDateTime | `string` | Datetime boundary for historical data | *Optional* | `"-P1D"` | 2.2 | @@ -42,36 +42,36 @@ To enable the SIRI updater you need to add it to the updaters section of the `ro ##### Parameter details -

authenticationType

+

authenticationType

**Since version:** `2.5` ∙ **Type:** `enum` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"sharedaccesskey"` -**Path:** /updaters/[12] +**Path:** /updaters/[11] **Enum values:** `sharedaccesskey` | `federatedidentity` Which authentication type to use -

customMidnight

+

customMidnight

**Since version:** `2.2` ∙ **Type:** `integer` ∙ **Cardinality:** `Optional` ∙ **Default value:** `0` -**Path:** /updaters/[12] +**Path:** /updaters/[11] Time on which time breaks into new day. It is common that operating day date breaks a little bit later than midnight so that the switch happens when traffic is at the lowest point. Parameter uses 24-hour format. If the switch happens on 4 am then set this field to 4. -

fullyQualifiedNamespace

+

fullyQualifiedNamespace

**Since version:** `2.5` ∙ **Type:** `string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[12] +**Path:** /updaters/[11] Service Bus fully qualified namespace used for authentication. Has to be present for authenticationMethod FederatedIdentity. -

servicebus-url

+

servicebus-url

**Since version:** `2.2` ∙ **Type:** `string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[12] +**Path:** /updaters/[11] Service Bus connection used for authentication. @@ -113,14 +113,14 @@ Has to be present for authenticationMethod SharedAccessKey. This should be Prima | Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | |------------------------------------------------------------|:----------:|------------------------------------------------------------------|:----------:|---------------------|:-----:| | type = "siri-azure-sx-updater" | `enum` | The type of the updater. | *Required* | | 1.5 | -| [authenticationType](#u__11__authenticationType) | `enum` | Which authentication type to use | *Optional* | `"sharedaccesskey"` | 2.5 | +| [authenticationType](#u__10__authenticationType) | `enum` | Which authentication type to use | *Optional* | `"sharedaccesskey"` | 2.5 | | autoDeleteOnIdle | `duration` | The time after which an inactive subscription is removed. | *Optional* | `"PT1H"` | 2.5 | -| [customMidnight](#u__11__customMidnight) | `integer` | Time on which time breaks into new day. | *Optional* | `0` | 2.2 | +| [customMidnight](#u__10__customMidnight) | `integer` | Time on which time breaks into new day. | *Optional* | `0` | 2.2 | | feedId | `string` | The ID of the feed to apply the updates to. | *Optional* | | 2.2 | -| [fullyQualifiedNamespace](#u__11__fullyQualifiedNamespace) | `string` | Service Bus fully qualified namespace used for authentication. | *Optional* | | 2.5 | +| [fullyQualifiedNamespace](#u__10__fullyQualifiedNamespace) | `string` | Service Bus fully qualified namespace used for authentication. | *Optional* | | 2.5 | | fuzzyTripMatching | `boolean` | Whether to apply fuzzyTripMatching on the updates | *Optional* | `false` | 2.2 | | prefetchCount | `integer` | The number of messages to fetch from the subscription at a time. | *Optional* | `10` | 2.5 | -| [servicebus-url](#u__11__servicebus_url) | `string` | Service Bus connection used for authentication. | *Optional* | | 2.2 | +| [servicebus-url](#u__10__servicebus_url) | `string` | Service Bus connection used for authentication. | *Optional* | | 2.2 | | topic | `string` | Service Bus topic to connect to. | *Optional* | | 2.2 | | history | `object` | Configuration for fetching historical data on startup | *Optional* | | 2.2 | |    fromDateTime | `string` | Datetime boundary for historical data. | *Optional* | `"-P1D"` | 2.2 | @@ -131,36 +131,36 @@ Has to be present for authenticationMethod SharedAccessKey. This should be Prima ##### Parameter details -

authenticationType

+

authenticationType

**Since version:** `2.5` ∙ **Type:** `enum` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"sharedaccesskey"` -**Path:** /updaters/[11] +**Path:** /updaters/[10] **Enum values:** `sharedaccesskey` | `federatedidentity` Which authentication type to use -

customMidnight

+

customMidnight

**Since version:** `2.2` ∙ **Type:** `integer` ∙ **Cardinality:** `Optional` ∙ **Default value:** `0` -**Path:** /updaters/[11] +**Path:** /updaters/[10] Time on which time breaks into new day. It is common that operating day date breaks a little bit later than midnight so that the switch happens when traffic is at the lowest point. Parameter uses 24-hour format. If the switch happens on 4 am then set this field to 4. -

fullyQualifiedNamespace

+

fullyQualifiedNamespace

**Since version:** `2.5` ∙ **Type:** `string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[11] +**Path:** /updaters/[10] Service Bus fully qualified namespace used for authentication. Has to be present for authenticationMethod FederatedIdentity. -

servicebus-url

+

servicebus-url

**Since version:** `2.2` ∙ **Type:** `string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[11] +**Path:** /updaters/[10] Service Bus connection used for authentication. diff --git a/doc/user/sandbox/siri/SiriGooglePubSubUpdater.md b/doc/user/sandbox/siri/SiriGooglePubSubUpdater.md index 74dfc4a4238..58d05d5490e 100644 --- a/doc/user/sandbox/siri/SiriGooglePubSubUpdater.md +++ b/doc/user/sandbox/siri/SiriGooglePubSubUpdater.md @@ -31,22 +31,22 @@ of the `router-config.json`. | Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | |------------------------------------------------------------|:----------:|----------------------------------------------------------------------------------|:----------:|---------------|:-----:| | type = "siri-et-google-pubsub-updater" | `enum` | The type of the updater. | *Required* | | 1.5 | -| [dataInitializationUrl](#u__13__dataInitializationUrl) | `string` | URL used to download over HTTP the recent history of SIRI-ET messages. | *Optional* | | 2.1 | +| [dataInitializationUrl](#u__12__dataInitializationUrl) | `string` | URL used to download over HTTP the recent history of SIRI-ET messages. | *Optional* | | 2.1 | | feedId | `string` | The ID of the feed to apply the updates to. | *Optional* | | 2.1 | | fuzzyTripMatching | `boolean` | If the trips should be matched fuzzily. | *Optional* | `false` | 2.1 | -| [initialGetDataTimeout](#u__13__initialGetDataTimeout) | `duration` | Timeout for retrieving the recent history of SIRI-ET messages. | *Optional* | `"PT30S"` | 2.1 | -| [reconnectPeriod](#u__13__reconnectPeriod) | `duration` | Wait this amount of time before trying to reconnect to the PubSub subscription. | *Optional* | `"PT30S"` | 2.1 | -| [subscriptionProjectName](#u__13__subscriptionProjectName) | `string` | The Google Cloud project that hosts the PubSub subscription. | *Required* | | 2.1 | +| [initialGetDataTimeout](#u__12__initialGetDataTimeout) | `duration` | Timeout for retrieving the recent history of SIRI-ET messages. | *Optional* | `"PT30S"` | 2.1 | +| [reconnectPeriod](#u__12__reconnectPeriod) | `duration` | Wait this amount of time before trying to reconnect to the PubSub subscription. | *Optional* | `"PT30S"` | 2.1 | +| [subscriptionProjectName](#u__12__subscriptionProjectName) | `string` | The Google Cloud project that hosts the PubSub subscription. | *Required* | | 2.1 | | topicName | `string` | The name of the PubSub topic that publishes the updates. | *Required* | | 2.1 | | topicProjectName | `string` | The Google Cloud project that hosts the PubSub topic that publishes the updates. | *Required* | | 2.1 | ##### Parameter details -

dataInitializationUrl

+

dataInitializationUrl

**Since version:** `2.1` ∙ **Type:** `string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[13] +**Path:** /updaters/[12] URL used to download over HTTP the recent history of SIRI-ET messages. @@ -55,10 +55,10 @@ If this parameter is set, the updater will be marked as initialized (primed) onl the message history is fully downloaded and applied. -

initialGetDataTimeout

+

initialGetDataTimeout

**Since version:** `2.1` ∙ **Type:** `duration` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"PT30S"` -**Path:** /updaters/[13] +**Path:** /updaters/[12] Timeout for retrieving the recent history of SIRI-ET messages. @@ -67,10 +67,10 @@ of time for the connection to be established. If the connection times out, the updater will retry indefinitely with exponential backoff. -

reconnectPeriod

+

reconnectPeriod

**Since version:** `2.1` ∙ **Type:** `duration` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"PT30S"` -**Path:** /updaters/[13] +**Path:** /updaters/[12] Wait this amount of time before trying to reconnect to the PubSub subscription. @@ -78,10 +78,10 @@ In case of a network error, the updater will try periodically to reconnect to th Google PubSub subscription. -

subscriptionProjectName

+

subscriptionProjectName

**Since version:** `2.1` ∙ **Type:** `string` ∙ **Cardinality:** `Required` -**Path:** /updaters/[13] +**Path:** /updaters/[12] The Google Cloud project that hosts the PubSub subscription. diff --git a/doc/user/sandbox/siri/SiriUpdater.md b/doc/user/sandbox/siri/SiriUpdater.md index 72ab45d0f39..f6c4c3f999f 100644 --- a/doc/user/sandbox/siri/SiriUpdater.md +++ b/doc/user/sandbox/siri/SiriUpdater.md @@ -37,16 +37,16 @@ To enable the SIRI updater you need to add it to the updaters section of the `ro | previewInterval | `duration` | TODO | *Optional* | | 2.0 | | requestorRef | `string` | The requester reference. | *Optional* | | 2.0 | | timeout | `duration` | The HTTP timeout to download the updates. | *Optional* | `"PT15S"` | 2.0 | -| [url](#u__9__url) | `string` | The URL to send the HTTP requests to. | *Required* | | 2.0 | -| [headers](#u__9__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | +| [url](#u__8__url) | `string` | The URL to send the HTTP requests to. | *Required* | | 2.0 | +| [headers](#u__8__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | ##### Parameter details -

url

+

url

**Since version:** `2.0` ∙ **Type:** `string` ∙ **Cardinality:** `Required` -**Path:** /updaters/[9] +**Path:** /updaters/[8] The URL to send the HTTP requests to. @@ -58,10 +58,10 @@ renamed by the loader when processed: -

headers

+

headers

**Since version:** `2.3` ∙ **Type:** `map of string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[9] +**Path:** /updaters/[8] HTTP headers to add to the request. Any header key, value can be inserted. @@ -93,25 +93,25 @@ HTTP headers to add to the request. Any header key, value can be inserted. -| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | -|----------------------------------|:---------------:|--------------------------------------------------------------------------------------------------------|:----------:|---------------|:-----:| -| type = "siri-sx-updater" | `enum` | The type of the updater. | *Required* | | 1.5 | -| blockReadinessUntilInitialized | `boolean` | Whether catching up with the updates should block the readiness check from returning a 'ready' result. | *Optional* | `false` | 2.0 | -| [earlyStart](#u__10__earlyStart) | `duration` | This value is subtracted from the actual validity defined in the message. | *Optional* | `"PT0S"` | 2.0 | -| feedId | `string` | The ID of the feed to apply the updates to. | *Required* | | 2.0 | -| frequency | `duration` | How often the updates should be retrieved. | *Optional* | `"PT1M"` | 2.0 | -| requestorRef | `string` | The requester reference. | *Optional* | | 2.0 | -| timeout | `duration` | The HTTP timeout to download the updates. | *Optional* | `"PT15S"` | 2.0 | -| [url](#u__10__url) | `string` | The URL to send the HTTP requests to. Supports http/https and file protocol. | *Required* | | 2.0 | -| [headers](#u__10__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | +| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | +|---------------------------------|:---------------:|--------------------------------------------------------------------------------------------------------|:----------:|---------------|:-----:| +| type = "siri-sx-updater" | `enum` | The type of the updater. | *Required* | | 1.5 | +| blockReadinessUntilInitialized | `boolean` | Whether catching up with the updates should block the readiness check from returning a 'ready' result. | *Optional* | `false` | 2.0 | +| [earlyStart](#u__9__earlyStart) | `duration` | This value is subtracted from the actual validity defined in the message. | *Optional* | `"PT0S"` | 2.0 | +| feedId | `string` | The ID of the feed to apply the updates to. | *Required* | | 2.0 | +| frequency | `duration` | How often the updates should be retrieved. | *Optional* | `"PT1M"` | 2.0 | +| requestorRef | `string` | The requester reference. | *Optional* | | 2.0 | +| timeout | `duration` | The HTTP timeout to download the updates. | *Optional* | `"PT15S"` | 2.0 | +| [url](#u__9__url) | `string` | The URL to send the HTTP requests to. Supports http/https and file protocol. | *Required* | | 2.0 | +| [headers](#u__9__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | ##### Parameter details -

earlyStart

+

earlyStart

**Since version:** `2.0` ∙ **Type:** `duration` ∙ **Cardinality:** `Optional` ∙ **Default value:** `"PT0S"` -**Path:** /updaters/[10] +**Path:** /updaters/[9] This value is subtracted from the actual validity defined in the message. @@ -119,10 +119,10 @@ Normally the planned departure time is used, so setting this to 10s will cause t SX-message to be included in trip-results 10 seconds before the the planned departure time. -

url

+

url

**Since version:** `2.0` ∙ **Type:** `string` ∙ **Cardinality:** `Required` -**Path:** /updaters/[10] +**Path:** /updaters/[9] The URL to send the HTTP requests to. Supports http/https and file protocol. @@ -135,10 +135,10 @@ renamed by the loader when processed: -

headers

+

headers

**Since version:** `2.3` ∙ **Type:** `map of string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[10] +**Path:** /updaters/[9] HTTP headers to add to the request. Any header key, value can be inserted. diff --git a/magidoc.mjs b/magidoc.mjs index 595ba25c0c0..6b05526a1ec 100644 --- a/magidoc.mjs +++ b/magidoc.mjs @@ -5,6 +5,7 @@ export default { }, website: { template: 'carbon-multi-page', + customStyles: ['https://docs.opentripplanner.org/en/dev-2.x/css/magidoc-overrides.css'], output: 'target/magidoc/api/graphql-gtfs/', options: { siteRoot: '/api/dev-2.x/graphql-gtfs', diff --git a/mkdocs.yml b/mkdocs.yml index 8b3748be2b0..1364be7be1f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -56,12 +56,12 @@ nav: - Visual Identity: 'Visual-Identity.md' - 'Usage': - Basic Tutorial: 'Basic-Tutorial.md' - - Data Sources: 'Data-Sources.md' + - Netex and SIRI Tutorial: 'Netex-Tutorial.md' - Getting OTP: 'Getting-OTP.md' + - Data Sources: 'Data-Sources.md' - Container Image: 'Container-Image.md' - System Requirements and Suggestions: 'System-Requirements.md' - Preparing OSM Data: 'Preparing-OSM.md' - - Netex and SIRI: 'Netex-Norway.md' - Troubleshooting: 'Troubleshooting-Routing.md' - Comparing OTP2 to OTP1: 'Version-Comparison.md' - Frontends: 'Frontends.md' @@ -90,6 +90,7 @@ nav: - "Stop Area Relations": 'StopAreas.md' - "Street Graph Pruning": 'IslandPruning.md' - Accessibility: 'Accessibility.md' + - NeTex and Siri compatibility: 'features-explained/Netex-Siri-Compatibility.md' - "Travel Time Analysis": 'Analysis.md' - "Logging": "Logging.md" - Development: diff --git a/pom.xml b/pom.xml index d8d0138934b..34997513f54 100644 --- a/pom.xml +++ b/pom.xml @@ -56,17 +56,17 @@ - 158 + 160 32.0 2.52 - 2.17.2 + 2.18.0 3.1.8 5.11.0 1.13.4 5.6.0 - 1.5.7 - 9.11.1 + 1.5.8 + 9.12.0 2.0.16 2.0.15 1.27 @@ -819,7 +819,7 @@ org.onebusaway onebusaway-gtfs - 3.2.3 + 3.2.4 @@ -858,7 +858,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.3.1 + 5.4 commons-cli @@ -989,7 +989,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.5 + 3.2.6 sign-artifacts diff --git a/renovate.json5 b/renovate.json5 index 045bbe07d22..900147e0f07 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -147,7 +147,8 @@ { "description": "give some projects time to publish a changelog before opening the PR", "matchPackagePrefixes": [ - "com.google.dagger:" + "com.google.dagger:", + "com.fasterxml.jackson" ], "minimumReleaseAge": "1 week" }, diff --git a/src/client/index.html b/src/client/index.html index 5b3dd90b341..88764c84a17 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -5,8 +5,8 @@ OTP Debug Client - - + +
diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/FareRuleSetTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/FareRuleSetTest.java new file mode 100644 index 00000000000..13d4f713634 --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/fares/FareRuleSetTest.java @@ -0,0 +1,222 @@ +package org.opentripplanner.ext.fares; + +import static org.junit.jupiter.api.Assertions.*; + +import java.time.Duration; +import java.util.HashSet; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opentripplanner.ext.fares.model.FareAttribute; +import org.opentripplanner.ext.fares.model.FareRuleSet; +import org.opentripplanner.transit.model.basic.Money; +import org.opentripplanner.transit.model.framework.FeedScopedId; + +class FareRuleSetTest { + + private FareRuleSet fareRuleSet; + static final Money TWO_FIFTY = Money.usDollars(2.50f); + + @BeforeEach + void setUp() { + FeedScopedId id = new FeedScopedId("feed", "fare1"); + FareAttribute fareAttribute = FareAttribute + .of(id) + .setPrice(TWO_FIFTY) + .setPaymentMethod(1) + .setTransfers(1) + .setTransferDuration(7200) + .build(); + fareRuleSet = new FareRuleSet(fareAttribute); + } + + @Test + void testHasNoRules() { + assertFalse(fareRuleSet.hasRules()); + } + + @Test + void testAddOriginDestination() { + fareRuleSet.addOriginDestination("A", "B"); + assertTrue(fareRuleSet.hasRules()); + } + + @Test + void testAddRouteOriginDestination() { + fareRuleSet.addRouteOriginDestination("Route1", "A", "B"); + assertTrue(fareRuleSet.hasRules()); + assertEquals(1, fareRuleSet.getRouteOriginDestinations().size()); + } + + @Test + void testAddContains() { + fareRuleSet.addContains("Zone1"); + assertTrue(fareRuleSet.hasRules()); + assertEquals(1, fareRuleSet.getContains().size()); + } + + @Test + void testAddRoute() { + FeedScopedId routeId = new FeedScopedId("feed", "route1"); + fareRuleSet.addRoute(routeId); + assertTrue(fareRuleSet.hasRules()); + assertEquals(1, fareRuleSet.getRoutes().size()); + } + + @Test + void testMatchesWithNoRules() { + var routes = Set.of(new FeedScopedId("feed", "route1")); + var trips = Set.of(new FeedScopedId("feed", "trip1")); + var zones = Set.of("zone1"); + assertTrue( + fareRuleSet.matches("A", "B", Set.of(), Set.of(), Set.of(), 0, Duration.ZERO, Duration.ZERO) + ); + assertTrue( + fareRuleSet.matches( + "A", + "B", + zones, + routes, + trips, + 0, + Duration.ofMinutes(100), + Duration.ofMinutes(100) + ) + ); + } + + @Test + void testMatchesWithOriginDestination() { + fareRuleSet.addOriginDestination("A", "B"); + assertTrue( + fareRuleSet.matches("A", "B", Set.of(), Set.of(), Set.of(), 0, Duration.ZERO, Duration.ZERO) + ); + assertFalse( + fareRuleSet.matches("B", "C", Set.of(), Set.of(), Set.of(), 0, Duration.ZERO, Duration.ZERO) + ); + } + + @Test + void testMatchesWithContains() { + Set zones = new HashSet<>(); + zones.add("Zone1"); + zones.add("Zone2"); + fareRuleSet.addContains("Zone1"); + fareRuleSet.addContains("Zone2"); + assertTrue( + fareRuleSet.matches("A", "B", zones, Set.of(), Set.of(), 0, Duration.ZERO, Duration.ZERO) + ); + assertFalse( + fareRuleSet.matches("A", "B", Set.of(), Set.of(), Set.of(), 0, Duration.ZERO, Duration.ZERO) + ); + } + + @Test + void testMatchesWithRoutes() { + Set routes = new HashSet<>(); + FeedScopedId routeId = new FeedScopedId("feed", "route1"); + FeedScopedId otherRouteId = new FeedScopedId("feed", "route2"); + routes.add(routeId); + fareRuleSet.addRoute(routeId); + assertTrue( + fareRuleSet.matches("A", "B", Set.of(), routes, Set.of(), 0, Duration.ZERO, Duration.ZERO) + ); + assertFalse( + fareRuleSet.matches( + "A", + "B", + Set.of(), + Set.of(otherRouteId), + Set.of(), + 0, + Duration.ZERO, + Duration.ZERO + ) + ); + } + + @Test + void testMatchesWithTransfers() { + assertTrue( + fareRuleSet.matches("A", "B", Set.of(), Set.of(), Set.of(), 1, Duration.ZERO, Duration.ZERO) + ); + assertFalse( + fareRuleSet.matches("A", "B", Set.of(), Set.of(), Set.of(), 2, Duration.ZERO, Duration.ZERO) + ); + } + + @Test + void testMatchesWithTransferDuration() { + assertTrue( + fareRuleSet.matches( + "A", + "B", + Set.of(), + Set.of(), + Set.of(), + 0, + Duration.ofSeconds(7000), + Duration.ZERO + ) + ); + assertFalse( + fareRuleSet.matches( + "A", + "B", + Set.of(), + Set.of(), + Set.of(), + 0, + Duration.ofSeconds(8000), + Duration.ZERO + ) + ); + } + + @Test + void testMatchesWithJourneyDuration() { + FareAttribute journeyFare = FareAttribute + .of(new FeedScopedId("feed", "journey")) + .setPrice(Money.usDollars(3.00f)) + .setPaymentMethod(1) + .setJourneyDuration(7200) + .build(); + FareRuleSet journeyRuleSet = new FareRuleSet(journeyFare); + + assertTrue( + journeyRuleSet.matches( + "A", + "B", + Set.of(), + Set.of(), + Set.of(), + 0, + Duration.ZERO, + Duration.ofSeconds(7000) + ) + ); + assertFalse( + journeyRuleSet.matches( + "A", + "B", + Set.of(), + Set.of(), + Set.of(), + 0, + Duration.ZERO, + Duration.ofSeconds(8000) + ) + ); + } + + @Test + void testAgencyMethods() { + assertFalse(fareRuleSet.hasAgencyDefined()); + assertNull(fareRuleSet.getAgency()); + + FeedScopedId agencyId = new FeedScopedId("feed", "agency1"); + fareRuleSet.setAgency(agencyId); + assertTrue(fareRuleSet.hasAgencyDefined()); + assertEquals(agencyId, fareRuleSet.getAgency()); + } +} diff --git a/src/ext-test/java/org/opentripplanner/ext/fares/model/FareProductTest.java b/src/ext-test/java/org/opentripplanner/ext/fares/model/FareProductTest.java index 089bac64c11..65f10db6f85 100644 --- a/src/ext-test/java/org/opentripplanner/ext/fares/model/FareProductTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/fares/model/FareProductTest.java @@ -6,7 +6,6 @@ import java.time.OffsetDateTime; import java.time.ZonedDateTime; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -57,7 +56,6 @@ void instanceId(FareProduct fareProduct, ZonedDateTime startTime, String expecte assertEquals(expectedInstanceId, instanceId); } - @Nonnull private static FareProduct fareProduct(Duration duration, RiderCategory cat, FareMedium medium) { return new FareProduct( new FeedScopedId("fares", "daypass"), diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/FlexIntegrationTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/FlexIntegrationTest.java index c88439a9e3f..56d66081e35 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/FlexIntegrationTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/FlexIntegrationTest.java @@ -13,7 +13,6 @@ import java.time.ZonedDateTime; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -216,7 +215,6 @@ private Itinerary getItinerary(GenericLocation from, GenericLocation to, int ind return itineraries.get(index); } - @Nonnull private static List getItineraries( GenericLocation from, GenericLocation to, diff --git a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java index 8bc9d7c8919..6280095b6be 100644 --- a/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/flex/trip/UnscheduledTripTest.java @@ -16,7 +16,6 @@ import java.util.List; import java.util.Objects; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/src/ext-test/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolverTest.java b/src/ext-test/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolverTest.java index 21d5a7f1696..e5b842a474a 100644 --- a/src/ext-test/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolverTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolverTest.java @@ -129,9 +129,12 @@ void testPopulateLegsWithRealtimeKeepStaySeated() { private static TripPattern delay(TripPattern pattern1, int seconds) { var originalTimeTable = pattern1.getScheduledTimetable(); - var delayedTimetable = new Timetable(pattern1); var delayedTripTimes = delay(originalTimeTable.getTripTimes(0), seconds); - delayedTimetable.addTripTimes(delayedTripTimes); + var delayedTimetable = Timetable + .of() + .withTripPattern(pattern1) + .addTripTimes(delayedTripTimes) + .build(); return pattern1.copy().withScheduledTimeTable(delayedTimetable).build(); } diff --git a/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java b/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java index cf29eec8f49..d10dbc05090 100644 --- a/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java @@ -2,12 +2,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.opentripplanner.transit.model._data.TransitModelForTest.id; import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertFailure; -import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.timetable.RealTimeState; @@ -15,33 +14,48 @@ import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.updater.spi.UpdateError; +import org.opentripplanner.updater.trip.RealtimeTestConstants; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; -import uk.org.siri.siri20.EstimatedTimetableDeliveryStructure; +import org.opentripplanner.updater.trip.TripInput; -class SiriTimetableSnapshotSourceTest { +class SiriTimetableSnapshotSourceTest implements RealtimeTestConstants { + + private static final TripInput TRIP_1_INPUT = TripInput + .of(TRIP_1_ID) + .withRoute(ROUTE_1.copy().withOperator(OPERATOR1).build()) + .addStop(STOP_A1, "0:00:10", "0:00:11") + .addStop(STOP_B1, "0:00:20", "0:00:21") + .build(); + + private static final TripInput TRIP_2_INPUT = TripInput + .of(TRIP_2_ID) + .addStop(STOP_A1, "0:01:00", "0:01:01") + .addStop(STOP_B1, "0:01:10", "0:01:11") + .addStop(STOP_C1, "0:01:20", "0:01:21") + .build(); @Test void testCancelTrip() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); - assertEquals(RealTimeState.SCHEDULED, env.getTripTimesForTrip(env.trip1).getRealTimeState()); + assertEquals(RealTimeState.SCHEDULED, env.getTripTimesForTrip(TRIP_1_ID).getRealTimeState()); var updates = new SiriEtBuilder(env.getDateTimeHelper()) - .withDatedVehicleJourneyRef(env.trip1.getId().getId()) + .withDatedVehicleJourneyRef(TRIP_1_ID) .withCancellation(true) .buildEstimatedTimetableDeliveries(); var result = env.applyEstimatedTimetable(updates); assertEquals(1, result.successful()); - assertEquals(RealTimeState.CANCELED, env.getTripTimesForTrip(env.trip1).getRealTimeState()); + assertEquals(RealTimeState.CANCELED, env.getTripTimesForTrip(TRIP_1_ID).getRealTimeState()); } @Test void testAddJourneyWithExistingRoute() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); - Route route = env.getTransitService().getRouteForId(env.route1Id); + Route route = ROUTE_1; int numPatternForRoute = env.getTransitService().getPatternsForRoute(route).size(); String newJourneyId = "newJourney"; @@ -55,7 +69,7 @@ void testAddJourneyWithExistingRoute() { "SCHEDULED | C1 0:01 0:01 | D1 0:03 0:03", env.getScheduledTimetable(newJourneyId) ); - FeedScopedId tripId = TransitModelForTest.id(newJourneyId); + FeedScopedId tripId = id(newJourneyId); TransitService transitService = env.getTransitService(); Trip trip = transitService.getTripForId(tripId); assertNotNull(trip); @@ -75,7 +89,8 @@ void testAddJourneyWithExistingRoute() { @Test void testAddJourneyWithNewRoute() { - var env = RealtimeTestEnvironment.siri(); + // we actually don't need the trip, but it's the only way to add a route to the index + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); String newRouteRef = "new route ref"; var updates = createValidAddedJourney(env) @@ -93,7 +108,7 @@ void testAddJourneyWithNewRoute() { ); TransitService transitService = env.getTransitService(); assertEquals(numRoutes + 1, transitService.getAllRoutes().size()); - FeedScopedId newRouteId = TransitModelForTest.id(newRouteRef); + FeedScopedId newRouteId = id(newRouteRef); Route newRoute = transitService.getRouteForId(newRouteId); assertNotNull(newRoute); assertEquals(1, transitService.getPatternsForRoute(newRoute).size()); @@ -101,7 +116,8 @@ void testAddJourneyWithNewRoute() { @Test void testAddJourneyMultipleTimes() { - var env = RealtimeTestEnvironment.siri(); + // we actually don't need the trip, but it's the only way to add a route to the index + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); var updates = createValidAddedJourney(env).buildEstimatedTimetableDeliveries(); int numTrips = env.getTransitService().getAllTrips().size(); @@ -115,20 +131,21 @@ void testAddJourneyMultipleTimes() { @Test void testAddedJourneyWithInvalidScheduledData() { - var env = RealtimeTestEnvironment.siri(); + // we actually don't need the trip, but it's the only way to add a route to the index + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); // Create an extra journey with invalid planned data (travel back in time) // and valid real time data var createExtraJourney = new SiriEtBuilder(env.getDateTimeHelper()) .withEstimatedVehicleJourneyCode("newJourney") .withIsExtraJourney(true) - .withOperatorRef(env.operator1Id.getId()) - .withLineRef(env.route1Id.getId()) + .withOperatorRef(OPERATOR_1_ID) + .withLineRef(ROUTE_1_ID) .withEstimatedCalls(builder -> builder - .call(env.stopA1) + .call(STOP_A1) .departAimedExpected("10:58", "10:48") - .call(env.stopB1) + .call(STOP_B1) .arriveAimedExpected("10:08", "10:58") ) .buildEstimatedTimetableDeliveries(); @@ -140,7 +157,7 @@ void testAddedJourneyWithInvalidScheduledData() { @Test void testAddedJourneyWithUnresolvableAgency() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().build(); // Create an extra journey with unknown line and operator var createExtraJourney = new SiriEtBuilder(env.getDateTimeHelper()) @@ -150,9 +167,9 @@ void testAddedJourneyWithUnresolvableAgency() { .withLineRef("unknown line") .withEstimatedCalls(builder -> builder - .call(env.stopA1) + .call(STOP_A1) .departAimedExpected("10:58", "10:48") - .call(env.stopB1) + .call(STOP_B1) .arriveAimedExpected("10:08", "10:58") ) .buildEstimatedTimetableDeliveries(); @@ -164,17 +181,17 @@ void testAddedJourneyWithUnresolvableAgency() { @Test void testReplaceJourney() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); var updates = new SiriEtBuilder(env.getDateTimeHelper()) .withEstimatedVehicleJourneyCode("newJourney") .withIsExtraJourney(true) // replace trip1 - .withVehicleJourneyRef(env.trip1.getId().getId()) - .withOperatorRef(env.operator1Id.getId()) - .withLineRef(env.route1Id.getId()) - .withRecordedCalls(builder -> builder.call(env.stopA1).departAimedActual("00:01", "00:02")) - .withEstimatedCalls(builder -> builder.call(env.stopC1).arriveAimedExpected("00:03", "00:04")) + .withVehicleJourneyRef(TRIP_1_ID) + .withOperatorRef(OPERATOR_1_ID) + .withLineRef(ROUTE_1_ID) + .withRecordedCalls(builder -> builder.call(STOP_A1).departAimedActual("00:01", "00:02")) + .withEstimatedCalls(builder -> builder.call(STOP_C1).arriveAimedExpected("00:03", "00:04")) .buildEstimatedTimetableDeliveries(); var result = env.applyEstimatedTimetable(updates); @@ -188,7 +205,7 @@ void testReplaceJourney() { ); // Original trip should not get canceled - var originalTripTimes = env.getTripTimesForTrip(env.trip1); + var originalTripTimes = env.getTripTimesForTrip(TRIP_1_ID); assertEquals(RealTimeState.SCHEDULED, originalTripTimes.getRealTimeState()); } @@ -197,17 +214,17 @@ void testReplaceJourney() { */ @Test void testUpdateJourneyWithDatedVehicleJourneyRef() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); var updates = updatedJourneyBuilder(env) - .withDatedVehicleJourneyRef(env.trip1.getId().getId()) + .withDatedVehicleJourneyRef(TRIP_1_ID) .buildEstimatedTimetableDeliveries(); var result = env.applyEstimatedTimetable(updates); assertEquals(1, result.successful()); assertTripUpdated(env); assertEquals( "UPDATED | A1 0:00:15 0:00:15 | B1 0:00:25 0:00:25", - env.getRealtimeTimetable(env.trip1) + env.getRealtimeTimetable(TRIP_1_ID) ); } @@ -216,11 +233,11 @@ void testUpdateJourneyWithDatedVehicleJourneyRef() { */ @Test void testUpdateJourneyWithFramedVehicleJourneyRef() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); var updates = updatedJourneyBuilder(env) .withFramedVehicleJourneyRef(builder -> - builder.withServiceDate(SERVICE_DATE).withVehicleJourneyRef(env.trip1.getId().getId()) + builder.withServiceDate(SERVICE_DATE).withVehicleJourneyRef(TRIP_1_ID) ) .buildEstimatedTimetableDeliveries(); var result = env.applyEstimatedTimetable(updates); @@ -233,7 +250,7 @@ void testUpdateJourneyWithFramedVehicleJourneyRef() { */ @Test void testUpdateJourneyWithoutJourneyRef() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); var updates = updatedJourneyBuilder(env).buildEstimatedTimetableDeliveries(); var result = env.applyEstimatedTimetable(updates); @@ -246,7 +263,7 @@ void testUpdateJourneyWithoutJourneyRef() { */ @Test void testUpdateJourneyWithFuzzyMatching() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); var updates = updatedJourneyBuilder(env).buildEstimatedTimetableDeliveries(); var result = env.applyEstimatedTimetableWithFuzzyMatcher(updates); @@ -260,17 +277,17 @@ void testUpdateJourneyWithFuzzyMatching() { */ @Test void testUpdateJourneyWithFuzzyMatchingAndMissingAimedDepartureTime() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); var updates = new SiriEtBuilder(env.getDateTimeHelper()) .withFramedVehicleJourneyRef(builder -> - builder.withServiceDate(RealtimeTestEnvironment.SERVICE_DATE).withVehicleJourneyRef("XXX") + builder.withServiceDate(SERVICE_DATE).withVehicleJourneyRef("XXX") ) .withEstimatedCalls(builder -> builder - .call(env.stopA1) + .call(STOP_A1) .departAimedExpected(null, "00:00:12") - .call(env.stopB1) + .call(STOP_B1) .arriveAimedExpected("00:00:20", "00:00:22") ) .buildEstimatedTimetableDeliveries(); @@ -285,15 +302,13 @@ void testUpdateJourneyWithFuzzyMatchingAndMissingAimedDepartureTime() { */ @Test void testChangeQuay() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); var updates = new SiriEtBuilder(env.getDateTimeHelper()) - .withDatedVehicleJourneyRef(env.trip1.getId().getId()) - .withRecordedCalls(builder -> - builder.call(env.stopA1).departAimedActual("00:00:11", "00:00:15") - ) + .withDatedVehicleJourneyRef(TRIP_1_ID) + .withRecordedCalls(builder -> builder.call(STOP_A1).departAimedActual("00:00:11", "00:00:15")) .withEstimatedCalls(builder -> - builder.call(env.stopB2).arriveAimedExpected("00:00:20", "00:00:33") + builder.call(STOP_B2).arriveAimedExpected("00:00:20", "00:00:33") ) .buildEstimatedTimetableDeliveries(); @@ -302,23 +317,23 @@ void testChangeQuay() { assertEquals(1, result.successful()); assertEquals( "MODIFIED | A1 [R] 0:00:15 0:00:15 | B2 0:00:33 0:00:33", - env.getRealtimeTimetable(env.trip1) + env.getRealtimeTimetable(TRIP_1_ID) ); } @Test void testCancelStop() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_2_INPUT).build(); var updates = new SiriEtBuilder(env.getDateTimeHelper()) - .withDatedVehicleJourneyRef(env.trip2.getId().getId()) + .withDatedVehicleJourneyRef(TRIP_2_ID) .withEstimatedCalls(builder -> builder - .call(env.stopA1) + .call(STOP_A1) .departAimedExpected("00:01:01", "00:01:01") - .call(env.stopB1) + .call(STOP_B1) .withIsCancellation(true) - .call(env.stopC1) + .call(STOP_C1) .arriveAimedExpected("00:01:30", "00:01:30") ) .buildEstimatedTimetableDeliveries(); @@ -328,7 +343,7 @@ void testCancelStop() { assertEquals(1, result.successful()); assertEquals( "MODIFIED | A1 0:01:01 0:01:01 | B1 [C] 0:01:10 0:01:11 | C1 0:01:30 0:01:30", - env.getRealtimeTimetable(env.trip2) + env.getRealtimeTimetable(TRIP_2_ID) ); } @@ -336,20 +351,18 @@ void testCancelStop() { @Test @Disabled("Not supported yet") void testAddStop() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); var updates = new SiriEtBuilder(env.getDateTimeHelper()) - .withDatedVehicleJourneyRef(env.trip1.getId().getId()) - .withRecordedCalls(builder -> - builder.call(env.stopA1).departAimedActual("00:00:11", "00:00:15") - ) + .withDatedVehicleJourneyRef(TRIP_1_ID) + .withRecordedCalls(builder -> builder.call(STOP_A1).departAimedActual("00:00:11", "00:00:15")) .withEstimatedCalls(builder -> builder - .call(env.stopD1) + .call(STOP_D1) .withIsExtraCall(true) .arriveAimedExpected("00:00:19", "00:00:20") .departAimedExpected("00:00:24", "00:00:25") - .call(env.stopB1) + .call(STOP_B1) .arriveAimedExpected("00:00:20", "00:00:33") ) .buildEstimatedTimetableDeliveries(); @@ -359,7 +372,7 @@ void testAddStop() { assertEquals(1, result.successful()); assertEquals( "MODIFIED | A1 0:00:15 0:00:15 | D1 [C] 0:00:20 0:00:25 | B1 0:00:33 0:00:33", - env.getRealtimeTimetable(env.trip1) + env.getRealtimeTimetable(TRIP_1_ID) ); } @@ -369,7 +382,7 @@ void testAddStop() { @Test void testNotMonitored() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); var updates = new SiriEtBuilder(env.getDateTimeHelper()) .withMonitored(false) @@ -382,19 +395,19 @@ void testNotMonitored() { @Test void testReplaceJourneyWithoutEstimatedVehicleJourneyCode() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); var updates = new SiriEtBuilder(env.getDateTimeHelper()) .withDatedVehicleJourneyRef("newJourney") .withIsExtraJourney(true) - .withVehicleJourneyRef(env.trip1.getId().getId()) - .withOperatorRef(env.operator1Id.getId()) - .withLineRef(env.route1Id.getId()) + .withVehicleJourneyRef(TRIP_1_ID) + .withOperatorRef(OPERATOR_1_ID) + .withLineRef(ROUTE_1_ID) .withEstimatedCalls(builder -> builder - .call(env.stopA1) + .call(STOP_A1) .departAimedExpected("00:01", "00:02") - .call(env.stopC1) + .call(STOP_C1) .arriveAimedExpected("00:03", "00:04") ) .buildEstimatedTimetableDeliveries(); @@ -407,15 +420,15 @@ void testReplaceJourneyWithoutEstimatedVehicleJourneyCode() { @Test void testNegativeHopTime() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); var updates = new SiriEtBuilder(env.getDateTimeHelper()) - .withDatedVehicleJourneyRef(env.trip1.getId().getId()) + .withDatedVehicleJourneyRef(TRIP_1_ID) .withRecordedCalls(builder -> builder - .call(env.stopA1) + .call(STOP_A1) .departAimedActual("00:00:11", "00:00:15") - .call(env.stopB1) + .call(STOP_B1) .arriveAimedActual("00:00:20", "00:00:14") ) .buildEstimatedTimetableDeliveries(); @@ -427,18 +440,18 @@ void testNegativeHopTime() { @Test void testNegativeDwellTime() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_2_INPUT).build(); var updates = new SiriEtBuilder(env.getDateTimeHelper()) - .withDatedVehicleJourneyRef(env.trip2.getId().getId()) + .withDatedVehicleJourneyRef(TRIP_2_ID) .withRecordedCalls(builder -> builder - .call(env.stopA1) + .call(STOP_A1) .departAimedActual("00:01:01", "00:01:01") - .call(env.stopB1) + .call(STOP_B1) .arriveAimedActual("00:01:10", "00:01:13") .departAimedActual("00:01:11", "00:01:12") - .call(env.stopB1) + .call(STOP_B1) .arriveAimedActual("00:01:20", "00:01:20") ) .buildEstimatedTimetableDeliveries(); @@ -452,19 +465,19 @@ void testNegativeDwellTime() { @Test @Disabled("Not supported yet") void testExtraUnknownStop() { - var env = RealtimeTestEnvironment.siri(); + var env = RealtimeTestEnvironment.siri().addTrip(TRIP_1_INPUT).build(); var updates = new SiriEtBuilder(env.getDateTimeHelper()) - .withDatedVehicleJourneyRef(env.trip1.getId().getId()) + .withDatedVehicleJourneyRef(TRIP_1_ID) .withEstimatedCalls(builder -> builder - .call(env.stopA1) + .call(STOP_A1) .departAimedExpected("00:00:11", "00:00:15") // Unexpected extra stop without isExtraCall flag - .call(env.stopD1) + .call(STOP_D1) .arriveAimedExpected("00:00:19", "00:00:20") .departAimedExpected("00:00:24", "00:00:25") - .call(env.stopB1) + .call(STOP_B1) .arriveAimedExpected("00:00:20", "00:00:33") ) .buildEstimatedTimetableDeliveries(); @@ -478,20 +491,19 @@ private static SiriEtBuilder createValidAddedJourney(RealtimeTestEnvironment env return new SiriEtBuilder(env.getDateTimeHelper()) .withEstimatedVehicleJourneyCode("newJourney") .withIsExtraJourney(true) - .withOperatorRef(env.operator1Id.getId()) - .withLineRef(env.route1Id.getId()) - .withRecordedCalls(builder -> builder.call(env.stopC1).departAimedActual("00:01", "00:02")) - .withEstimatedCalls(builder -> builder.call(env.stopD1).arriveAimedExpected("00:03", "00:04") - ); + .withOperatorRef(OPERATOR_1_ID) + .withLineRef(ROUTE_1_ID) + .withRecordedCalls(builder -> builder.call(STOP_C1).departAimedActual("00:01", "00:02")) + .withEstimatedCalls(builder -> builder.call(STOP_D1).arriveAimedExpected("00:03", "00:04")); } private static SiriEtBuilder updatedJourneyBuilder(RealtimeTestEnvironment env) { return new SiriEtBuilder(env.getDateTimeHelper()) .withEstimatedCalls(builder -> builder - .call(env.stopA1) + .call(STOP_A1) .departAimedExpected("00:00:11", "00:00:15") - .call(env.stopB1) + .call(STOP_B1) .arriveAimedExpected("00:00:20", "00:00:25") ); } @@ -499,7 +511,7 @@ private static SiriEtBuilder updatedJourneyBuilder(RealtimeTestEnvironment env) private static void assertTripUpdated(RealtimeTestEnvironment env) { assertEquals( "UPDATED | A1 0:00:15 0:00:15 | B1 0:00:25 0:00:25", - env.getRealtimeTimetable(env.trip1) + env.getRealtimeTimetable(TRIP_1_ID) ); } } diff --git a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehiclerental/mapper/VehicleRentalLayerTest.java b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehiclerental/mapper/VehicleRentalLayerTest.java index 984d4927041..45a4ab30744 100644 --- a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehiclerental/mapper/VehicleRentalLayerTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehiclerental/mapper/VehicleRentalLayerTest.java @@ -8,7 +8,6 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.framework.i18n.TranslatedString; @@ -114,7 +113,6 @@ public void realtimeStation() { assertEquals(3, map.get("spacesAvailable")); } - @Nonnull private static RentalVehicleType vehicleType(RentalFormFactor formFactor) { return new RentalVehicleType( new FeedScopedId("1", formFactor.name()), diff --git a/src/ext-test/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdaterTest.java b/src/ext-test/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdaterTest.java deleted file mode 100644 index f931e3d964e..00000000000 --- a/src/ext-test/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdaterTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.opentripplanner.ext.vehicleparking.noi; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import java.time.Duration; -import org.junit.jupiter.api.Test; -import org.opentripplanner.test.support.ResourceLoader; -import org.opentripplanner.updater.spi.HttpHeaders; - -class NoiUpdaterTest { - - @Test - void parse() { - var uri = ResourceLoader.of(this).uri("stations.json"); - var parameters = new NoiUpdaterParameters( - "noi", - uri, - "noi", - Duration.ofSeconds(30), - HttpHeaders.empty() - ); - var updater = new NoiUpdater(parameters); - updater.update(); - var lots = updater.getUpdates(); - - assertEquals(14, lots.size()); - - lots.forEach(l -> assertNotNull(l.getName())); - - var first = lots.getFirst(); - assertEquals("noi:105", first.getId().toString()); - assertEquals("(46.49817, 11.35726)", first.getCoordinate().toString()); - assertEquals("P05 - Laurin", first.getName().toString()); - assertEquals(57, first.getAvailability().getCarSpaces()); - assertEquals(90, first.getCapacity().getCarSpaces()); - - var last = lots.getLast(); - assertEquals( - "noi:935af00d-aa5f-eb11-9889-501ac5928d31-0.8458736393052522", - last.getId().toString() - ); - assertEquals("(46.5057, 11.3395)", last.getCoordinate().toString()); - assertEquals( - "Parksensoren Bozen - PNI Parksensor Nr.10 Commissariato - Viale Eugenio di savoia", - last.getName().toString() - ); - assertEquals(0, last.getAvailability().getCarSpaces()); - assertEquals(1, last.getCapacity().getCarSpaces()); - } -} diff --git a/src/ext-test/resources/org/opentripplanner/ext/vehicleparking/noi/stations.json b/src/ext-test/resources/org/opentripplanner/ext/vehicleparking/noi/stations.json deleted file mode 100644 index 2bbb07ac98b..00000000000 --- a/src/ext-test/resources/org/opentripplanner/ext/vehicleparking/noi/stations.json +++ /dev/null @@ -1,159 +0,0 @@ -{ - "last_updated": 1711368767, - "ttl": 0, - "version": "3.0.0", - "data": { - "stations": [ - { - "type": "station", - "station_id": "105", - "name": "P05 - Laurin", - "lat": 46.498174, - "lon": 11.357255, - "city": "Bolzano - Bozen", - "capacity": 90, - "free": 57 - }, - { - "type": "station", - "station_id": "TRENTO:areaexsitviacanestrinip1", - "name": "Area ex SIT via Canestrini - P1", - "lat": 46.0691, - "lon": 11.1162, - "address": "Lung'Adige Monte Grappa", - "city": "Trento", - "capacity": 300, - "free": 0 - }, - { - "type": "station", - "station_id": "ROVERETO:asm", - "name": "A.S.M.", - "lat": 45.893593, - "lon": 11.036507, - "address": "Piazzale ex-A.S.M - Via Manzoni - Rovereto", - "city": "Rovereto", - "capacity": 145, - "free": 42 - }, - { - "type": "station", - "station_id": "ROVERETO:centrostorico", - "name": "Centro Storico", - "lat": 45.890306, - "lon": 11.045004, - "address": "Viale dei Colli - Rovereto", - "city": "Rovereto", - "capacity": 143, - "free": 20 - }, - { - "type": "station", - "station_id": "ROVERETO:mart", - "name": "Mart", - "lat": 45.894705, - "lon": 11.044661, - "address": "Mart - Via Sticcotta - Rovereto", - "city": "Rovereto", - "capacity": 224, - "free": 224 - }, - { - "type": "sensor", - "station_id": "001bc50670100557-0.30188412882192206", - "group_name": "area viale Druso", - "group_id": "area_viale_druso", - "name": "piazzetta Mazzoni 3", - "lat": 46.495025, - "lon": 11.347069, - "address": "area viale Druso", - "city": "Bolzano - Bozen", - "free": false - }, - { - "type": "sensor", - "station_id": "001bc50670100541-0.9632040952321754", - "group_name": "Via A. Rosmini 22-26", - "group_id": "via_a_rosmini_22_26", - "name": "Via A. Rosmini 22-26", - "lat": 46.498292, - "lon": 11.348031, - "address": "Via A. Rosmini 22-26", - "city": "Bolzano - Bozen", - "free": false - }, - { - "type": "sensor", - "station_id": "001bc50670112a6b-0.6239539554369709", - "group_name": "Via Amalfi", - "group_id": "via_amalfi", - "name": "Via Amalfi angolo Via Druso", - "lat": 46.495283, - "lon": 11.332472, - "address": "Via Amalfi", - "city": "Bolzano - Bozen", - "free": false - }, - { - "type": "sensor", - "station_id": "001bc5067010064d-0.18879954213280836", - "group_name": "area viale Druso", - "group_id": "area_viale_druso", - "name": "piazzetta Mazzoni 4", - "lat": 46.495056, - "lon": 11.347056, - "address": "area viale Druso", - "city": "Bolzano - Bozen", - "free": false - }, - { - "type": "sensor", - "station_id": "001bc50670112976-0.4989211141789258", - "group_name": "Viale Druso 237", - "group_id": "viale_druso_237", - "name": "Viale Druso 237", - "lat": 46.495, - "lon": 11.328703, - "address": "Viale Druso 237", - "city": "Bolzano - Bozen", - "free": true - }, - { - "type": "sensor", - "station_id": "9398a35b-ef3d-eb11-b9ed-0050f244b601-0.12775006754129703", - "group_id": "", - "name": "Parksensoren Bozen - PNI Parksensor Nr.3 Siegesplatz Parkplatz", - "lat": 46.501, - "lon": 11.3431, - "free": true - }, - { - "type": "sensor", - "station_id": "7776d25f-f03d-eb11-b9ed-0050f244b601-0.4355636862513992", - "group_id": "", - "name": "Parksensoren Bozen - PNI Parksensor Nr.5 DucaDaostastrasse", - "lat": 46.4953, - "lon": 11.3396, - "free": false - }, - { - "type": "sensor", - "station_id": "e3e26add-ee3d-eb11-b9ed-0050f244b601-0.8423578257530036", - "group_id": "", - "name": "Parksensoren Bozen - PNI Parksensor Nr.4 Bahnhof BZ Richtung Rittnerseilbahn", - "lat": 46.497, - "lon": 11.3583, - "free": false - }, - { - "type": "sensor", - "station_id": "935af00d-aa5f-eb11-9889-501ac5928d31-0.8458736393052522", - "group_id": "", - "name": "Parksensoren Bozen - PNI Parksensor Nr.10 Commissariato - Viale Eugenio di savoia", - "lat": 46.5057, - "lon": 11.3395, - "free": false - } - ] - } -} \ No newline at end of file diff --git a/src/ext/java/org/opentripplanner/ext/datastore/gs/GsDataSourceRepository.java b/src/ext/java/org/opentripplanner/ext/datastore/gs/GsDataSourceRepository.java index ee7e4614ee7..f049beef0b8 100644 --- a/src/ext/java/org/opentripplanner/ext/datastore/gs/GsDataSourceRepository.java +++ b/src/ext/java/org/opentripplanner/ext/datastore/gs/GsDataSourceRepository.java @@ -9,7 +9,6 @@ import java.io.IOException; import java.net.URI; import java.util.Collections; -import javax.annotation.Nonnull; import org.opentripplanner.datastore.api.CompositeDataSource; import org.opentripplanner.datastore.api.DataSource; import org.opentripplanner.datastore.api.FileType; @@ -39,7 +38,7 @@ public void open() { } @Override - public DataSource findSource(@Nonnull URI uri, @Nonnull FileType type) { + public DataSource findSource(URI uri, FileType type) { if (skipUri(uri)) { return null; } @@ -48,7 +47,7 @@ public DataSource findSource(@Nonnull URI uri, @Nonnull FileType type) { } @Override - public CompositeDataSource findCompositeSource(@Nonnull URI uri, @Nonnull FileType type) { + public CompositeDataSource findCompositeSource(URI uri, FileType type) { if (skipUri(uri)) { return null; } diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedTransitLeg.java b/src/ext/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedTransitLeg.java index 9d10b87bbd6..ce838480186 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedTransitLeg.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/CombinedInterlinedTransitLeg.java @@ -5,7 +5,6 @@ import java.time.ZonedDateTime; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.LineString; import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.model.fare.FareProductUse; @@ -39,19 +38,16 @@ public Agency getAgency() { return first.getAgency(); } - @Nonnull @Override public TransitMode getMode() { return first.getMode(); } - @Nonnull @Override public Route getRoute() { return first.getRoute(); } - @Nonnull @Override public Trip getTrip() { return first.getTrip(); diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceFactory.java b/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceFactory.java index ee4c87924ea..d7a3a0425ec 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceFactory.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/DefaultFareServiceFactory.java @@ -92,7 +92,7 @@ protected void fillFareRules( FareRuleSet fareRule = fareRuleSet.get(id); if (fareRule == null) { // Should never happen by design - LOG.error("Inexistant fare ID in fare rule: " + id); + LOG.error("Nonexistent fare ID in fare rule: " + id); continue; } String contains = rule.getContainsId(); diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/GtfsFaresV2Service.java b/src/ext/java/org/opentripplanner/ext/fares/impl/GtfsFaresV2Service.java index 6aad14179b4..5bff64f05e0 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/GtfsFaresV2Service.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/GtfsFaresV2Service.java @@ -13,7 +13,6 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import org.opentripplanner.ext.fares.model.FareDistance; import org.opentripplanner.ext.fares.model.FareLegRule; import org.opentripplanner.ext.fares.model.FareTransferRule; @@ -210,7 +209,7 @@ private boolean transferRuleMatchesNextLeg( .orElse(false); } - private Optional getFareLegRuleByGroupId(@Nonnull FeedScopedId groupId) { + private Optional getFareLegRuleByGroupId(FeedScopedId groupId) { return legRules.stream().filter(lr -> groupId.equals(lr.legGroupId())).findAny(); } diff --git a/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareFactory.java b/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareFactory.java index 34a03c1fc06..d48cad48450 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareFactory.java +++ b/src/ext/java/org/opentripplanner/ext/fares/impl/OrcaFareFactory.java @@ -24,6 +24,8 @@ public FareService makeFareService() { @Override public void processGtfs(FareRulesData fareRuleService, OtpTransitService transitService) { fillFareRules(fareRuleService.fareAttributes(), fareRuleService.fareRules(), regularFareRules); + // ORCA agencies don't rely on fare attributes without rules, so let's remove them. + regularFareRules.entrySet().removeIf(entry -> !entry.getValue().hasRules()); } /** diff --git a/src/ext/java/org/opentripplanner/ext/fares/model/FareAttribute.java b/src/ext/java/org/opentripplanner/ext/fares/model/FareAttribute.java index 2c93fb016fa..39a6c0bd1d0 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/model/FareAttribute.java +++ b/src/ext/java/org/opentripplanner/ext/fares/model/FareAttribute.java @@ -2,7 +2,6 @@ package org.opentripplanner.ext.fares.model; import java.util.Objects; -import javax.annotation.Nonnull; import org.opentripplanner.transit.model.basic.Money; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -77,7 +76,7 @@ public Integer getJourneyDuration() { } @Override - public boolean sameAs(@Nonnull FareAttribute other) { + public boolean sameAs(FareAttribute other) { return ( getId().equals(other.getId()) && getPrice().equals(other.getPrice()) && @@ -88,7 +87,6 @@ public boolean sameAs(@Nonnull FareAttribute other) { ); } - @Nonnull @Override public FareAttributeBuilder copy() { return new FareAttributeBuilder(this); diff --git a/src/ext/java/org/opentripplanner/ext/fares/model/FareLegRule.java b/src/ext/java/org/opentripplanner/ext/fares/model/FareLegRule.java index 28ef5dcab6c..984dcb0291a 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/model/FareLegRule.java +++ b/src/ext/java/org/opentripplanner/ext/fares/model/FareLegRule.java @@ -3,19 +3,18 @@ import java.util.Collection; import java.util.List; import java.util.Objects; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.model.fare.FareProduct; import org.opentripplanner.transit.model.framework.FeedScopedId; public record FareLegRule( - @Nonnull FeedScopedId id, + FeedScopedId id, @Nullable FeedScopedId legGroupId, @Nullable String networkId, @Nullable String fromAreaId, @Nullable String toAreaId, @Nullable FareDistance fareDistance, - @Nonnull Collection fareProducts + Collection fareProducts ) { public FareLegRule { Objects.requireNonNull(id); 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 30119631216..b8fbb0f1fe9 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/model/FareRuleSet.java +++ b/src/ext/java/org/opentripplanner/ext/fares/model/FareRuleSet.java @@ -40,6 +40,19 @@ public Set getRouteOriginDestinations() { return routeOriginDestinations; } + /** + * Determine whether the FareRuleSet has any rules added. + * @return True if any rules have been added. + */ + public boolean hasRules() { + return ( + !routes.isEmpty() || + !originDestinations.isEmpty() || + !routeOriginDestinations.isEmpty() || + !contains.isEmpty() + ); + } + public void addContains(String containsId) { contains.add(containsId); } @@ -60,6 +73,19 @@ public FareAttribute getFareAttribute() { return fareAttribute; } + /** + * Determines whether the FareRuleSet matches against a set of itinerary parameters + * based on the added rules and fare attribute + * @param startZone Origin zone + * @param endZone End zone + * @param zonesVisited A set containing the names of zones visited on the fare + * @param routesVisited A set containing the route IDs visited + * @param tripsVisited [Not implemented] A set containing the trip IDs visited + * @param transfersUsed Number of transfers already used + * @param tripTime Time from beginning of first leg to beginning of current leg to be evaluated + * @param journeyTime Total journey time from beginning of first leg to end of current leg + * @return True if this FareAttribute should apply to this leg + */ public boolean matches( String startZone, String endZone, diff --git a/src/ext/java/org/opentripplanner/ext/fares/model/FareTransferRule.java b/src/ext/java/org/opentripplanner/ext/fares/model/FareTransferRule.java index d3b2020771f..87917bc819c 100644 --- a/src/ext/java/org/opentripplanner/ext/fares/model/FareTransferRule.java +++ b/src/ext/java/org/opentripplanner/ext/fares/model/FareTransferRule.java @@ -2,18 +2,17 @@ import java.time.Duration; import java.util.Collection; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.model.fare.FareProduct; import org.opentripplanner.transit.model.framework.FeedScopedId; public record FareTransferRule( - @Nonnull FeedScopedId id, + FeedScopedId id, @Nullable FeedScopedId fromLegGroup, @Nullable FeedScopedId toLegGroup, int transferCount, @Nullable Duration timeLimit, - @Nonnull Collection fareProducts + Collection fareProducts ) { public String feedId() { return id.getFeedId(); diff --git a/src/ext/java/org/opentripplanner/ext/flex/AreaStopsToVerticesMapper.java b/src/ext/java/org/opentripplanner/ext/flex/AreaStopsToVerticesMapper.java index 612fb7a1d29..8b969c99344 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/AreaStopsToVerticesMapper.java +++ b/src/ext/java/org/opentripplanner/ext/flex/AreaStopsToVerticesMapper.java @@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableMultimap; import jakarta.inject.Inject; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.Point; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.logging.ProgressTracker; @@ -77,7 +76,6 @@ public void buildGraph() { LOG.info(progress.completeMessage()); } - @Nonnull private static Stream matchingVerticesForStop( StreetIndex streetIndex, AreaStop areaStop diff --git a/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java b/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java index 0544a46da72..24f996ca5fe 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java +++ b/src/ext/java/org/opentripplanner/ext/flex/FlexibleTransitLeg.java @@ -6,7 +6,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.LineString; import org.opentripplanner.ext.flex.edgetype.FlexTripEdge; import org.opentripplanner.framework.i18n.I18NString; @@ -96,7 +95,6 @@ public LegTime end() { } @Override - @Nonnull public TransitMode getMode() { return getTrip().getMode(); } diff --git a/src/ext/java/org/opentripplanner/ext/flex/edgetype/FlexTripEdge.java b/src/ext/java/org/opentripplanner/ext/flex/edgetype/FlexTripEdge.java index 40201295d7b..70b0fcdcb3c 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/edgetype/FlexTripEdge.java +++ b/src/ext/java/org/opentripplanner/ext/flex/edgetype/FlexTripEdge.java @@ -2,7 +2,6 @@ import java.time.LocalDate; import java.util.Objects; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.LineString; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPath; import org.opentripplanner.ext.flex.trip.FlexTrip; @@ -92,7 +91,6 @@ public double getDistanceMeters() { } @Override - @Nonnull public State[] traverse(State s0) { StateEditor editor = s0.edit(this); editor.setBackMode(TraverseMode.FLEX); diff --git a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexTrip.java b/src/ext/java/org/opentripplanner/ext/flex/trip/FlexTrip.java index 8fdb1b8fd5c..caceab676ce 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/FlexTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/FlexTrip.java @@ -3,7 +3,6 @@ import java.util.List; import java.util.Objects; import java.util.Set; -import javax.annotation.Nonnull; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.StopTime; @@ -140,7 +139,7 @@ public abstract FlexPathCalculator decorateFlexPathCalculator( ); @Override - public boolean sameAs(@Nonnull T other) { + public boolean sameAs(T other) { return getId().equals(other.getId()) && Objects.equals(trip, other.getTrip()); } } 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 5ea0cb9fb91..c8484532b68 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/ScheduledDeviatedTrip.java @@ -9,7 +9,6 @@ import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.ScheduledFlexPathCalculator; import org.opentripplanner.model.PickDrop; @@ -152,7 +151,7 @@ public boolean isAlightingPossible(StopLocation toStop) { } @Override - public boolean sameAs(@Nonnull ScheduledDeviatedTrip other) { + public boolean sameAs(ScheduledDeviatedTrip other) { return ( super.sameAs(other) && Arrays.equals(stopTimes, other.stopTimes) && @@ -161,7 +160,6 @@ public boolean sameAs(@Nonnull ScheduledDeviatedTrip other) { ); } - @Nonnull @Override public TransitBuilder copy() { return new ScheduledDeviatedTripBuilder(this); 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 20a77bb94ed..30c79ac0d1a 100644 --- a/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java +++ b/src/ext/java/org/opentripplanner/ext/flex/trip/UnscheduledTrip.java @@ -10,7 +10,6 @@ import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import org.opentripplanner.ext.flex.flexpathcalculator.FlexPathCalculator; import org.opentripplanner.ext.flex.flexpathcalculator.TimePenaltyCalculator; import org.opentripplanner.framework.lang.DoubleUtils; @@ -197,7 +196,7 @@ public boolean isAlightingPossible(StopLocation stop) { } @Override - public boolean sameAs(@Nonnull UnscheduledTrip other) { + public boolean sameAs(UnscheduledTrip other) { return ( super.sameAs(other) && Arrays.equals(stopTimes, other.stopTimes) && @@ -206,7 +205,6 @@ public boolean sameAs(@Nonnull UnscheduledTrip other) { ); } - @Nonnull @Override public TransitBuilder copy() { return new UnscheduledTripBuilder(this); diff --git a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java index 71b80ac58a6..764c40d3f86 100644 --- a/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java +++ b/src/ext/java/org/opentripplanner/ext/geocoder/LuceneIndex.java @@ -18,7 +18,7 @@ import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.codecs.Codec; import org.apache.lucene.codecs.PostingsFormat; -import org.apache.lucene.codecs.lucene99.Lucene99Codec; +import org.apache.lucene.codecs.lucene912.Lucene912Codec; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field.Store; import org.apache.lucene.document.StoredField; @@ -34,7 +34,7 @@ import org.apache.lucene.search.FuzzyQuery; import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.suggest.document.Completion99PostingsFormat; +import org.apache.lucene.search.suggest.document.Completion912PostingsFormat; import org.apache.lucene.search.suggest.document.CompletionAnalyzer; import org.apache.lucene.search.suggest.document.ContextQuery; import org.apache.lucene.search.suggest.document.ContextSuggestField; @@ -200,8 +200,8 @@ private StopCluster toStopCluster(Document document) { static IndexWriterConfig iwcWithSuggestField(Analyzer analyzer, final Set suggestFields) { IndexWriterConfig iwc = new IndexWriterConfig(analyzer); - Codec filterCodec = new Lucene99Codec() { - final PostingsFormat postingsFormat = new Completion99PostingsFormat(); + Codec filterCodec = new Lucene912Codec() { + final PostingsFormat postingsFormat = new Completion912PostingsFormat(); @Override public PostingsFormat getPostingsFormatForField(String field) { diff --git a/src/ext/java/org/opentripplanner/ext/reportapi/model/CachedValue.java b/src/ext/java/org/opentripplanner/ext/reportapi/model/CachedValue.java index 6a7dac61b23..acf5758fa66 100644 --- a/src/ext/java/org/opentripplanner/ext/reportapi/model/CachedValue.java +++ b/src/ext/java/org/opentripplanner/ext/reportapi/model/CachedValue.java @@ -3,7 +3,6 @@ import java.time.Duration; import java.time.Instant; import java.util.function.Supplier; -import javax.annotation.Nonnull; /** * The purpose of this class is to be a generic container for caching expensive computations. @@ -16,7 +15,7 @@ public class CachedValue { private T value; private Instant timeout; - public CachedValue(@Nonnull Duration cacheInterval) { + public CachedValue(Duration cacheInterval) { this.value = null; this.cacheInterval = cacheInterval; this.timeout = calculateTimeout(); @@ -27,7 +26,7 @@ public CachedValue(@Nonnull Duration cacheInterval) { *

* Otherwise, recompute and return it. */ - public T get(@Nonnull Supplier supplier) { + public T get(Supplier supplier) { synchronized (this) { if (hasExpired()) { this.value = supplier.get(); diff --git a/src/ext/java/org/opentripplanner/ext/reportapi/model/GraphReportBuilder.java b/src/ext/java/org/opentripplanner/ext/reportapi/model/GraphReportBuilder.java index 57d0872b9bd..7f819c70792 100644 --- a/src/ext/java/org/opentripplanner/ext/reportapi/model/GraphReportBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/reportapi/model/GraphReportBuilder.java @@ -4,7 +4,6 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Function; -import javax.annotation.Nonnull; import org.opentripplanner.standalone.api.OtpServerRequestContext; public class GraphReportBuilder { @@ -50,13 +49,11 @@ public static GraphStats build(OtpServerRequestContext context) { ); } - @Nonnull private static String firstLetterToLowerCase(Object instance) { var className = instance.getClass().getSimpleName(); return Character.toLowerCase(className.charAt(0)) + className.substring(1); } - @Nonnull private static TypeStats countValues(Collection input, Function classify) { Map result = new HashMap<>(); input.forEach(item -> { diff --git a/src/ext/java/org/opentripplanner/ext/reportapi/model/TransfersReport.java b/src/ext/java/org/opentripplanner/ext/reportapi/model/TransfersReport.java index 2cfe1b19c1c..e53cd6c3c58 100644 --- a/src/ext/java/org/opentripplanner/ext/reportapi/model/TransfersReport.java +++ b/src/ext/java/org/opentripplanner/ext/reportapi/model/TransfersReport.java @@ -4,7 +4,6 @@ import java.util.List; import java.util.Optional; -import javax.annotation.Nonnull; import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; @@ -196,7 +195,6 @@ private TxPoint pointInfo(TransferPoint p, boolean boarding) { return r; } - @Nonnull private static String getName(Operator operator) { return Optional.ofNullable(operator).map(o -> o.getId().getId()).orElse(""); } diff --git a/src/ext/java/org/opentripplanner/ext/ridehailing/model/RideHailingLeg.java b/src/ext/java/org/opentripplanner/ext/ridehailing/model/RideHailingLeg.java index 91a7a1fbf6f..94587333c4e 100644 --- a/src/ext/java/org/opentripplanner/ext/ridehailing/model/RideHailingLeg.java +++ b/src/ext/java/org/opentripplanner/ext/ridehailing/model/RideHailingLeg.java @@ -1,6 +1,5 @@ package org.opentripplanner.ext.ridehailing.model; -import javax.annotation.Nonnull; import org.opentripplanner.model.plan.StreetLeg; import org.opentripplanner.model.plan.StreetLegBuilder; @@ -27,7 +26,6 @@ public RideHailingProvider provider() { return provider; } - @Nonnull public RideEstimate rideEstimate() { return estimate; } diff --git a/src/ext/java/org/opentripplanner/ext/ridehailing/service/uber/UberService.java b/src/ext/java/org/opentripplanner/ext/ridehailing/service/uber/UberService.java index 9b276fc053e..d5ab1e8c5fa 100644 --- a/src/ext/java/org/opentripplanner/ext/ridehailing/service/uber/UberService.java +++ b/src/ext/java/org/opentripplanner/ext/ridehailing/service/uber/UberService.java @@ -13,7 +13,6 @@ import java.util.Currency; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; import org.opentripplanner.ext.ridehailing.CachingRideHailingService; import org.opentripplanner.ext.ridehailing.RideHailingServiceParameters; import org.opentripplanner.ext.ridehailing.model.ArrivalTime; @@ -191,7 +190,6 @@ private boolean filterRides(Ride a, boolean wheelchairAccessible) { } } - @Nonnull private Map headers() throws IOException { return Map.ofEntries( entry(AUTHORIZATION, "Bearer %s".formatted(oauthService.getToken())), diff --git a/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java b/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java index cdbaa8f3d4c..a6894f0748a 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java @@ -14,7 +14,6 @@ import java.util.List; import java.util.Objects; import java.util.function.Function; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.ext.siri.mapper.PickDropMapper; import org.opentripplanner.framework.i18n.NonLocalizedString; @@ -210,13 +209,6 @@ Result build() { // TODO: We always create a new TripPattern to be able to modify its scheduled timetable StopPattern stopPattern = new StopPattern(aimedStopTimes); - TripPattern pattern = TripPattern - .of(getTripPatternId.apply(trip)) - .withRoute(trip.getRoute()) - .withMode(trip.getMode()) - .withNetexSubmode(trip.getNetexSubMode()) - .withStopPattern(stopPattern) - .build(); RealTimeTripTimes tripTimes = TripTimesFactory.tripTimes( trip, @@ -229,7 +221,16 @@ Result build() { // therefore they must be valid tripTimes.validateNonIncreasingTimes(); tripTimes.setServiceCode(transitService.getServiceCodeForId(trip.getServiceId())); - pattern.add(tripTimes); + + TripPattern pattern = TripPattern + .of(getTripPatternId.apply(trip)) + .withRoute(trip.getRoute()) + .withMode(trip.getMode()) + .withNetexSubmode(trip.getNetexSubMode()) + .withStopPattern(stopPattern) + .withScheduledTimeTableBuilder(builder -> builder.addTripTimes(tripTimes)) + .build(); + RealTimeTripTimes updatedTripTimes = tripTimes.copyScheduledTimes(); // Loop through calls again and apply updates @@ -284,7 +285,6 @@ Result build() { * * @return a new Route */ - @Nonnull private Route createRoute(Agency agency) { var routeBuilder = Route.of(entityResolver.resolveId(lineRef)); diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriFuzzyTripMatcher.java b/src/ext/java/org/opentripplanner/ext/siri/SiriFuzzyTripMatcher.java index ed5e9818b32..5ce0b114371 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriFuzzyTripMatcher.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriFuzzyTripMatcher.java @@ -10,7 +10,6 @@ import java.util.Set; import java.util.function.BiFunction; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.model.Timetable; @@ -196,7 +195,6 @@ private static String createStartStopKey(String lastStopId, int lastStopArrivalT return lastStopId + ":" + lastStopArrivalTime; } - @Nonnull private Set getMatchingTripsOnStopOrSiblings( String lastStopPoint, ZonedDateTime arrivalTime, diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriTripPatternCache.java b/src/ext/java/org/opentripplanner/ext/siri/SiriTripPatternCache.java index 40c008d0004..01ae306b861 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriTripPatternCache.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriTripPatternCache.java @@ -4,7 +4,6 @@ import java.util.HashMap; import java.util.Map; import java.util.function.Function; -import javax.annotation.Nonnull; import org.opentripplanner.transit.model.network.StopPattern; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.Trip; @@ -71,9 +70,9 @@ public SiriTripPatternCache( * @return cached or newly created trip pattern */ public synchronized TripPattern getOrCreateTripPattern( - @Nonnull final StopPattern stopPattern, - @Nonnull final Trip trip, - @Nonnull LocalDate serviceDate + final StopPattern stopPattern, + final Trip trip, + LocalDate serviceDate ) { TripPattern originalTripPattern = getPatternForTrip.apply(trip); diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/google/SiriETGooglePubsubUpdaterParameters.java b/src/ext/java/org/opentripplanner/ext/siri/updater/google/SiriETGooglePubsubUpdaterParameters.java index c9e070311c6..934b6d311c8 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/google/SiriETGooglePubsubUpdaterParameters.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/google/SiriETGooglePubsubUpdaterParameters.java @@ -2,13 +2,12 @@ import java.time.Duration; import java.util.Objects; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.updater.trip.UrlUpdaterParameters; public record SiriETGooglePubsubUpdaterParameters( - @Nonnull String configRef, + String configRef, @Nullable String feedId, String subscriptionProjectName, String topicProjectName, diff --git a/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSourceParameters.java b/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSourceParameters.java index ca0f5497601..5aa834554c7 100644 --- a/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSourceParameters.java +++ b/src/ext/java/org/opentripplanner/ext/smoovebikerental/SmooveBikeRentalDataSourceParameters.java @@ -1,6 +1,5 @@ package org.opentripplanner.ext.smoovebikerental; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.vehicle_rental.VehicleRentalSourceType; @@ -26,7 +25,6 @@ public String getNetwork(String defaultValue) { return network == null || network.isEmpty() ? defaultValue : network; } - @Nonnull @Override public VehicleRentalSourceType sourceType() { return VehicleRentalSourceType.SMOOVE; diff --git a/src/ext/java/org/opentripplanner/ext/stopconsolidation/StopConsolidationModule.java b/src/ext/java/org/opentripplanner/ext/stopconsolidation/StopConsolidationModule.java index 100941d88d4..7f5cf431a4c 100644 --- a/src/ext/java/org/opentripplanner/ext/stopconsolidation/StopConsolidationModule.java +++ b/src/ext/java/org/opentripplanner/ext/stopconsolidation/StopConsolidationModule.java @@ -4,7 +4,6 @@ import java.util.Collection; import java.util.List; import java.util.Objects; -import javax.annotation.Nonnull; import org.opentripplanner.datastore.api.DataSource; import org.opentripplanner.ext.stopconsolidation.internal.DefaultStopConsolidationService; import org.opentripplanner.ext.stopconsolidation.model.ConsolidatedStopGroup; @@ -62,7 +61,6 @@ public void buildGraph() { }); } - @Nonnull private TripPattern modifyStopsInPattern( TripPattern pattern, List replacements diff --git a/src/ext/java/org/opentripplanner/ext/stopconsolidation/internal/DefaultStopConsolidationService.java b/src/ext/java/org/opentripplanner/ext/stopconsolidation/internal/DefaultStopConsolidationService.java index 9f31e366be5..53a0f8ed827 100644 --- a/src/ext/java/org/opentripplanner/ext/stopconsolidation/internal/DefaultStopConsolidationService.java +++ b/src/ext/java/org/opentripplanner/ext/stopconsolidation/internal/DefaultStopConsolidationService.java @@ -4,7 +4,6 @@ import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.opentripplanner.ext.stopconsolidation.StopConsolidationRepository; import org.opentripplanner.ext.stopconsolidation.StopConsolidationService; import org.opentripplanner.ext.stopconsolidation.model.ConsolidatedStopGroup; @@ -86,7 +85,6 @@ public StopLocation agencySpecificStop(StopLocation stop, Agency agency) { } } - @Nonnull private Optional findAgencySpecificStop(StopLocation stop, Agency agency) { return repo .groups() diff --git a/src/ext/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/DigitransitVehicleParkingPropertyMapper.java b/src/ext/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/DigitransitVehicleParkingPropertyMapper.java index 892d4907395..951552e1fdc 100644 --- a/src/ext/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/DigitransitVehicleParkingPropertyMapper.java +++ b/src/ext/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/DigitransitVehicleParkingPropertyMapper.java @@ -4,7 +4,6 @@ import java.util.Collection; import java.util.List; import java.util.Locale; -import javax.annotation.Nonnull; import org.opentripplanner.apis.support.mapping.PropertyMapper; import org.opentripplanner.framework.i18n.I18NStringMapper; import org.opentripplanner.inspector.vector.KeyValue; @@ -27,7 +26,6 @@ protected Collection map(VehicleParking vehicleParking) { return basicMapping(vehicleParking); } - @Nonnull protected ArrayList basicMapping(VehicleParking vehicleParking) { return new ArrayList<>( List.of( diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdater.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdater.java deleted file mode 100644 index d5ef7e63d13..00000000000 --- a/src/ext/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdater.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.opentripplanner.ext.vehicleparking.noi; - -import com.fasterxml.jackson.databind.JsonNode; -import org.opentripplanner.framework.geometry.WgsCoordinate; -import org.opentripplanner.framework.i18n.NonLocalizedString; -import org.opentripplanner.framework.tostring.ToStringBuilder; -import org.opentripplanner.routing.vehicle_parking.VehicleParking; -import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; -import org.opentripplanner.routing.vehicle_parking.VehicleParkingState; -import org.opentripplanner.transit.model.framework.FeedScopedId; -import org.opentripplanner.updater.spi.GenericJsonDataSource; - -/** - * Vehicle parking updater class for NOI's open data hub format APIs. - */ -public class NoiUpdater extends GenericJsonDataSource { - - private static final String JSON_PARSE_PATH = "data/stations"; - private final NoiUpdaterParameters params; - - public NoiUpdater(NoiUpdaterParameters parameters) { - super(parameters.url().toString(), JSON_PARSE_PATH, parameters.httpHeaders()); - this.params = parameters; - } - - @Override - protected VehicleParking parseElement(JsonNode jsonNode) { - var type = jsonNode.path("type").textValue(); - VehicleParkingSpaces capacity, availability; - if (type.equals("station")) { - capacity = extractSpaces(jsonNode, "capacity"); - availability = extractSpaces(jsonNode, "free"); - } else if (type.equals("sensor")) { - capacity = VehicleParkingSpaces.builder().carSpaces(1).build(); - var isFree = jsonNode.path("free").asBoolean(); - availability = VehicleParkingSpaces.builder().carSpaces(isFree ? 1 : 0).build(); - } else { - throw new IllegalArgumentException("Unknown type '%s'".formatted(type)); - } - - var vehicleParkId = new FeedScopedId(params.feedId(), jsonNode.path("station_id").asText()); - var name = new NonLocalizedString(jsonNode.path("name").asText()); - double lat = jsonNode.path("lat").asDouble(); - double lon = jsonNode.path("lon").asDouble(); - var coordinate = new WgsCoordinate(lat, lon); - VehicleParking.VehicleParkingEntranceCreator entrance = builder -> - builder - .entranceId(new FeedScopedId(params.feedId(), vehicleParkId.getId() + "/entrance")) - .coordinate(coordinate) - .walkAccessible(true) - .carAccessible(true); - - return VehicleParking - .builder() - .id(vehicleParkId) - .name(name) - .state(VehicleParkingState.OPERATIONAL) - .coordinate(coordinate) - .capacity(capacity) - .availability(availability) - .carPlaces(true) - .entrance(entrance) - .build(); - } - - private static VehicleParkingSpaces extractSpaces(JsonNode jsonNode, String free) { - return VehicleParkingSpaces.builder().carSpaces(jsonNode.get(free).asInt()).build(); - } - - @Override - public String toString() { - return ToStringBuilder - .of(this.getClass()) - .addStr("url", this.params.url().toString()) - .toString(); - } -} diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdaterParameters.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdaterParameters.java deleted file mode 100644 index b34769c9dd2..00000000000 --- a/src/ext/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdaterParameters.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.opentripplanner.ext.vehicleparking.noi; - -import static org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters.UpdateType.FULL; - -import java.net.URI; -import java.time.Duration; -import org.opentripplanner.updater.spi.HttpHeaders; -import org.opentripplanner.updater.vehicle_parking.VehicleParkingSourceType; -import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters; - -/** - * Class that extends {@link VehicleParkingUpdaterParameters} with parameters required by {@link - * NoiUpdater}. - */ -public record NoiUpdaterParameters( - String configRef, - URI url, - String feedId, - Duration frequency, - HttpHeaders httpHeaders -) - implements VehicleParkingUpdaterParameters { - @Override - public VehicleParkingSourceType sourceType() { - return VehicleParkingSourceType.NOI_OPEN_DATA_HUB; - } - - @Override - public UpdateType updateType() { - return FULL; - } -} diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/sirifm/SiriFmUpdaterParameters.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/sirifm/SiriFmUpdaterParameters.java index 5afdffb7067..daecb2b2bb9 100644 --- a/src/ext/java/org/opentripplanner/ext/vehicleparking/sirifm/SiriFmUpdaterParameters.java +++ b/src/ext/java/org/opentripplanner/ext/vehicleparking/sirifm/SiriFmUpdaterParameters.java @@ -5,14 +5,13 @@ import java.net.URI; import java.time.Duration; -import org.opentripplanner.ext.vehicleparking.noi.NoiUpdater; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.vehicle_parking.VehicleParkingSourceType; import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters; /** * Class that extends {@link VehicleParkingUpdaterParameters} with parameters required by {@link - * NoiUpdater}. + * SiriFmDatasource}. */ public record SiriFmUpdaterParameters( String configRef, diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLRequestContext.java b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLRequestContext.java index dfc3e2d75f8..192cbb26aa9 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLRequestContext.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLRequestContext.java @@ -1,6 +1,5 @@ package org.opentripplanner.apis.gtfs; -import javax.annotation.Nonnull; import org.opentripplanner.routing.api.RoutingService; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.fares.FareService; @@ -38,7 +37,6 @@ public static GraphQLRequestContext ofServerContext(OtpServerRequestContext cont * Returns a clone of the default route request. The clone is necessary because one HTTP * request can lead to several GraphQL queries, for example through batch or alias queries. */ - @Nonnull @Override public RouteRequest defaultRouteRequest() { return defaultRouteRequest.clone(); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java index 8ec3172db52..25953652365 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/GraphQLScalars.java @@ -16,7 +16,6 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Optional; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.Geometry; import org.opentripplanner.framework.graphql.scalar.DateScalarFactory; import org.opentripplanner.framework.graphql.scalar.DurationScalarFactory; @@ -65,8 +64,7 @@ public String parseLiteral(Object input) { .coercing( new Coercing() { @Override - public String serialize(@Nonnull Object dataFetcherResult) - throws CoercingSerializeException { + public String serialize(Object dataFetcherResult) throws CoercingSerializeException { if (dataFetcherResult instanceof ZonedDateTime zdt) { return zdt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); } else if (dataFetcherResult instanceof OffsetDateTime odt) { @@ -123,8 +121,7 @@ public OffsetDateTime parseLiteral(Object input) throws CoercingParseLiteralExce private static final String VALIDATION_ERROR_MESSAGE = "Not a valid WGS84 coordinate value"; @Override - public Double serialize(@Nonnull Object dataFetcherResult) - throws CoercingSerializeException { + public Double serialize(Object dataFetcherResult) throws CoercingSerializeException { if (dataFetcherResult instanceof Double doubleValue) { return doubleValue; } else if (dataFetcherResult instanceof Float floatValue) { @@ -188,8 +185,7 @@ private static Optional validateCoordinate(double coordinate) { "Cost cannot be negative or greater than %d".formatted(MAX_COST); @Override - public Integer serialize(@Nonnull Object dataFetcherResult) - throws CoercingSerializeException { + public Integer serialize(Object dataFetcherResult) throws CoercingSerializeException { if (dataFetcherResult instanceof Integer intValue) { return intValue; } else if (dataFetcherResult instanceof Cost costValue) { @@ -358,8 +354,7 @@ public Grams parseLiteral(Object input) throws CoercingParseLiteralException { "Value is under 0 or greater than 1."; @Override - public Double serialize(@Nonnull Object dataFetcherResult) - throws CoercingSerializeException { + public Double serialize(Object dataFetcherResult) throws CoercingSerializeException { var validationException = new CoercingSerializeException(VALIDATION_ERROR_MESSAGE); if (dataFetcherResult instanceof Double doubleValue) { return validateRatio(doubleValue).orElseThrow(() -> validationException); @@ -425,8 +420,7 @@ private static Optional validateRatio(double ratio) { "Reluctance needs to be between %s and %s".formatted(MIN_Reluctance, MAX_Reluctance); @Override - public Double serialize(@Nonnull Object dataFetcherResult) - throws CoercingSerializeException { + public Double serialize(Object dataFetcherResult) throws CoercingSerializeException { if (dataFetcherResult instanceof Double doubleValue) { return doubleValue; } else if (dataFetcherResult instanceof Float floatValue) { diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java index 9ec83a4bf67..8d84c7f0b03 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/LegImpl.java @@ -10,6 +10,7 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.apis.gtfs.mapping.NumberMapper; +import org.opentripplanner.apis.gtfs.mapping.RealtimeStateMapper; import org.opentripplanner.ext.restapi.mapping.LocalDateMapper; import org.opentripplanner.ext.ridehailing.model.RideEstimate; import org.opentripplanner.ext.ridehailing.model.RideHailingLeg; @@ -23,6 +24,7 @@ import org.opentripplanner.model.plan.StreetLeg; import org.opentripplanner.model.plan.TransitLeg; import org.opentripplanner.model.plan.WalkStep; +import org.opentripplanner.model.plan.legreference.LegReferenceSerializer; import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.routing.alternativelegs.AlternativeLegs; import org.opentripplanner.routing.alternativelegs.AlternativeLegsFilter; @@ -189,10 +191,12 @@ public DataFetcher realTime() { return environment -> getSource(environment).getRealTime(); } - // TODO @Override - public DataFetcher realtimeState() { - return environment -> null; + public DataFetcher realtimeState() { + return environment -> { + var state = getSource(environment).getRealTimeState(); + return RealtimeStateMapper.map(state); + }; } @Override @@ -324,4 +328,15 @@ public DataFetcher> nextLegs() { public DataFetcher accessibilityScore() { return environment -> NumberMapper.toDouble(getSource(environment).accessibilityScore()); } + + @Override + public DataFetcher id() { + return environment -> { + var ref = getSource(environment).getLegReference(); + if (ref == null) { + return null; + } + return LegReferenceSerializer.encode(ref); + }; + } } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java index 0943fa309fc..95984ba6dd0 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java @@ -42,6 +42,9 @@ import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.gtfs.mapping.DirectionMapper; import org.opentripplanner.model.TripTimeOnDate; +import org.opentripplanner.model.plan.Leg; +import org.opentripplanner.model.plan.legreference.LegReference; +import org.opentripplanner.model.plan.legreference.LegReferenceSerializer; import org.opentripplanner.routing.alertpatch.EntitySelector; import org.opentripplanner.routing.alertpatch.TransitAlert; import org.opentripplanner.routing.api.request.RouteRequest; @@ -362,6 +365,20 @@ public DataFetcher> nearest() { }; } + @Override + public DataFetcher leg() { + return environment -> { + TransitService transitService = getTransitService(environment); + var args = new GraphQLTypes.GraphQLQueryTypeLegArgs(environment.getArguments()); + String id = args.getGraphQLId(); + LegReference ref = LegReferenceSerializer.decode(id); + if (ref == null) { + return null; + } + return ref.getLeg(transitService); + }; + } + @Override public DataFetcher node() { return environment -> { diff --git a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java index faf59ef9d6e..bc14d15fbf2 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/datafetchers/StoptimeImpl.java @@ -3,10 +3,11 @@ import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import org.opentripplanner.apis.gtfs.generated.GraphQLDataFetchers; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.apis.gtfs.mapping.RealtimeStateMapper; import org.opentripplanner.framework.graphql.GraphQLUtils; import org.opentripplanner.model.StopTime; import org.opentripplanner.model.TripTimeOnDate; -import org.opentripplanner.transit.model.timetable.RealTimeState; import org.opentripplanner.transit.model.timetable.Trip; public class StoptimeImpl implements GraphQLDataFetchers.GraphQLStoptime { @@ -67,11 +68,11 @@ public DataFetcher realtimeDeparture() { } @Override - public DataFetcher realtimeState() { + public DataFetcher realtimeState() { return environment -> getSource(environment).isCanceledEffectively() - ? RealTimeState.CANCELED.name() - : getSource(environment).getRealTimeState().name(); + ? GraphQLTypes.GraphQLRealtimeState.CANCELED + : RealtimeStateMapper.map(getSource(environment).getRealTimeState()); } @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 67944543580..7532faf28bd 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLDataFetchers.java @@ -18,6 +18,7 @@ import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLInputField; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLOccupancyStatus; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRealtimeState; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRelativeDirection; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRoutingErrorCode; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLTransitMode; @@ -482,6 +483,8 @@ public interface GraphQLLeg { public DataFetcher headsign(); + public DataFetcher id(); + public DataFetcher interlineWithPreviousLeg(); public DataFetcher intermediatePlace(); @@ -502,7 +505,7 @@ public interface GraphQLLeg { public DataFetcher realTime(); - public DataFetcher realtimeState(); + public DataFetcher realtimeState(); public DataFetcher rentedBike(); @@ -779,6 +782,8 @@ public interface GraphQLQueryType { public DataFetcher fuzzyTrip(); + public DataFetcher leg(); + public DataFetcher> nearest(); public DataFetcher node(); @@ -1078,7 +1083,7 @@ public interface GraphQLStoptime { public DataFetcher realtimeDeparture(); - public DataFetcher realtimeState(); + public DataFetcher realtimeState(); public DataFetcher scheduledArrival(); diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java index ed3e9afefc9..3cd98b15652 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/GraphQLTypes.java @@ -2432,6 +2432,25 @@ public void setGraphQLTime(Integer time) { } } + public static class GraphQLQueryTypeLegArgs { + + private String id; + + public GraphQLQueryTypeLegArgs(Map args) { + if (args != null) { + this.id = (String) args.get("id"); + } + } + + public String getGraphQLId() { + return this.id; + } + + public void setGraphQLId(String id) { + this.id = id; + } + } + public static class GraphQLQueryTypeNearestArgs { private String after; 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 29490a28b78..2a7f2a95ba0 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 @@ -84,7 +84,7 @@ config: PlanConnection: graphql.execution.DataFetcherResult PlanEdge: graphql.relay.DefaultEdge#DefaultEdge PlanPageInfo: org.opentripplanner.apis.gtfs.model.PlanPageInfo#PlanPageInfo - RealtimeState: String + RealtimeState: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRealtimeState#GraphQLRealtimeState RelativeDirection: org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRelativeDirection#GraphQLRelativeDirection Route: org.opentripplanner.transit.model.network.Route#Route RoutingError: org.opentripplanner.routing.api.response.RoutingError#RoutingError diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapper.java index cd04601d599..79f55f1e50a 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/BikesAllowedMapper.java @@ -1,13 +1,11 @@ package org.opentripplanner.apis.gtfs.mapping; -import javax.annotation.Nonnull; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLBikesAllowed; import org.opentripplanner.transit.model.network.BikeAccess; public class BikesAllowedMapper { - @Nonnull - public static GraphQLBikesAllowed map(@Nonnull BikeAccess bikesAllowed) { + public static GraphQLBikesAllowed map(BikeAccess bikesAllowed) { return switch (bikesAllowed) { case UNKNOWN -> GraphQLBikesAllowed.NO_INFORMATION; case ALLOWED -> GraphQLBikesAllowed.ALLOWED; diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java index 1deb6e3bb0e..69a78b05f55 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/DirectionMapper.java @@ -1,6 +1,5 @@ package org.opentripplanner.apis.gtfs.mapping; -import javax.annotation.Nonnull; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLAbsoluteDirection; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes.GraphQLRelativeDirection; @@ -9,7 +8,6 @@ public final class DirectionMapper { - @Nonnull public static GraphQLAbsoluteDirection map(AbsoluteDirection dir) { return switch (dir) { case NORTH -> GraphQLAbsoluteDirection.NORTH; @@ -23,7 +21,6 @@ public static GraphQLAbsoluteDirection map(AbsoluteDirection dir) { }; } - @Nonnull public static GraphQLRelativeDirection map(RelativeDirection relativeDirection) { return switch (relativeDirection) { case DEPART -> GraphQLRelativeDirection.DEPART; diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/RealtimeStateMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/RealtimeStateMapper.java new file mode 100644 index 00000000000..6bd46466546 --- /dev/null +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/RealtimeStateMapper.java @@ -0,0 +1,22 @@ +package org.opentripplanner.apis.gtfs.mapping; + +import javax.annotation.Nullable; +import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; +import org.opentripplanner.transit.model.timetable.RealTimeState; + +/** + * Maps from the internal model to the API one. + */ +public class RealtimeStateMapper { + + public static GraphQLTypes.GraphQLRealtimeState map(@Nullable RealTimeState state) { + if (state == null) return null; + return switch (state) { + case SCHEDULED -> GraphQLTypes.GraphQLRealtimeState.SCHEDULED; + case UPDATED -> GraphQLTypes.GraphQLRealtimeState.UPDATED; + case CANCELED, DELETED -> GraphQLTypes.GraphQLRealtimeState.CANCELED; + case ADDED -> GraphQLTypes.GraphQLRealtimeState.ADDED; + case MODIFIED -> GraphQLTypes.GraphQLRealtimeState.MODIFIED; + }; + } +} diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ArgumentUtils.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ArgumentUtils.java index b04affeaaf2..8541bd1c3c5 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ArgumentUtils.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/ArgumentUtils.java @@ -7,7 +7,6 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; @@ -63,7 +62,6 @@ static Map getParking(DataFetchingEnvironment environment, Strin *

* TODO this ugliness can be removed when the bug gets fixed */ - @Nonnull static Collection> getParkingFilters( DataFetchingEnvironment environment, String type @@ -83,7 +81,6 @@ static Collection> getParkingFilters( *

* TODO this ugliness can be removed when the bug gets fixed */ - @Nonnull static Collection> getParkingPreferred( DataFetchingEnvironment environment, String type @@ -103,7 +100,6 @@ static Set parseSelectFilters(Collection> filters) { return parseFilters(filters, "select"); } - @Nonnull private static Set parseFilters(Collection> filters, String key) { return filters .stream() diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/LegacyRouteRequestMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/LegacyRouteRequestMapper.java index 050127d9a27..696ce9c45b5 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/LegacyRouteRequestMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/LegacyRouteRequestMapper.java @@ -12,7 +12,6 @@ import java.util.Map; import java.util.function.Consumer; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import org.opentripplanner.api.common.LocationStringParser; import org.opentripplanner.api.parameter.QualifiedMode; import org.opentripplanner.api.parameter.QualifiedModeSet; @@ -35,7 +34,6 @@ public class LegacyRouteRequestMapper { - @Nonnull public static RouteRequest toRouteRequest( DataFetchingEnvironment environment, GraphQLRequestContext context diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/OptimizationTypeMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/OptimizationTypeMapper.java index dc17891f9d2..6e848ce7f66 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/OptimizationTypeMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/OptimizationTypeMapper.java @@ -1,12 +1,10 @@ package org.opentripplanner.apis.gtfs.mapping.routerequest; -import javax.annotation.Nonnull; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.routing.core.VehicleRoutingOptimizeType; public final class OptimizationTypeMapper { - @Nonnull public static VehicleRoutingOptimizeType map(GraphQLTypes.GraphQLOptimizeType optimizeType) { return switch (optimizeType) { case QUICK -> VehicleRoutingOptimizeType.SHORTEST_DURATION; diff --git a/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapper.java b/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapper.java index ada358d98b3..7a13d8d38a0 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapper.java +++ b/src/main/java/org/opentripplanner/apis/gtfs/mapping/routerequest/RouteRequestMapper.java @@ -9,7 +9,6 @@ import graphql.schema.DataFetchingEnvironment; import java.time.Instant; -import javax.annotation.Nonnull; import org.opentripplanner.apis.gtfs.GraphQLRequestContext; import org.opentripplanner.apis.gtfs.generated.GraphQLTypes; import org.opentripplanner.framework.graphql.GraphQLUtils; @@ -22,7 +21,6 @@ public class RouteRequestMapper { - @Nonnull public static RouteRequest toRouteRequest( DataFetchingEnvironment environment, GraphQLRequestContext context diff --git a/src/main/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchema.java b/src/main/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchema.java index ed69cb44e59..6cf1ec1b1d8 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchema.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/TransmodelGraphQLSchema.java @@ -1568,7 +1568,7 @@ private GraphQLSchema create() { GraphQLFieldDefinition .newFieldDefinition() .name("leg") - .description("Refetch a single leg based on its id") + .description("Refetch a single transit leg based on its id") .withDirective(TransmodelDirectives.TIMING_DATA) .type(LegType.REF) .argument( @@ -1595,7 +1595,9 @@ private GraphQLSchema create() { GraphQLFieldDefinition .newFieldDefinition() .name("serverInfo") - .description("Get OTP server information") + .description( + "Get OTP deployment information. This is only useful for developers of OTP itself not regular API users." + ) .withDirective(TransmodelDirectives.TIMING_DATA) .type(new GraphQLNonNull(serverInfoType)) .dataFetcher(e -> projectInfo()) diff --git a/src/main/java/org/opentripplanner/apis/transmodel/mapping/TransitIdMapper.java b/src/main/java/org/opentripplanner/apis/transmodel/mapping/TransitIdMapper.java index 1554a8c9d7c..1231ebd73b6 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/mapping/TransitIdMapper.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/mapping/TransitIdMapper.java @@ -4,7 +4,6 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.lang.StringUtils; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; @@ -39,7 +38,6 @@ public static String mapIDToApi(FeedScopedId id) { * Return an empty collection if the collection of ids is null. * If the collection of ids contains null or blank elements, they are ignored. */ - @Nonnull public static List mapIDsToDomainNullSafe(@Nullable Collection ids) { if (ids == null) { return List.of(); diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/framework/ServerInfoType.java b/src/main/java/org/opentripplanner/apis/transmodel/model/framework/ServerInfoType.java index ae6889ab033..8f679cafda3 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/framework/ServerInfoType.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/framework/ServerInfoType.java @@ -9,6 +9,7 @@ import graphql.schema.GraphQLFieldDefinition; import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLOutputType; +import org.opentripplanner.apis.transmodel.support.GqlUtil; public class ServerInfoType { @@ -16,6 +17,13 @@ public static GraphQLOutputType create() { return GraphQLObjectType .newObject() .name("ServerInfo") + .description( + """ + Information about the deployment. This is only useful to developers of OTP itself. + It is not recommended for regular API consumers to use this type as it has no + stability guarantees. + """ + ) .field( GraphQLFieldDefinition .newFieldDefinition() @@ -99,6 +107,21 @@ public static GraphQLOutputType create() { .dataFetcher(e -> projectInfo().getOtpSerializationVersionId()) .build() ) + .field( + GraphQLFieldDefinition + .newFieldDefinition() + .name("internalTransitModelTimeZone") + .description( + """ + The internal time zone of the transit data. + + Note: The input data can be in several time zones, but OTP internally operates on a single one. + """ + ) + .type(Scalars.GraphQLString) + .dataFetcher(e -> GqlUtil.getTransitService(e).getTimeZone()) + .build() + ) .build(); } } diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java b/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java index ec7b736f7a6..0e167fe9cb0 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java @@ -65,7 +65,9 @@ public static GraphQLObjectType create( GraphQLFieldDefinition .newFieldDefinition() .name("id") - .description("An identifier for the leg, which can be used to re-fetch the information.") + .description( + "An identifier for the leg, which can be used to re-fetch transit leg information." + ) .type(Scalars.GraphQLID) .dataFetcher(env -> LegReferenceSerializer.encode(leg(env).getLegReference())) .build() diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/scalars/DateTimeScalarFactory.java b/src/main/java/org/opentripplanner/apis/transmodel/model/scalars/DateTimeScalarFactory.java index 9738a277a41..f539715206e 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/scalars/DateTimeScalarFactory.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/scalars/DateTimeScalarFactory.java @@ -12,7 +12,6 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.time.temporal.TemporalAccessor; -import javax.annotation.Nonnull; import org.opentripplanner.framework.time.OffsetDateTimeParser; public final class DateTimeScalarFactory { @@ -41,7 +40,7 @@ public static GraphQLScalarType createMillisecondsSinceEpochAsDateTimeStringScal .coercing( new Coercing<>() { @Override - public String serialize(@Nonnull Object input) { + public String serialize(Object input) { if (input instanceof Long inputAsLong) { return Instant.ofEpochMilli(inputAsLong).atZone(timeZone).format(FORMATTER); } @@ -49,7 +48,7 @@ public String serialize(@Nonnull Object input) { } @Override - public Long parseValue(@Nonnull Object input) { + public Long parseValue(Object input) { Instant instant = null; if (input instanceof CharSequence inputAsCharSequence) { try { @@ -80,7 +79,7 @@ public Long parseValue(@Nonnull Object input) { } @Override - public Long parseLiteral(@Nonnull Object input) { + public Long parseLiteral(Object input) { if (input instanceof StringValue inputAsStringValue) { return parseValue(inputAsStringValue.getValue()); } diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/scalars/DoubleFunctionFactory.java b/src/main/java/org/opentripplanner/apis/transmodel/model/scalars/DoubleFunctionFactory.java index fd45b001642..5259c0dd9db 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/scalars/DoubleFunctionFactory.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/scalars/DoubleFunctionFactory.java @@ -10,7 +10,6 @@ import graphql.schema.GraphQLScalarType; import java.util.Locale; import java.util.NoSuchElementException; -import javax.annotation.Nonnull; import org.opentripplanner.routing.api.request.framework.LinearFunctionSerialization; public class DoubleFunctionFactory { @@ -40,9 +39,9 @@ public static GraphQLScalarType createDoubleFunctionScalar() { new Coercing() { @Override public String serialize( - @Nonnull Object dataFetcherResult, - @Nonnull GraphQLContext graphQLContext, - @Nonnull Locale locale + Object dataFetcherResult, + GraphQLContext graphQLContext, + Locale locale ) { var value = (DoubleFunction) dataFetcherResult; return LinearFunctionSerialization.serialize(value.constant(), value.coefficient()); @@ -50,9 +49,9 @@ public String serialize( @Override public DoubleFunction parseValue( - @Nonnull Object input, - @Nonnull GraphQLContext graphQLContext, - @Nonnull Locale locale + Object input, + GraphQLContext graphQLContext, + Locale locale ) throws CoercingParseValueException { try { String text = (String) input; @@ -64,10 +63,10 @@ public DoubleFunction parseValue( @Override public DoubleFunction parseLiteral( - @Nonnull Value input, - @Nonnull CoercedVariables variables, - @Nonnull GraphQLContext graphQLContext, - @Nonnull Locale locale + Value input, + CoercedVariables variables, + GraphQLContext graphQLContext, + Locale locale ) throws CoercingParseLiteralException { if (input instanceof StringValue stringValue) { return parseValue(stringValue.getValue(), graphQLContext, locale); diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/DatedServiceJourneyQuery.java b/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/DatedServiceJourneyQuery.java index 0f4557fb755..aef659901e4 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/DatedServiceJourneyQuery.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/DatedServiceJourneyQuery.java @@ -10,14 +10,14 @@ import graphql.schema.GraphQLOutputType; import java.time.LocalDate; import java.util.List; -import java.util.stream.Stream; import org.opentripplanner.apis.transmodel.mapping.TransitIdMapper; import org.opentripplanner.apis.transmodel.model.EnumTypes; import org.opentripplanner.apis.transmodel.model.framework.TransmodelScalars; import org.opentripplanner.apis.transmodel.support.GqlUtil; +import org.opentripplanner.transit.api.request.TripOnServiceDateRequest; +import org.opentripplanner.transit.api.request.TripOnServiceDateRequestBuilder; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.timetable.TripAlteration; -import org.opentripplanner.transit.model.timetable.TripOnServiceDate; /** * A GraphQL query for retrieving data on DatedServiceJourneys @@ -93,72 +93,34 @@ public static GraphQLFieldDefinition createQuery(GraphQLOutputType datedServiceJ .type(new GraphQLList(new GraphQLNonNull(Scalars.GraphQLString))) ) .dataFetcher(environment -> { - Stream stream = GqlUtil - .getTransitService(environment) - .getAllTripOnServiceDates() - .stream(); - + // The null safety checks are not needed here - they are taken care of by the request + // object, but reuse let's use the mapping method and leave this improvement until all APIs + // are pushing this check into the domain request. + var authorities = mapIDsToDomainNullSafe(environment.getArgument("authorities")); var lines = mapIDsToDomainNullSafe(environment.getArgument("lines")); var serviceJourneys = mapIDsToDomainNullSafe(environment.getArgument("serviceJourneys")); + var replacementFor = mapIDsToDomainNullSafe(environment.getArgument("replacementFor")); var privateCodes = environment.>getArgument("privateCodes"); var operatingDays = environment.>getArgument("operatingDays"); var alterations = environment.>getArgument("alterations"); - var authorities = mapIDsToDomainNullSafe(environment.getArgument("authorities")); - var replacementFor = mapIDsToDomainNullSafe(environment.getArgument("replacementFor")); - if (!lines.isEmpty()) { - stream = - stream.filter(tripOnServiceDate -> - lines.contains(tripOnServiceDate.getTrip().getRoute().getId()) - ); - } + TripOnServiceDateRequestBuilder tripOnServiceDateRequestBuilder = TripOnServiceDateRequest + .of() + .withOperatingDays(operatingDays) + .withAuthorities(authorities) + .withLines(lines) + .withServiceJourneys(serviceJourneys) + .withReplacementFor(replacementFor); - if (!serviceJourneys.isEmpty()) { - stream = - stream.filter(tripOnServiceDate -> - serviceJourneys.contains(tripOnServiceDate.getTrip().getId()) - ); - } + tripOnServiceDateRequestBuilder = + tripOnServiceDateRequestBuilder.withPrivateCodes(privateCodes); - if (privateCodes != null && !privateCodes.isEmpty()) { - stream = - stream.filter(tripOnServiceDate -> - privateCodes.contains(tripOnServiceDate.getTrip().getNetexInternalPlanningCode()) - ); - } + tripOnServiceDateRequestBuilder = + tripOnServiceDateRequestBuilder.withAlterations(alterations); - // At least one operationg day is required - var days = operatingDays.stream().toList(); - - stream = - stream.filter(tripOnServiceDate -> days.contains(tripOnServiceDate.getServiceDate())); - - if (alterations != null && !alterations.isEmpty()) { - stream = - stream.filter(tripOnServiceDate -> - alterations.contains(tripOnServiceDate.getTripAlteration()) - ); - } - - if (!authorities.isEmpty()) { - stream = - stream.filter(tripOnServiceDate -> - authorities.contains(tripOnServiceDate.getTrip().getRoute().getAgency().getId()) - ); - } - - if (!replacementFor.isEmpty()) { - stream = - stream.filter(tripOnServiceDate -> - !tripOnServiceDate.getReplacementFor().isEmpty() && - tripOnServiceDate - .getReplacementFor() - .stream() - .anyMatch(replacement -> replacementFor.contains(replacement.getId())) - ); - } - - return stream.toList(); + return GqlUtil + .getTransitService(environment) + .getTripOnServiceDates(tripOnServiceDateRequestBuilder.build()); }) .build(); } diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java index 21cdfee9ef7..cc30726b474 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/DebugStyleSpec.java @@ -18,6 +18,7 @@ import org.opentripplanner.street.model.edge.EscalatorEdge; import org.opentripplanner.street.model.edge.PathwayEdge; import org.opentripplanner.street.model.edge.StreetEdge; +import org.opentripplanner.street.model.edge.StreetStationCentroidLink; import org.opentripplanner.street.model.edge.StreetTransitEntranceLink; import org.opentripplanner.street.model.edge.StreetTransitStopLink; import org.opentripplanner.street.model.edge.StreetVehicleParkingLink; @@ -109,7 +110,8 @@ static StyleSpec build( StreetTransitEntranceLink.class, BoardingLocationToStopLink.class, StreetVehicleRentalLink.class, - StreetVehicleParkingLink.class + StreetVehicleParkingLink.class, + StreetStationCentroidLink.class ) .lineWidth(LINE_WIDTH) .minZoom(13) diff --git a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java index f97830232bd..26863157353 100644 --- a/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java +++ b/src/main/java/org/opentripplanner/apis/vectortiles/GraphInspectorVectorTileResource.java @@ -23,7 +23,6 @@ import java.util.Objects; import java.util.function.Predicate; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import org.glassfish.grizzly.http.server.Request; import org.opentripplanner.apis.support.TileJson; import org.opentripplanner.apis.vectortiles.model.LayerParams; @@ -164,7 +163,6 @@ private String tileJsonUrl(String base, List> layers) ); } - @Nonnull private List feedInfos() { return serverContext .transitService() diff --git a/src/main/java/org/opentripplanner/astar/AStar.java b/src/main/java/org/opentripplanner/astar/AStar.java index 1b4c69bfc16..9c14669a159 100644 --- a/src/main/java/org/opentripplanner/astar/AStar.java +++ b/src/main/java/org/opentripplanner/astar/AStar.java @@ -7,7 +7,6 @@ import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import org.opentripplanner.astar.model.BinHeap; import org.opentripplanner.astar.model.GraphPath; import org.opentripplanner.astar.model.ShortestPathTree; @@ -63,7 +62,7 @@ public class AStar< Set toVertices, SearchTerminationStrategy terminationStrategy, DominanceFunction dominanceFunction, - @Nonnull Duration timeout, + Duration timeout, Collection initialStates ) { this.heuristic = heuristic; diff --git a/src/main/java/org/opentripplanner/astar/AStarBuilder.java b/src/main/java/org/opentripplanner/astar/AStarBuilder.java index a4b0ac32389..349b7185b29 100644 --- a/src/main/java/org/opentripplanner/astar/AStarBuilder.java +++ b/src/main/java/org/opentripplanner/astar/AStarBuilder.java @@ -6,7 +6,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import javax.annotation.Nonnull; import org.opentripplanner.astar.model.GraphPath; import org.opentripplanner.astar.model.ShortestPathTree; import org.opentripplanner.astar.spi.AStarEdge; @@ -98,7 +97,6 @@ public Builder setDominanceFunction(DominanceFunction dominanceFunction) return builder; } - @Nonnull protected abstract Duration streetRoutingTimeout(); public Builder setOriginBackEdge(Edge originBackEdge) { diff --git a/src/main/java/org/opentripplanner/astar/spi/AStarEdge.java b/src/main/java/org/opentripplanner/astar/spi/AStarEdge.java index 91872021fc1..c4caafb19a9 100644 --- a/src/main/java/org/opentripplanner/astar/spi/AStarEdge.java +++ b/src/main/java/org/opentripplanner/astar/spi/AStarEdge.java @@ -1,7 +1,5 @@ package org.opentripplanner.astar.spi; -import javax.annotation.Nonnull; - /** * Represents an edge in the street network. Most edges have a one-to-one mapping to real world * things like street segments or stairs. @@ -43,6 +41,5 @@ public interface AStarEdge< * vehicle is speculatively dropped off and the passenger continues on foot in case * that the destination is inside the zone. */ - @Nonnull State[] traverse(State s0); } diff --git a/src/main/java/org/opentripplanner/datastore/OtpDataStore.java b/src/main/java/org/opentripplanner/datastore/OtpDataStore.java index 397d3f64c70..b85622de7f1 100644 --- a/src/main/java/org/opentripplanner/datastore/OtpDataStore.java +++ b/src/main/java/org/opentripplanner/datastore/OtpDataStore.java @@ -21,7 +21,6 @@ import java.util.Optional; import java.util.function.Function; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.datastore.api.CompositeDataSource; import org.opentripplanner.datastore.api.DataSource; @@ -135,25 +134,21 @@ public List getRepositoryDescriptions() { * @return The collection may contain elements of type {@link DataSource} or {@link * CompositeDataSource}. */ - @Nonnull public Collection listExistingSourcesFor(FileType type) { assertDataStoreIsOpened(); return sources.get(type).stream().filter(DataSource::exists).collect(Collectors.toList()); } - @Nonnull public DataSource getStreetGraph() { assertDataStoreIsOpened(); return streetGraph; } - @Nonnull public DataSource getGraph() { assertDataStoreIsOpened(); return graph; } - @Nonnull public CompositeDataSource getBuildReportDir() { assertDataStoreIsOpened(); return buildReportDir; @@ -188,11 +183,7 @@ private LocalDataSourceRepository getLocalDataSourceRepo( return localRepos.get(0); } - private DataSource findSingleSource( - @Nullable URI uri, - @Nonnull String filename, - @Nonnull FileType type - ) { + private DataSource findSingleSource(@Nullable URI uri, String filename, FileType type) { if (uri != null) { return findSourceUsingAllRepos(it -> it.findSource(uri, type)); } @@ -201,8 +192,8 @@ private DataSource findSingleSource( private CompositeDataSource findCompositeSource( @Nullable URI uri, - @Nonnull String filename, - @Nonnull FileType type + String filename, + FileType type ) { if (uri != null) { return findSourceUsingAllRepos(it -> it.findCompositeSource(uri, type)); @@ -211,10 +202,7 @@ private CompositeDataSource findCompositeSource( } } - private List findMultipleSources( - @Nonnull Collection uris, - @Nonnull FileType type - ) { + private List findMultipleSources(Collection uris, FileType type) { if (uris == null || uris.isEmpty()) { return localRepository.listExistingSources(type); } @@ -227,8 +215,8 @@ private List findMultipleSources( } private List findMultipleCompositeSources( - @Nonnull Collection uris, - @Nonnull FileType type + Collection uris, + FileType type ) { if (uris.isEmpty()) { return localRepository diff --git a/src/main/java/org/opentripplanner/datastore/api/OtpDataStoreConfig.java b/src/main/java/org/opentripplanner/datastore/api/OtpDataStoreConfig.java index 984d66de2e9..ac220d932d6 100644 --- a/src/main/java/org/opentripplanner/datastore/api/OtpDataStoreConfig.java +++ b/src/main/java/org/opentripplanner/datastore/api/OtpDataStoreConfig.java @@ -3,7 +3,6 @@ import java.net.URI; import java.util.List; import java.util.regex.Pattern; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.datastore.OtpDataStore; @@ -48,7 +47,6 @@ public interface OtpDataStoreConfig { *

* This parameter is optional. If {@code null} GTFS files are loaded from {@code baseDirectory}. */ - @Nonnull List gtfsFiles(); /** @@ -56,7 +54,6 @@ public interface OtpDataStoreConfig { *

* This parameter is optional. If {@code null} Netex files are loaded from {@code baseDirectory}. */ - @Nonnull List netexFiles(); /** diff --git a/src/main/java/org/opentripplanner/datastore/base/DataSourceRepository.java b/src/main/java/org/opentripplanner/datastore/base/DataSourceRepository.java index 92f2a5769b3..51f3e23347e 100644 --- a/src/main/java/org/opentripplanner/datastore/base/DataSourceRepository.java +++ b/src/main/java/org/opentripplanner/datastore/base/DataSourceRepository.java @@ -1,7 +1,6 @@ package org.opentripplanner.datastore.base; import java.net.URI; -import javax.annotation.Nonnull; import org.opentripplanner.datastore.api.CompositeDataSource; import org.opentripplanner.datastore.api.DataSource; import org.opentripplanner.datastore.api.FileType; @@ -43,7 +42,7 @@ public interface DataSourceRepository { * @param type the file type to load. * @return the datasource wrapper that can be used to access the data source. */ - DataSource findSource(@Nonnull URI uri, @Nonnull FileType type); + DataSource findSource(URI uri, FileType type); /** * Get the a composite data source (zip/directory) for the given uri and type. @@ -54,5 +53,5 @@ public interface DataSourceRepository { * @param type the file type to load. * @return the datasource wrapper that can be used to access the data source. */ - CompositeDataSource findCompositeSource(@Nonnull URI uri, @Nonnull FileType type); + CompositeDataSource findCompositeSource(URI uri, FileType type); } diff --git a/src/main/java/org/opentripplanner/datastore/base/LocalDataSourceRepository.java b/src/main/java/org/opentripplanner/datastore/base/LocalDataSourceRepository.java index a68b6753fa1..10776c31f6e 100644 --- a/src/main/java/org/opentripplanner/datastore/base/LocalDataSourceRepository.java +++ b/src/main/java/org/opentripplanner/datastore/base/LocalDataSourceRepository.java @@ -1,7 +1,6 @@ package org.opentripplanner.datastore.base; import java.util.List; -import javax.annotation.Nonnull; import org.opentripplanner.datastore.api.CompositeDataSource; import org.opentripplanner.datastore.api.DataSource; import org.opentripplanner.datastore.api.FileType; @@ -43,7 +42,7 @@ static boolean isCurrentDir(String filename) { * @param type the file type to load. * @return the datasource wrapper that can be used to access the data source. */ - CompositeDataSource findCompositeSource(String localFilename, @Nonnull FileType type); + CompositeDataSource findCompositeSource(String localFilename, FileType type); /** * List all existing data sources for the given type. diff --git a/src/main/java/org/opentripplanner/datastore/file/FileDataSourceRepository.java b/src/main/java/org/opentripplanner/datastore/file/FileDataSourceRepository.java index 75a157d83a0..ec5555be29e 100644 --- a/src/main/java/org/opentripplanner/datastore/file/FileDataSourceRepository.java +++ b/src/main/java/org/opentripplanner/datastore/file/FileDataSourceRepository.java @@ -16,7 +16,6 @@ import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; -import javax.annotation.Nonnull; import org.opentripplanner.datastore.api.CompositeDataSource; import org.opentripplanner.datastore.api.DataSource; import org.opentripplanner.datastore.api.FileType; @@ -56,7 +55,6 @@ public FileDataSourceRepository( /** * Use for unit testing */ - @Nonnull public static CompositeDataSource compositeSource(File file, FileType type) { // The cast is safe return createCompositeSource(file, type); @@ -73,12 +71,12 @@ public void open() { } @Override - public DataSource findSource(@Nonnull URI uri, @Nonnull FileType type) { + public DataSource findSource(URI uri, FileType type) { return new FileDataSource(openFile(uri, type), type); } @Override - public CompositeDataSource findCompositeSource(@Nonnull URI uri, @Nonnull FileType type) { + public CompositeDataSource findCompositeSource(URI uri, FileType type) { return createCompositeSource(openFile(uri, type), type); } diff --git a/src/main/java/org/opentripplanner/datastore/https/HttpsDataSourceRepository.java b/src/main/java/org/opentripplanner/datastore/https/HttpsDataSourceRepository.java index 56afc888c73..a0abd07cf38 100644 --- a/src/main/java/org/opentripplanner/datastore/https/HttpsDataSourceRepository.java +++ b/src/main/java/org/opentripplanner/datastore/https/HttpsDataSourceRepository.java @@ -4,7 +4,6 @@ import java.time.Duration; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; import org.apache.hc.core5.http.Header; import org.opentripplanner.datastore.api.CompositeDataSource; import org.opentripplanner.datastore.api.DataSource; @@ -33,7 +32,7 @@ public String description() { public void open() {} @Override - public DataSource findSource(@Nonnull URI uri, @Nonnull FileType type) { + public DataSource findSource(URI uri, FileType type) { if (skipUri(uri)) { return null; } @@ -41,7 +40,7 @@ public DataSource findSource(@Nonnull URI uri, @Nonnull FileType type) { } @Override - public CompositeDataSource findCompositeSource(@Nonnull URI uri, @Nonnull FileType type) { + public CompositeDataSource findCompositeSource(URI uri, FileType type) { if (skipUri(uri)) { return null; } diff --git a/src/main/java/org/opentripplanner/framework/application/OTPFeature.java b/src/main/java/org/opentripplanner/framework/application/OTPFeature.java index 29c2a452448..324f5397673 100644 --- a/src/main/java/org/opentripplanner/framework/application/OTPFeature.java +++ b/src/main/java/org/opentripplanner/framework/application/OTPFeature.java @@ -4,6 +4,7 @@ import java.util.Map; import java.util.function.Supplier; import java.util.stream.Collectors; +import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -199,6 +200,7 @@ public boolean isOff() { /** * If feature is turned on, then return supplied object if not return {@code null}. */ + @Nullable public T isOnElseNull(Supplier supplier) { return isOn() ? supplier.get() : null; } diff --git a/src/main/java/org/opentripplanner/framework/collection/CollectionUtils.java b/src/main/java/org/opentripplanner/framework/collection/CollectionUtils.java index 856542bc87b..27473dd1383 100644 --- a/src/main/java/org/opentripplanner/framework/collection/CollectionUtils.java +++ b/src/main/java/org/opentripplanner/framework/collection/CollectionUtils.java @@ -37,6 +37,10 @@ public static String toString(@Nullable Collection c, String nullText) { /** * A null-safe version of isEmpty() for a collection. *

+ * The main strategy for handling collections in OTP is to avoid nullable collection fields and + * use empty collections instead. So, before using this method check if the variable/field is + * indeed `@Nullable`. + *

* If the collection is {@code null} then {@code true} is returned. *

* If the collection is empty then {@code true} is returned. diff --git a/src/main/java/org/opentripplanner/framework/collection/ListUtils.java b/src/main/java/org/opentripplanner/framework/collection/ListUtils.java index 5964a1674e3..35b7e083695 100644 --- a/src/main/java/org/opentripplanner/framework/collection/ListUtils.java +++ b/src/main/java/org/opentripplanner/framework/collection/ListUtils.java @@ -69,4 +69,13 @@ public static List ofNullable(T input) { return List.of(input); } } + + /** + * This method converts the given collection to an instance of a List. If the input is + * {@code null} an empty collection is returned. If not the {@link List#copyOf(Collection)} is + * called. + */ + public static List nullSafeImmutableList(Collection c) { + return (c == null) ? List.of() : List.copyOf(c); + } } diff --git a/src/main/java/org/opentripplanner/framework/concurrent/OtpRequestThreadFactory.java b/src/main/java/org/opentripplanner/framework/concurrent/OtpRequestThreadFactory.java index e88bbb420cd..cb738363bcb 100644 --- a/src/main/java/org/opentripplanner/framework/concurrent/OtpRequestThreadFactory.java +++ b/src/main/java/org/opentripplanner/framework/concurrent/OtpRequestThreadFactory.java @@ -2,7 +2,6 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.util.concurrent.ThreadFactory; -import javax.annotation.Nonnull; import org.opentripplanner.framework.application.LogMDCSupport; /** @@ -34,7 +33,7 @@ public static ThreadFactory of(String nameFormat) { } @Override - public Thread newThread(@Nonnull Runnable r) { + public Thread newThread(Runnable r) { if (LogMDCSupport.isRequestTracingInLoggingEnabled()) { return delegate.newThread(new LogMDCRunnableDecorator(r)); } diff --git a/src/main/java/org/opentripplanner/framework/geometry/SphericalDistanceLibrary.java b/src/main/java/org/opentripplanner/framework/geometry/SphericalDistanceLibrary.java index 38c80e86d79..531170ad137 100644 --- a/src/main/java/org/opentripplanner/framework/geometry/SphericalDistanceLibrary.java +++ b/src/main/java/org/opentripplanner/framework/geometry/SphericalDistanceLibrary.java @@ -196,6 +196,20 @@ public static double metersToLonDegrees(double distanceMeters, double latDeg) { return dLatDeg / minCosLat; } + /** + * Approximately move a coordinate a given number of meters. This will fail if crossing the anti- + * meridian or any of the poles. + */ + public static WgsCoordinate moveMeters( + WgsCoordinate coordinate, + double latMeters, + double lonMeters + ) { + var degreesLat = metersToDegrees(latMeters); + var degreesLon = metersToLonDegrees(lonMeters, coordinate.latitude()); + return coordinate.add(degreesLat, degreesLon); + } + public static Envelope bounds(double lat, double lon, double latDistance, double lonDistance) { double radiusOfEarth = RADIUS_OF_EARTH_IN_M; diff --git a/src/main/java/org/opentripplanner/framework/geometry/WgsCoordinate.java b/src/main/java/org/opentripplanner/framework/geometry/WgsCoordinate.java index e818d50c1f6..6c3dbf0f9d3 100644 --- a/src/main/java/org/opentripplanner/framework/geometry/WgsCoordinate.java +++ b/src/main/java/org/opentripplanner/framework/geometry/WgsCoordinate.java @@ -163,6 +163,34 @@ public WgsCoordinate roundToApproximate100m() { return new WgsCoordinate(lat, lng); } + /** + * Return a new coordinate that is moved an approximate number of meters east. + */ + public WgsCoordinate moveEastMeters(double meters) { + return SphericalDistanceLibrary.moveMeters(this, 0, meters); + } + + /** + * Return a new coordinate that is moved an approximate number of meters west. + */ + public WgsCoordinate moveWestMeters(double meters) { + return SphericalDistanceLibrary.moveMeters(this, 0, -meters); + } + + /** + * Return a new coordinate that is moved an approximate number of meters north. + */ + public WgsCoordinate moveNorthMeters(double meters) { + return SphericalDistanceLibrary.moveMeters(this, meters, 0); + } + + /** + * Return a new coordinate that is moved an approximate number of meters south. + */ + public WgsCoordinate moveSouthMeters(double meters) { + return SphericalDistanceLibrary.moveMeters(this, -meters, 0); + } + /** * Return a string on the form: {@code "(60.12345, 11.12345)"}. Up to 5 digits are used after the * period(.), even if the coordinate is specified with a higher precision. diff --git a/src/main/java/org/opentripplanner/framework/graphql/GraphQLUtils.java b/src/main/java/org/opentripplanner/framework/graphql/GraphQLUtils.java index 334513fd419..ff5485d75b2 100644 --- a/src/main/java/org/opentripplanner/framework/graphql/GraphQLUtils.java +++ b/src/main/java/org/opentripplanner/framework/graphql/GraphQLUtils.java @@ -4,7 +4,6 @@ import java.util.Locale; import java.util.Map; import java.util.Optional; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; public class GraphQLUtils { @@ -65,7 +64,7 @@ private static Optional getDefaultLocale(DataFetchingEnvironment environ return Optional.ofNullable((Locale) localContext.get("locale")); } - private static boolean acceptAnyLocale(@Nonnull Locale locale) { + private static boolean acceptAnyLocale(Locale locale) { return locale.getLanguage().equals("*"); } } diff --git a/src/main/java/org/opentripplanner/framework/graphql/scalar/CostScalarFactory.java b/src/main/java/org/opentripplanner/framework/graphql/scalar/CostScalarFactory.java index 6f710328174..415118013f3 100644 --- a/src/main/java/org/opentripplanner/framework/graphql/scalar/CostScalarFactory.java +++ b/src/main/java/org/opentripplanner/framework/graphql/scalar/CostScalarFactory.java @@ -10,7 +10,6 @@ import graphql.schema.GraphQLScalarType; import java.util.Locale; import java.util.NoSuchElementException; -import javax.annotation.Nonnull; import org.opentripplanner.framework.model.Cost; import org.opentripplanner.framework.time.DurationUtils; @@ -54,7 +53,7 @@ private static Cost parseCost(String input) throws CoercingParseValueException { private static Coercing createCoercing() { return new Coercing<>() { @Override - public String serialize(@Nonnull Object result, GraphQLContext c, Locale l) { + public String serialize(Object result, GraphQLContext c, Locale l) { return serializeCost((Cost) result); } @@ -74,7 +73,6 @@ public Cost parseLiteral(Value input, CoercedVariables v, GraphQLContext c, L } @Override - @Nonnull public Value valueToLiteral(Object input, GraphQLContext c, Locale l) { return StringValue.of((String) input); } diff --git a/src/main/java/org/opentripplanner/framework/graphql/scalar/DurationScalarFactory.java b/src/main/java/org/opentripplanner/framework/graphql/scalar/DurationScalarFactory.java index 50af0fbc759..b685ecc597e 100644 --- a/src/main/java/org/opentripplanner/framework/graphql/scalar/DurationScalarFactory.java +++ b/src/main/java/org/opentripplanner/framework/graphql/scalar/DurationScalarFactory.java @@ -8,7 +8,6 @@ import graphql.schema.GraphQLScalarType; import java.time.Duration; import java.time.format.DateTimeParseException; -import javax.annotation.Nonnull; import org.opentripplanner.framework.time.DurationUtils; public class DurationScalarFactory { @@ -30,8 +29,7 @@ public static GraphQLScalarType createDurationScalar() { private static class DurationCoercing implements Coercing { @Override - @Nonnull - public String serialize(@Nonnull Object input) throws CoercingSerializeException { + public String serialize(Object input) throws CoercingSerializeException { if (input instanceof Duration duration) { return DurationUtils.formatDurationWithLeadingMinus(duration); } @@ -40,8 +38,7 @@ public String serialize(@Nonnull Object input) throws CoercingSerializeException } @Override - @Nonnull - public Duration parseValue(@Nonnull Object input) throws CoercingParseValueException { + public Duration parseValue(Object input) throws CoercingParseValueException { try { return DurationUtils.duration(input.toString()); } catch (DateTimeParseException dtpe) { @@ -50,8 +47,7 @@ public Duration parseValue(@Nonnull Object input) throws CoercingParseValueExcep } @Override - @Nonnull - public Duration parseLiteral(@Nonnull Object input) throws CoercingParseLiteralException { + public Duration parseLiteral(Object input) throws CoercingParseLiteralException { if (input instanceof StringValue) { return parseValue(((StringValue) input).getValue()); } diff --git a/src/main/java/org/opentripplanner/framework/i18n/I18NStringMapper.java b/src/main/java/org/opentripplanner/framework/i18n/I18NStringMapper.java index acdb30b301f..ca3de85c221 100644 --- a/src/main/java/org/opentripplanner/framework/i18n/I18NStringMapper.java +++ b/src/main/java/org/opentripplanner/framework/i18n/I18NStringMapper.java @@ -1,7 +1,6 @@ package org.opentripplanner.framework.i18n; import java.util.Locale; -import javax.annotation.Nonnull; import javax.annotation.Nullable; public class I18NStringMapper { @@ -17,7 +16,6 @@ public String mapToApi(I18NString string) { return string == null ? null : string.toString(locale); } - @Nonnull public String mapNonnullToApi(I18NString string) { return string.toString(locale); } diff --git a/src/main/java/org/opentripplanner/framework/i18n/NonLocalizedString.java b/src/main/java/org/opentripplanner/framework/i18n/NonLocalizedString.java index 3eddae235e4..dd7dfc8df05 100644 --- a/src/main/java/org/opentripplanner/framework/i18n/NonLocalizedString.java +++ b/src/main/java/org/opentripplanner/framework/i18n/NonLocalizedString.java @@ -4,7 +4,6 @@ import java.util.Locale; import java.util.Objects; import java.util.function.Function; -import javax.annotation.Nonnull; import javax.annotation.Nullable; /** @@ -18,7 +17,7 @@ public class NonLocalizedString implements I18NString, Serializable { private final String name; - public NonLocalizedString(@Nonnull String name) { + public NonLocalizedString(String name) { this.name = Objects.requireNonNull(name); } @@ -40,7 +39,7 @@ public static NonLocalizedString ofNullable(@Nullable String name) { */ public static NonLocalizedString ofNullable( @Nullable W wrapper, - @Nonnull Function getValueOp + Function getValueOp ) { return wrapper == null ? null : new NonLocalizedString(getValueOp.apply(wrapper)); } @@ -51,8 +50,8 @@ public static NonLocalizedString ofNullable( */ public static NonLocalizedString ofNullable( @Nullable W wrapper, - @Nonnull Function getValueOp, - @Nonnull String defaultValue + Function getValueOp, + String defaultValue ) { return new NonLocalizedString(wrapper == null ? defaultValue : getValueOp.apply(wrapper)); } @@ -61,11 +60,7 @@ public static NonLocalizedString ofNullable( * Check if name is non-null and returns an instance of {@link NonLocalizedString}, otherwise * returns a {@link NonLocalizedString} with the default name. */ - @Nonnull - public static NonLocalizedString ofNullableOrElse( - @Nullable String name, - @Nonnull String defaultName - ) { + public static NonLocalizedString ofNullableOrElse(@Nullable String name, String defaultName) { return new NonLocalizedString(name == null ? defaultName : name); } @@ -73,11 +68,7 @@ public static NonLocalizedString ofNullableOrElse( * Check if name is non-null and returns an instance of {@link NonLocalizedString}, otherwise * returns a {@link I18NString} with the default name. */ - @Nonnull - public static I18NString ofNullableOrElse( - @Nullable String name, - @Nonnull I18NString defaultName - ) { + public static I18NString ofNullableOrElse(@Nullable String name, I18NString defaultName) { return name == null ? defaultName : new NonLocalizedString(name); } diff --git a/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java b/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java index 3affa734605..81f8ee5b3c4 100644 --- a/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java +++ b/src/main/java/org/opentripplanner/framework/io/JsonDataListDownloader.java @@ -11,7 +11,6 @@ import java.util.Map; import java.util.Objects; import java.util.function.Function; -import javax.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,20 +24,20 @@ public class JsonDataListDownloader { private final OtpHttpClient otpHttpClient; public JsonDataListDownloader( - @Nonnull String url, - @Nonnull String jsonParsePath, - @Nonnull Function elementParser, - @Nonnull Map headers + String url, + String jsonParsePath, + Function elementParser, + Map headers ) { this(url, jsonParsePath, elementParser, headers, new OtpHttpClientFactory().create(LOG)); } public JsonDataListDownloader( - @Nonnull String url, - @Nonnull String jsonParsePath, - @Nonnull Function elementParser, - @Nonnull Map headers, - @Nonnull OtpHttpClient OtpHttpClient + String url, + String jsonParsePath, + Function elementParser, + Map headers, + OtpHttpClient OtpHttpClient ) { this.url = Objects.requireNonNull(url); this.jsonParsePath = Objects.requireNonNull(jsonParsePath); diff --git a/src/main/java/org/opentripplanner/framework/json/JsonUtils.java b/src/main/java/org/opentripplanner/framework/json/JsonUtils.java index 1f235eb6483..17b875b5596 100644 --- a/src/main/java/org/opentripplanner/framework/json/JsonUtils.java +++ b/src/main/java/org/opentripplanner/framework/json/JsonUtils.java @@ -2,11 +2,10 @@ import com.fasterxml.jackson.databind.JsonNode; import java.util.Optional; -import javax.annotation.Nonnull; public class JsonUtils { - public static Optional asText(@Nonnull JsonNode node, @Nonnull String field) { + public static Optional asText(JsonNode node, String field) { JsonNode valueNode = node.get(field); if (valueNode == null) { return Optional.empty(); diff --git a/src/main/java/org/opentripplanner/framework/lang/MemEfficientArrayBuilder.java b/src/main/java/org/opentripplanner/framework/lang/MemEfficientArrayBuilder.java index 8b3a6ba6cd0..7bf36dd5b86 100644 --- a/src/main/java/org/opentripplanner/framework/lang/MemEfficientArrayBuilder.java +++ b/src/main/java/org/opentripplanner/framework/lang/MemEfficientArrayBuilder.java @@ -2,7 +2,6 @@ import java.util.Arrays; import java.util.Objects; -import javax.annotation.Nonnull; /** * This array builder is used to minimize the creation of new objects (arrays). It takes an array as base, @@ -20,7 +19,7 @@ public final class MemEfficientArrayBuilder { private final T[] original; private T[] array = null; - private MemEfficientArrayBuilder(@Nonnull T[] original) { + private MemEfficientArrayBuilder(T[] original) { this.original = Objects.requireNonNull(original); } diff --git a/src/main/java/org/opentripplanner/framework/lang/StringUtils.java b/src/main/java/org/opentripplanner/framework/lang/StringUtils.java index 9eff448d0c7..5d18981e0e3 100644 --- a/src/main/java/org/opentripplanner/framework/lang/StringUtils.java +++ b/src/main/java/org/opentripplanner/framework/lang/StringUtils.java @@ -1,7 +1,6 @@ package org.opentripplanner.framework.lang; import java.util.regex.Pattern; -import javax.annotation.Nonnull; /** * OTP String utils extending the Java lang String... @@ -120,7 +119,7 @@ public static String padRight(String value, char ch, int width) { } /** Replace single quotes with double quotes. */ - public static String quoteReplace(@Nonnull String text) { + public static String quoteReplace(String text) { return text.replace('\'', '\"'); } diff --git a/src/main/java/org/opentripplanner/framework/time/DateUtils.java b/src/main/java/org/opentripplanner/framework/time/DateUtils.java index d212af56b79..36f62fb0448 100644 --- a/src/main/java/org/opentripplanner/framework/time/DateUtils.java +++ b/src/main/java/org/opentripplanner/framework/time/DateUtils.java @@ -10,7 +10,6 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.List; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -86,7 +85,7 @@ public static ZonedDateTime toZonedDateTime(String date, String time, ZoneId tz) } // TODO: could be replaced with Apache's DateFormat.parseDate ??? - public static LocalDate parseDate(@Nonnull String input) { + public static LocalDate parseDate(String input) { LocalDate retVal = null; try { String newString = input diff --git a/src/main/java/org/opentripplanner/framework/time/TimeUtils.java b/src/main/java/org/opentripplanner/framework/time/TimeUtils.java index 9cc5594cb76..f2d2cf5de41 100644 --- a/src/main/java/org/opentripplanner/framework/time/TimeUtils.java +++ b/src/main/java/org/opentripplanner/framework/time/TimeUtils.java @@ -14,7 +14,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.Nonnull; /** * Time utility methods. See the unit test for examples on how to use this class. @@ -108,7 +107,7 @@ public static int time(String hhmmss, int defaultValue) { * very helpful when specifying a schedule using a sting instead of using a int array with seconds * past midnight. */ - public static int[] times(@Nonnull String input) { + public static int[] times(String input) { return Arrays.stream(input.split("[ ,;]+")).mapToInt(TimeUtils::time).toArray(); } diff --git a/src/main/java/org/opentripplanner/framework/tostring/ToStringBuilder.java b/src/main/java/org/opentripplanner/framework/tostring/ToStringBuilder.java index 6480b6f1187..f600a2a31bc 100644 --- a/src/main/java/org/opentripplanner/framework/tostring/ToStringBuilder.java +++ b/src/main/java/org/opentripplanner/framework/tostring/ToStringBuilder.java @@ -15,7 +15,6 @@ 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; @@ -372,7 +371,7 @@ private ToStringBuilder addIfNotIgnored( return addIt(name, mapToString.apply(value)); } - private ToStringBuilder addIt(String name, @Nonnull String value) { + private ToStringBuilder addIt(String name, String value) { addLabel(name); addValue(value); return this; @@ -387,7 +386,7 @@ private void addLabel(String name) { sb.append(name); } - private void addValue(@Nonnull String value) { + private void addValue(String value) { sb.append(FIELD_VALUE_SEP); sb.append(value); } diff --git a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java index 3c1408089a4..d6f91696e64 100644 --- a/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java +++ b/src/main/java/org/opentripplanner/graph_builder/GraphBuilder.java @@ -8,7 +8,6 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.ext.emissions.EmissionsDataModel; import org.opentripplanner.ext.stopconsolidation.StopConsolidationRepository; @@ -44,11 +43,7 @@ public class GraphBuilder implements Runnable { private boolean hasTransitData = false; @Inject - public GraphBuilder( - @Nonnull Graph baseGraph, - @Nonnull TransitModel transitModel, - @Nonnull DataImportIssueStore issueStore - ) { + public GraphBuilder(Graph baseGraph, TransitModel transitModel, DataImportIssueStore issueStore) { this.graph = baseGraph; this.transitModel = transitModel; this.issueStore = issueStore; @@ -167,6 +162,8 @@ public static GraphBuilder create( graphBuilder.addModule(factory.emissionsModule()); } + graphBuilder.addModuleOptional(factory.routeToCentroidStationIdValidator()); + if (config.dataImportReport) { graphBuilder.addModule(factory.dataImportIssueReporter()); } diff --git a/src/main/java/org/opentripplanner/graph_builder/issue/api/DataImportIssueSummary.java b/src/main/java/org/opentripplanner/graph_builder/issue/api/DataImportIssueSummary.java index ba1e6610a30..da3110d0ce7 100644 --- a/src/main/java/org/opentripplanner/graph_builder/issue/api/DataImportIssueSummary.java +++ b/src/main/java/org/opentripplanner/graph_builder/issue/api/DataImportIssueSummary.java @@ -8,7 +8,6 @@ import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,7 +73,6 @@ public void logSummary() { .forEach(issueType -> ISSUE_LOG.info(String.format(FMT, issueType, summary.get(issueType)))); } - @Nonnull public Map asMap() { return summary; } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/AddTransitModelEntitiesToGraph.java b/src/main/java/org/opentripplanner/graph_builder/module/AddTransitModelEntitiesToGraph.java index a53730b104c..a4bbb10da68 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/AddTransitModelEntitiesToGraph.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/AddTransitModelEntitiesToGraph.java @@ -39,6 +39,7 @@ import org.opentripplanner.transit.model.site.PathwayMode; import org.opentripplanner.transit.model.site.PathwayNode; import org.opentripplanner.transit.model.site.RegularStop; +import org.opentripplanner.transit.model.site.Station; import org.opentripplanner.transit.model.site.StationElement; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.service.TransitModel; @@ -86,6 +87,7 @@ private void applyToGraph(TransitModel transitModel) { addStopsToGraphAndGenerateStopVertexes(transitModel); addEntrancesToGraph(); + addStationCentroidsToGraph(); addPathwayNodesToGraph(); addBoardingAreasToGraph(); @@ -142,6 +144,14 @@ private void addEntrancesToGraph() { } } + private void addStationCentroidsToGraph() { + for (Station station : otpTransitService.stopModel().listStations()) { + if (station.shouldRouteToCentroid()) { + vertexFactory.stationCentroid(station); + } + } + } + private void addPathwayNodesToGraph() { for (PathwayNode node : otpTransitService.getAllPathwayNodes()) { TransitPathwayNodeVertex nodeVertex = vertexFactory.transitPathwayNode(node); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index 3137b66070f..a87f28182ff 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -104,8 +104,7 @@ public void buildGraph() { LOG.debug("Linking stop '{}' {}", stop, ts0); for (RouteRequest transferProfile : transferRequests) { - for (NearbyStop sd : findNearbyStops( - nearbyStopFinder, + for (NearbyStop sd : nearbyStopFinder.findNearbyStops( ts0, transferProfile, transferProfile.journey().transfer(), @@ -126,8 +125,7 @@ public void buildGraph() { if (OTPFeature.FlexRouting.isOn()) { // This code is for finding transfers from AreaStops to Stops, transfers // from Stops to AreaStops and between Stops are already covered above. - for (NearbyStop sd : findNearbyStops( - nearbyStopFinder, + for (NearbyStop sd : nearbyStopFinder.findNearbyStops( ts0, transferProfile, transferProfile.journey().transfer(), @@ -203,15 +201,5 @@ private NearbyStopFinder createNearbyStopFinder() { } } - private static Iterable findNearbyStops( - NearbyStopFinder nearbyStopFinder, - Vertex vertex, - RouteRequest request, - StreetRequest streetRequest, - boolean reverseDirection - ) { - return nearbyStopFinder.findNearbyStops(vertex, request, streetRequest, reverseDirection); - } - private record TransferKey(StopLocation source, StopLocation target, List edges) {} } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/RouteToCentroidStationIdsValidator.java b/src/main/java/org/opentripplanner/graph_builder/module/RouteToCentroidStationIdsValidator.java new file mode 100644 index 00000000000..0b17f956ae8 --- /dev/null +++ b/src/main/java/org/opentripplanner/graph_builder/module/RouteToCentroidStationIdsValidator.java @@ -0,0 +1,50 @@ +package org.opentripplanner.graph_builder.module; + +import java.util.Collection; +import java.util.stream.Collectors; +import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; +import org.opentripplanner.graph_builder.model.GraphBuilderModule; +import org.opentripplanner.transit.model.framework.AbstractTransitEntity; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.service.TransitModel; + +public class RouteToCentroidStationIdsValidator implements GraphBuilderModule { + + private final DataImportIssueStore issueStore; + private final Collection transitRouteToStationCentroid; + private final TransitModel transitModel; + + public RouteToCentroidStationIdsValidator( + DataImportIssueStore issueStore, + Collection transitRouteToStationCentroid, + TransitModel transitModel + ) { + this.issueStore = issueStore; + this.transitRouteToStationCentroid = transitRouteToStationCentroid; + this.transitModel = transitModel; + } + + private void validate() { + var stationIds = transitModel + .getStopModel() + .listStations() + .stream() + .map(AbstractTransitEntity::getId) + .collect(Collectors.toSet()); + transitRouteToStationCentroid + .stream() + .filter(id -> !stationIds.contains(id)) + .forEach(id -> + issueStore.add( + "UnknownStationId", + "Config parameter 'transitRouteToStationCentroid' specified a station that does not exist: %s", + id + ) + ); + } + + @Override + public void buildGraph() { + validate(); + } +} 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 48e6e484a0c..a134dd3c75b 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/StreetLinkerModule.java @@ -3,8 +3,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.function.BiFunction; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.framework.logging.ProgressTracker; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; @@ -15,14 +15,17 @@ import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingHelper; import org.opentripplanner.street.model.edge.Edge; +import org.opentripplanner.street.model.edge.StreetStationCentroidLink; import org.opentripplanner.street.model.edge.StreetTransitEntranceLink; import org.opentripplanner.street.model.edge.StreetTransitStopLink; import org.opentripplanner.street.model.edge.StreetVehicleParkingLink; import org.opentripplanner.street.model.edge.VehicleParkingEdge; +import org.opentripplanner.street.model.vertex.StationCentroidVertex; import org.opentripplanner.street.model.vertex.StreetVertex; import org.opentripplanner.street.model.vertex.TransitEntranceVertex; import org.opentripplanner.street.model.vertex.TransitStopVertex; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; +import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.TraverseModeSet; import org.opentripplanner.transit.model.site.GroupStop; @@ -70,6 +73,7 @@ public void buildGraph() { if (graph.hasStreets) { linkTransitStops(graph, transitModel); linkTransitEntrances(graph); + linkStationCentroids(graph); linkVehicleParks(graph, issueStore); } @@ -175,7 +179,6 @@ private void linkToDriveableEdge(TransitStopVertex tStop) { ); } - @Nonnull private static List createStopLinkEdges( TransitStopVertex vertex, StreetVertex streetVertex @@ -257,6 +260,34 @@ private void linkTransitEntrances(Graph graph) { } } + private void linkStationCentroids(Graph graph) { + BiFunction> stationAndStreetVertexLinker = ( + theStation, + streetVertex + ) -> + List.of( + StreetStationCentroidLink.createStreetStationLink( + (StationCentroidVertex) theStation, + streetVertex + ), + StreetStationCentroidLink.createStreetStationLink( + streetVertex, + (StationCentroidVertex) theStation + ) + ); + + for (StationCentroidVertex station : graph.getVerticesOfType(StationCentroidVertex.class)) { + graph + .getLinker() + .linkVertexPermanently( + station, + new TraverseModeSet(TraverseMode.WALK), + LinkingDirection.BOTH_WAYS, + stationAndStreetVertexLinker + ); + } + } + private void linkVehicleParks(Graph graph, DataImportIssueStore issueStore) { LOG.info("Linking vehicle parks to graph..."); List vehicleParkingToRemove = new ArrayList<>(); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/TimeZoneAdjusterModule.java b/src/main/java/org/opentripplanner/graph_builder/module/TimeZoneAdjusterModule.java index eb3674bf76a..7168066afe3 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/TimeZoneAdjusterModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/TimeZoneAdjusterModule.java @@ -7,6 +7,7 @@ import java.util.HashMap; import java.util.Map; import org.opentripplanner.graph_builder.model.GraphBuilderModule; +import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.service.TransitModel; /** @@ -45,9 +46,15 @@ public void buildGraph() { return; } - pattern - .getScheduledTimetable() - .updateAllTripTimes(it -> it.adjustTimesToGraphTimeZone(timeShift)); + TripPattern updatedPattern = pattern + .copy() + .withScheduledTimeTableBuilder(builder -> + builder.updateAllTripTimes(tt -> tt.adjustTimesToGraphTimeZone(timeShift)) + ) + .build(); + // replace the original pattern with the updated pattern in the transit model + transitModel.addTripPattern(updatedPattern.getId(), updatedPattern); }); + transitModel.index(); } } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderFactory.java b/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderFactory.java index 9c778481d4d..21b125b1188 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderFactory.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderFactory.java @@ -19,6 +19,7 @@ import org.opentripplanner.graph_builder.module.DirectTransferGenerator; import org.opentripplanner.graph_builder.module.GraphCoherencyCheckerModule; import org.opentripplanner.graph_builder.module.OsmBoardingLocationsModule; +import org.opentripplanner.graph_builder.module.RouteToCentroidStationIdsValidator; import org.opentripplanner.graph_builder.module.StreetLinkerModule; import org.opentripplanner.graph_builder.module.TimeZoneAdjusterModule; import org.opentripplanner.graph_builder.module.TripPatternNamer; @@ -58,6 +59,9 @@ public interface GraphBuilderFactory { CalculateWorldEnvelopeModule calculateWorldEnvelopeModule(); StreetLimitationParameters streetLimitationParameters(); + @Nullable + RouteToCentroidStationIdsValidator routeToCentroidStationIdValidator(); + @Nullable StopConsolidationModule stopConsolidationModule(); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java b/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java index 10d3a997579..2b8de9e1984 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java @@ -24,6 +24,7 @@ import org.opentripplanner.graph_builder.issue.report.DataImportIssueReporter; import org.opentripplanner.graph_builder.issue.service.DefaultDataImportIssueStore; import org.opentripplanner.graph_builder.module.DirectTransferGenerator; +import org.opentripplanner.graph_builder.module.RouteToCentroidStationIdsValidator; import org.opentripplanner.graph_builder.module.StreetLinkerModule; import org.opentripplanner.graph_builder.module.islandpruning.PruneIslands; import org.opentripplanner.graph_builder.module.ned.DegreeGridNEDTileSource; @@ -302,6 +303,20 @@ static StopConsolidationModule providesStopConsolidationModule( .orElse(null); } + @Provides + @Singleton + @Nullable + static RouteToCentroidStationIdsValidator routeToCentroidStationIdValidator( + DataImportIssueStore issueStore, + BuildConfig config, + TransitModel transitModel + ) { + var ids = config.transitRouteToStationCentroid(); + return ids.isEmpty() + ? null + : new RouteToCentroidStationIdsValidator(issueStore, ids, transitModel); + } + /* private methods */ private static ElevationGridCoverageFactory createNedElevationFactory( diff --git a/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinder.java b/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinder.java index baf528a739f..e54c27249e1 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinder.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinder.java @@ -2,6 +2,7 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; import java.time.Duration; import java.util.ArrayList; import java.util.Collection; @@ -40,18 +41,39 @@ public class StreetNearbyStopFinder implements NearbyStopFinder { private final Duration durationLimit; private final int maxStopCount; private final DataOverlayContext dataOverlayContext; + private final Set ignoreVertices; /** * Construct a NearbyStopFinder for the given graph and search radius. + * + * @param maxStopCount The maximum stops to return. 0 means no limit. Regardless of the maxStopCount + * we will always return all the directly connected stops. */ public StreetNearbyStopFinder( Duration durationLimit, int maxStopCount, DataOverlayContext dataOverlayContext + ) { + this(durationLimit, maxStopCount, dataOverlayContext, Set.of()); + } + + /** + * Construct a NearbyStopFinder for the given graph and search radius. + * + * @param maxStopCount The maximum stops to return. 0 means no limit. Regardless of the maxStopCount + * we will always return all the directly connected stops. + * @param ignoreVertices A set of stop vertices to ignore and not return NearbyStops for. + */ + public StreetNearbyStopFinder( + Duration durationLimit, + int maxStopCount, + DataOverlayContext dataOverlayContext, + Set ignoreVertices ) { this.dataOverlayContext = dataOverlayContext; this.durationLimit = durationLimit; this.maxStopCount = maxStopCount; + this.ignoreVertices = ignoreVertices; } /** @@ -66,36 +88,40 @@ public Collection findNearbyStops( StreetRequest streetRequest, boolean reverseDirection ) { - return findNearbyStops(Set.of(vertex), reverseDirection, routingRequest, streetRequest); + return findNearbyStops(Set.of(vertex), routingRequest, streetRequest, reverseDirection); } /** * Return all stops within a certain radius of the given vertex, using network distance along * streets. If the origin vertex is a StopVertex, the result will include it. * - * @param originVertices the origin point of the street search + * @param originVertices the origin point of the street search. * @param reverseDirection if true the paths returned instead originate at the nearby stops and - * have the originVertex as the destination + * have the originVertex as the destination. */ public Collection findNearbyStops( Set originVertices, - boolean reverseDirection, RouteRequest request, - StreetRequest streetRequest + StreetRequest streetRequest, + boolean reverseDirection ) { OTPRequestTimeoutException.checkForTimeout(); - List stopsFound = createDirectlyConnectedStops( - originVertices, + List stopsFound = NearbyStop.nearbyStopsForTransitStopVerticesFiltered( + Sets.difference(originVertices, ignoreVertices), reverseDirection, request, streetRequest ); // Return only the origin vertices if there are no valid street modes - if (streetRequest.mode() == StreetMode.NOT_SET) { + if ( + streetRequest.mode() == StreetMode.NOT_SET || + (maxStopCount != 0 && stopsFound.size() >= maxStopCount) + ) { return stopsFound; } + stopsFound = new ArrayList<>(stopsFound); ShortestPathTree spt = StreetSearchBuilder .of() @@ -116,7 +142,9 @@ public Collection findNearbyStops( // TODO use GenericAStar and a traverseVisitor? Add an earliestArrival switch to genericAStar? for (State state : spt.getAllStates()) { Vertex targetVertex = state.getVertex(); - if (originVertices.contains(targetVertex)) continue; + if (originVertices.contains(targetVertex) || ignoreVertices.contains(targetVertex)) { + continue; + } if (targetVertex instanceof TransitStopVertex tsv && state.isFinal()) { stopsFound.add(NearbyStop.nearbyStopForState(state, tsv.getStop())); } @@ -162,46 +190,12 @@ private SkipEdgeStrategy getSkipEdgeStrategy() { var durationSkipEdgeStrategy = new DurationSkipEdgeStrategy(durationLimit); if (maxStopCount > 0) { - var strategy = new MaxCountSkipEdgeStrategy<>( - maxStopCount, - StreetNearbyStopFinder::hasReachedStop - ); + var strategy = new MaxCountSkipEdgeStrategy<>(maxStopCount, this::hasReachedStop); return new ComposingSkipEdgeStrategy<>(strategy, durationSkipEdgeStrategy); } return durationSkipEdgeStrategy; } - private static List createDirectlyConnectedStops( - Set originVertices, - boolean reverseDirection, - RouteRequest request, - StreetRequest streetRequest - ) { - List stopsFound = new ArrayList<>(); - - StreetSearchRequest streetSearchRequest = StreetSearchRequestMapper - .mapToTransferRequest(request) - .withArriveBy(reverseDirection) - .withMode(streetRequest.mode()) - .build(); - - /* Add the origin vertices if they are stops */ - for (Vertex vertex : originVertices) { - if (vertex instanceof TransitStopVertex tsv) { - stopsFound.add( - new NearbyStop( - tsv.getStop(), - 0, - Collections.emptyList(), - new State(vertex, streetSearchRequest) - ) - ); - } - } - - return stopsFound; - } - private boolean canBoardFlex(State state, boolean reverse) { Collection edges = reverse ? state.getVertex().getIncoming() @@ -224,7 +218,10 @@ private boolean canBoardFlex(State state, boolean reverse) { * states that speculatively rent a vehicle move the walk states down the A* priority queue until * the required number of stops are reached to abort the search, leading to zero egress results. */ - public static boolean hasReachedStop(State state) { - return state.getVertex() instanceof TransitStopVertex && state.isFinal(); + public boolean hasReachedStop(State state) { + var vertex = state.getVertex(); + return ( + vertex instanceof TransitStopVertex && state.isFinal() && !ignoreVertices.contains(vertex) + ); } } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/ElevatorProcessor.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/ElevatorProcessor.java index 3a0a9fa404b..57e1abdd2d3 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/ElevatorProcessor.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/ElevatorProcessor.java @@ -7,7 +7,6 @@ import java.util.List; import java.util.Map; import java.util.OptionalInt; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.graph_builder.issue.api.Issue; @@ -46,9 +45,9 @@ class ElevatorProcessor { private final VertexGenerator vertexGenerator; public ElevatorProcessor( - @Nonnull DataImportIssueStore issueStore, - @Nonnull OsmDatabase osmdb, - @Nonnull VertexGenerator vertexGenerator + DataImportIssueStore issueStore, + OsmDatabase osmdb, + VertexGenerator vertexGenerator ) { this.issueStore = issueStore; this.osmdb = osmdb; diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java index 10c215ee448..df46836942f 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmModule.java @@ -8,7 +8,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; @@ -63,7 +62,7 @@ public class OsmModule implements GraphBuilderModule { Collection providers, Graph graph, DataImportIssueStore issueStore, - @Nonnull StreetLimitationParameters streetLimitationParameters, + StreetLimitationParameters streetLimitationParameters, OsmProcessingParameters params ) { this.providers = List.copyOf(providers); diff --git a/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java b/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java index 60cf780f714..819e9e77d02 100644 --- a/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java +++ b/src/main/java/org/opentripplanner/graph_builder/services/osm/EdgeNamer.java @@ -1,6 +1,5 @@ package org.opentripplanner.graph_builder.services.osm; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.graph_builder.module.osm.StreetEdgePair; @@ -33,7 +32,7 @@ public interface EdgeNamer { */ void postprocess(); - default I18NString getNameForWay(OSMWithTags way, @Nonnull String id) { + default I18NString getNameForWay(OSMWithTags way, String id) { var name = name(way); if (name == null) { diff --git a/src/main/java/org/opentripplanner/gtfs/GenerateTripPatternsOperation.java b/src/main/java/org/opentripplanner/gtfs/GenerateTripPatternsOperation.java index bf26f9c6d7b..08ad300fa9a 100644 --- a/src/main/java/org/opentripplanner/gtfs/GenerateTripPatternsOperation.java +++ b/src/main/java/org/opentripplanner/gtfs/GenerateTripPatternsOperation.java @@ -3,6 +3,7 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -23,6 +24,7 @@ import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.network.StopPattern; import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.network.TripPatternBuilder; import org.opentripplanner.transit.model.timetable.Direction; import org.opentripplanner.transit.model.timetable.FrequencyEntry; import org.opentripplanner.transit.model.timetable.Trip; @@ -40,13 +42,18 @@ public class GenerateTripPatternsOperation { private final Map tripPatternIdCounters = new HashMap<>(); - private final OtpTransitServiceBuilder transitDaoBuilder; + private final OtpTransitServiceBuilder transitServiceBuilder; private final DataImportIssueStore issueStore; private final Deduplicator deduplicator; private final Set calendarServiceIds; private final GeometryProcessor geometryProcessor; - private final Multimap tripPatterns; + // TODO the linked hashset configuration ensures that TripPatterns are created in the same order + // as Trips are imported, as a workaround for issue #6067 + private final Multimap tripPatternBuilders = MultimapBuilder + .linkedHashKeys() + .linkedHashSetValues() + .build(); private final ListMultimap frequenciesForTrip = ArrayListMultimap.create(); private int freqCount = 0; @@ -59,18 +66,17 @@ public GenerateTripPatternsOperation( Set calendarServiceIds, GeometryProcessor geometryProcessor ) { - this.transitDaoBuilder = builder; + this.transitServiceBuilder = builder; this.issueStore = issueStore; this.deduplicator = deduplicator; this.calendarServiceIds = calendarServiceIds; this.geometryProcessor = geometryProcessor; - this.tripPatterns = transitDaoBuilder.getTripPatterns(); } public void run() { collectFrequencyByTrip(); - final Collection trips = transitDaoBuilder.getTripsById().values(); + final Collection trips = transitServiceBuilder.getTripsById().values(); var progressLogger = ProgressTracker.track("build trip patterns", 50_000, trips.size()); LOG.info(progressLogger.startMessage()); @@ -85,6 +91,14 @@ public void run() { } } + tripPatternBuilders + .values() + .stream() + .map(TripPatternBuilder::build) + .forEach(tripPattern -> + transitServiceBuilder.getTripPatterns().put(tripPattern.getStopPattern(), tripPattern) + ); + LOG.info(progressLogger.completeMessage()); LOG.info( "Added {} frequency-based and {} single-trip timetable entries.", @@ -107,7 +121,7 @@ public boolean hasScheduledTrips() { * the same trip can be added at once to the same Timetable/TripPattern. */ private void collectFrequencyByTrip() { - for (Frequency freq : transitDaoBuilder.getFrequencies()) { + for (Frequency freq : transitServiceBuilder.getFrequencies()) { frequenciesForTrip.put(freq.getTrip(), freq); } } @@ -119,7 +133,7 @@ private void buildTripPatternForTrip(Trip trip) { return; // Invalid trip, skip it, it will break later } - List stopTimes = transitDaoBuilder.getStopTimesSortedByTrip().get(trip); + List stopTimes = transitServiceBuilder.getStopTimesSortedByTrip().get(trip); // If after filtering this trip does not contain at least 2 stoptimes, it does not serve any purpose. var staticTripWithFewerThan2Stops = @@ -134,8 +148,7 @@ private void buildTripPatternForTrip(Trip trip) { // Get the existing TripPattern for this filtered StopPattern, or create one. StopPattern stopPattern = new StopPattern(stopTimes); - Direction direction = trip.getDirection(); - TripPattern tripPattern = findOrCreateTripPattern(stopPattern, trip, direction); + TripPatternBuilder tripPatternBuilder = findOrCreateTripPattern(stopPattern, trip); // Create a TripTimes object for this list of stoptimes, which form one trip. TripTimes tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, deduplicator); @@ -144,44 +157,42 @@ private void buildTripPatternForTrip(Trip trip) { List frequencies = frequenciesForTrip.get(trip); if (!frequencies.isEmpty()) { for (Frequency freq : frequencies) { - tripPattern.add(new FrequencyEntry(freq, tripTimes)); + tripPatternBuilder.withScheduledTimeTableBuilder(builder -> + builder.addFrequencyEntry(new FrequencyEntry(freq, tripTimes)) + ); freqCount++; } } // This trip was not frequency-based. Add the TripTimes directly to the TripPattern's scheduled timetable. else { - tripPattern.add(tripTimes); + tripPatternBuilder.withScheduledTimeTableBuilder(builder -> builder.addTripTimes(tripTimes)); scheduledCount++; } } - private TripPattern findOrCreateTripPattern( - StopPattern stopPattern, - Trip trip, - Direction direction - ) { + private TripPatternBuilder findOrCreateTripPattern(StopPattern stopPattern, Trip trip) { Route route = trip.getRoute(); - for (TripPattern tripPattern : tripPatterns.get(stopPattern)) { + Direction direction = trip.getDirection(); + for (TripPatternBuilder tripPatternBuilder : tripPatternBuilders.get(stopPattern)) { if ( - tripPattern.getRoute().equals(route) && - tripPattern.getDirection().equals(direction) && - tripPattern.getMode().equals(trip.getMode()) && - tripPattern.getNetexSubmode().equals(trip.getNetexSubMode()) + tripPatternBuilder.getRoute().equals(route) && + tripPatternBuilder.getDirection().equals(direction) && + tripPatternBuilder.getMode().equals(trip.getMode()) && + tripPatternBuilder.getNetexSubmode().equals(trip.getNetexSubMode()) ) { - return tripPattern; + return tripPatternBuilder; } } FeedScopedId patternId = generateUniqueIdForTripPattern(route, direction); - TripPattern tripPattern = TripPattern + TripPatternBuilder tripPatternBuilder = TripPattern .of(patternId) .withRoute(route) .withStopPattern(stopPattern) .withMode(trip.getMode()) .withNetexSubmode(trip.getNetexSubMode()) - .withHopGeometries(geometryProcessor.createHopGeometries(trip)) - .build(); - tripPatterns.put(stopPattern, tripPattern); - return tripPattern; + .withHopGeometries(geometryProcessor.createHopGeometries(trip)); + tripPatternBuilders.put(stopPattern, tripPatternBuilder); + return tripPatternBuilder; } /** diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java index d58410741f9..b7aea1c2e2f 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/GTFSToOtpTransitServiceMapper.java @@ -72,7 +72,6 @@ public class GTFSToOtpTransitServiceMapper { private final FareTransferRuleMapper fareTransferRuleMapper; - private final StopAreaMapper stopAreaMapper; private final DirectionMapper directionMapper; private final DataImportIssueStore issueStore; @@ -112,9 +111,6 @@ public GTFSToOtpTransitServiceMapper( boardingAreaMapper = new BoardingAreaMapper(translationHelper, stopLookup); locationMapper = new LocationMapper(builder.stopModel(), issueStore); locationGroupMapper = new LocationGroupMapper(stopMapper, locationMapper, builder.stopModel()); - // the use of stop areas were reverted in the spec - // this code will go away, please migrate now! - stopAreaMapper = new StopAreaMapper(stopMapper, locationMapper, builder.stopModel()); pathwayMapper = new PathwayMapper(stopMapper, entranceMapper, pathwayNodeMapper, boardingAreaMapper); routeMapper = new RouteMapper(agencyMapper, issueStore, translationHelper); @@ -126,7 +122,6 @@ public GTFSToOtpTransitServiceMapper( stopMapper, locationMapper, locationGroupMapper, - stopAreaMapper, tripMapper, bookingRuleMapper, translationHelper @@ -166,7 +161,6 @@ public void mapStopTripAndRouteDataIntoBuilder() { // Stop areas and Stop groups are only used in FLEX routes builder.stopModel().withAreaStops(locationMapper.map(data.getAllLocations())); builder.stopModel().withGroupStops(locationGroupMapper.map(data.getAllLocationGroups())); - builder.stopModel().withGroupStops(stopAreaMapper.map(data.getAllStopAreas())); } builder.getPathways().addAll(pathwayMapper.map(data.getAllPathways())); diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/StopAreaMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/StopAreaMapper.java deleted file mode 100644 index 55c836aa458..00000000000 --- a/src/main/java/org/opentripplanner/gtfs/mapping/StopAreaMapper.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.opentripplanner.gtfs.mapping; - -import static org.opentripplanner.gtfs.mapping.AgencyAndIdMapper.mapAgencyAndId; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.onebusaway.gtfs.model.Location; -import org.onebusaway.gtfs.model.Stop; -import org.opentripplanner.framework.collection.MapUtils; -import org.opentripplanner.framework.i18n.NonLocalizedString; -import org.opentripplanner.transit.model.site.GroupStop; -import org.opentripplanner.transit.model.site.GroupStopBuilder; -import org.opentripplanner.transit.service.StopModelBuilder; - -/** - * For a while GTFS Flex location groups were replaced by GTFS Fares v2 stop areas. After a few - * months, this decision was reverted and a new style of location groups were re-added to the Flex - * spec. - * @deprecated Arcadis tooling still produces stop areas and for a while we will support both. Please don't rely - * on this as the class will be removed in the future! - */ -@Deprecated -public class StopAreaMapper { - - private final StopMapper stopMapper; - - private final LocationMapper locationMapper; - - private final Map mappedStopAreas = new HashMap<>(); - private final StopModelBuilder stopModel; - - public StopAreaMapper( - StopMapper stopMapper, - LocationMapper locationMapper, - StopModelBuilder stopModel - ) { - this.stopMapper = stopMapper; - this.locationMapper = locationMapper; - this.stopModel = stopModel; - } - - Collection map(Collection allAreas) { - return MapUtils.mapToList(allAreas, this::map); - } - - /** Map from GTFS to OTP model, {@code null} safe. */ - GroupStop map(org.onebusaway.gtfs.model.StopArea original) { - return original == null ? null : mappedStopAreas.computeIfAbsent(original, this::doMap); - } - - private GroupStop doMap(org.onebusaway.gtfs.model.StopArea element) { - GroupStopBuilder groupStopBuilder = stopModel - .groupStop(mapAgencyAndId(element.getId())) - .withName(new NonLocalizedString(element.getName())); - - for (org.onebusaway.gtfs.model.StopLocation location : element.getLocations()) { - switch (location) { - case Stop stop -> groupStopBuilder.addLocation(stopMapper.map(stop)); - case Location loc -> groupStopBuilder.addLocation(locationMapper.map(loc)); - case org.onebusaway.gtfs.model.StopArea ignored -> throw new RuntimeException( - "Nested GroupStops are not allowed" - ); - case null, default -> throw new RuntimeException( - "Unknown location type: " + location.getClass().getSimpleName() - ); - } - } - - return groupStopBuilder.build(); - } -} diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java index 67b250c5061..5f20b6e0224 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/StopTimeMapper.java @@ -7,7 +7,6 @@ import org.onebusaway.gtfs.model.Location; import org.onebusaway.gtfs.model.LocationGroup; import org.onebusaway.gtfs.model.Stop; -import org.onebusaway.gtfs.model.StopArea; import org.opentripplanner.framework.collection.MapUtils; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.model.StopTime; @@ -22,7 +21,6 @@ class StopTimeMapper { private final LocationMapper locationMapper; private final LocationGroupMapper locationGroupMapper; - private final StopAreaMapper stopAreaMapper; private final TripMapper tripMapper; private final BookingRuleMapper bookingRuleMapper; @@ -35,7 +33,6 @@ class StopTimeMapper { StopMapper stopMapper, LocationMapper locationMapper, LocationGroupMapper locationGroupMapper, - StopAreaMapper stopAreaMapper, TripMapper tripMapper, BookingRuleMapper bookingRuleMapper, TranslationHelper translationHelper @@ -43,7 +40,6 @@ class StopTimeMapper { this.stopMapper = stopMapper; this.locationMapper = locationMapper; this.locationGroupMapper = locationGroupMapper; - this.stopAreaMapper = stopAreaMapper; this.tripMapper = tripMapper; this.bookingRuleMapper = bookingRuleMapper; this.translationHelper = translationHelper; @@ -71,8 +67,6 @@ private StopTime doMap(org.onebusaway.gtfs.model.StopTime rhs) { case Stop stop -> lhs.setStop(stopMapper.map(stop)); case Location location -> lhs.setStop(locationMapper.map(location)); case LocationGroup locGroup -> lhs.setStop(locationGroupMapper.map(locGroup)); - // TODO: only here for backwards compatibility, this will be removed in the future - case StopArea area -> lhs.setStop(stopAreaMapper.map(area)); default -> throw new IllegalArgumentException( "Unknown location type: %s".formatted(stopLocation) ); diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TranslationHelper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TranslationHelper.java index 03ed54d55f7..ce61c01b05e 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TranslationHelper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/TranslationHelper.java @@ -7,7 +7,6 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.onebusaway.csv_entities.schema.annotations.CsvField; import org.onebusaway.csv_entities.schema.annotations.CsvFieldNameConvention; @@ -83,9 +82,9 @@ void importTranslations(Collection allTranslations, Collection clazz, - @Nonnull String fieldName, - @Nonnull String recordId, + Class clazz, + String fieldName, + String recordId, @Nullable String defaultValue ) { return getTranslation(clazz, fieldName, recordId, null, defaultValue); @@ -93,9 +92,9 @@ I18NString getTranslation( @Nullable I18NString getTranslation( - @Nonnull Class clazz, - @Nonnull String fieldName, - @Nonnull String recordId, + Class clazz, + String fieldName, + String recordId, @Nullable String recordSubId, @Nullable String defaultValue ) { diff --git a/src/main/java/org/opentripplanner/model/GenericLocation.java b/src/main/java/org/opentripplanner/model/GenericLocation.java index 2b0ecc3f697..fd270741e92 100644 --- a/src/main/java/org/opentripplanner/model/GenericLocation.java +++ b/src/main/java/org/opentripplanner/model/GenericLocation.java @@ -17,23 +17,32 @@ public class GenericLocation { * A label for the place, if provided. This is pass-through information and does not affect * routing in any way. */ + @Nullable public final String label; /** * Refers to a specific element in the OTP model. This can currently be a regular stop, area stop, * group stop, station, multi-modal station or group of stations. */ + @Nullable public final FeedScopedId stopId; /** * Coordinates of the location. These can be used by themselves or as a fallback if placeId is not * found. */ + @Nullable public final Double lat; + @Nullable public final Double lng; - public GenericLocation(String label, FeedScopedId stopId, Double lat, Double lng) { + public GenericLocation( + @Nullable String label, + @Nullable FeedScopedId stopId, + @Nullable Double lat, + @Nullable Double lng + ) { this.label = label; this.stopId = stopId; this.lat = lat; diff --git a/src/main/java/org/opentripplanner/model/Timetable.java b/src/main/java/org/opentripplanner/model/Timetable.java index 10c384e6211..6740e2cc99d 100644 --- a/src/main/java/org/opentripplanner/model/Timetable.java +++ b/src/main/java/org/opentripplanner/model/Timetable.java @@ -16,13 +16,11 @@ import java.time.LocalDate; import java.time.ZoneId; import java.util.ArrayList; +import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; -import java.util.function.UnaryOperator; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.transit.model.framework.DataValidationException; @@ -48,8 +46,7 @@ * one Timetable when stop time updates are being applied: one for the scheduled stop times, one for * each snapshot of updated stop times, another for a working buffer of updated stop times, etc. *

- * TODO OTP2 - Move this to package: org.opentripplanner.model after as Entur NeTEx PRs are merged. - * Also consider moving its dependencies into package org.opentripplanner.routing. The NEW + * TODO OTP2 consider moving dependencies into package org.opentripplanner.routing. The NEW * Timetable should not have any dependencies to [?] */ public class Timetable implements Serializable { @@ -58,28 +55,30 @@ public class Timetable implements Serializable { private final TripPattern pattern; - private final List tripTimes = new ArrayList<>(); + private final List tripTimes; - private final List frequencyEntries = new ArrayList<>(); + private final List frequencyEntries; @Nullable private final LocalDate serviceDate; + Timetable(TimetableBuilder timetableBuilder) { + this.pattern = timetableBuilder.getPattern(); + this.serviceDate = timetableBuilder.getServiceDate(); + this.tripTimes = timetableBuilder.createImmutableOrderedListOfTripTimes(); + this.frequencyEntries = List.copyOf(timetableBuilder.getFrequencies()); + } + /** Construct an empty Timetable. */ - public Timetable(TripPattern pattern) { - this.pattern = pattern; - this.serviceDate = null; + public static TimetableBuilder of() { + return new TimetableBuilder(); } /** - * Copy constructor: create an un-indexed Timetable with the same TripTimes as the specified - * timetable. + * Copy timetable into a builder witch can be used to modify the timetable. */ - Timetable(Timetable tt, @Nonnull LocalDate serviceDate) { - Objects.requireNonNull(serviceDate); - tripTimes.addAll(tt.tripTimes); - this.serviceDate = serviceDate; - this.pattern = tt.pattern; + public TimetableBuilder copyOf() { + return new TimetableBuilder(this); } /** @return the index of TripTimes for this trip ID in this particular Timetable */ @@ -134,17 +133,6 @@ public TripTimes getTripTimes(FeedScopedId tripId) { return null; } - /** - * Set new trip times for trip given a trip index - * - * @param tripIndex trip index of trip - * @param tt new trip times for trip - * @return old trip times of trip - */ - public TripTimes setTripTimes(int tripIndex, TripTimes tt) { - return tripTimes.set(tripIndex, tt); - } - /** * Apply the TripUpdate to the appropriate TripTimes from this Timetable. The existing TripTimes * must not be modified directly because they may be shared with the underlying @@ -386,44 +374,6 @@ public Result createUpdatedTripTimesFromGTFSRT( return Result.success(new TripTimesPatch(newTimes, skippedStopIndices)); } - /** - * Add a trip to this Timetable. The Timetable must be analyzed, compacted, and indexed any time - * trips are added, but this is not done automatically because it is time consuming and should - * only be done once after an entire batch of trips are added. Note that the trip is not added to - * the enclosing pattern here, but in the pattern's wrapper function. Here we don't know if it's a - * scheduled trip or a realtime-added trip. - */ - public void addTripTimes(TripTimes tt) { - tripTimes.add(tt); - } - - /** - * Apply the same update to all trip-times inculuding scheduled and frequency based - * trip times. - *

- * THIS IS NOT THREAD-SAFE - ONLY USE THIS METHOD DURING GRAPH-BUILD! - */ - public void updateAllTripTimes(UnaryOperator update) { - tripTimes.replaceAll(update); - frequencyEntries.replaceAll(it -> - new FrequencyEntry( - it.startTime, - it.endTime, - it.headway, - it.exactTimes, - update.apply(it.tripTimes) - ) - ); - } - - /** - * Add a frequency entry to this Timetable. See addTripTimes method. Maybe Frequency Entries - * should just be TripTimes for simplicity. - */ - public void addFrequencyEntry(FrequencyEntry freq) { - frequencyEntries.add(freq); - } - public boolean isValidFor(LocalDate serviceDate) { return this.serviceDate == null || this.serviceDate.equals(serviceDate); } @@ -473,21 +423,61 @@ public LocalDate getServiceDate() { } /** - * The direction for all the trips in this pattern. + * Return the direction for all the trips in this timetable. + * By construction, all trips in a timetable have the same direction. */ public Direction getDirection() { + return getDirection(tripTimes, frequencyEntries); + } + + /** + * Return an arbitrary TripTimes in this Timetable. + * Return a scheduled trip times if it exists, otherwise return a frequency-based trip times. + */ + public TripTimes getRepresentativeTripTimes() { + return getRepresentativeTripTimes(tripTimes, frequencyEntries); + } + + /** + * @return true if the timetable was created by a real-time update, false if this + * timetable is based on scheduled data. + * Only real-time timetables have a service date. + */ + public boolean isCreatedByRealTimeUpdater() { + return serviceDate != null; + } + + /** + * The direction for the given collections of trip times. + * The method assumes that all trip times have the same directions and picks up one arbitrarily. + * @param scheduledTripTimes all the scheduled-based trip times in a timetable. + * @param frequencies all the frequency-based trip times in a timetable. + */ + static Direction getDirection( + Collection scheduledTripTimes, + Collection frequencies + ) { return Optional - .ofNullable(getRepresentativeTripTimes()) + .ofNullable(getRepresentativeTripTimes(scheduledTripTimes, frequencies)) .map(TripTimes::getTrip) .map(Trip::getDirection) .orElse(Direction.UNKNOWN); } - public TripTimes getRepresentativeTripTimes() { - if (!getTripTimes().isEmpty()) { - return getTripTimes(0); - } else if (!getFrequencyEntries().isEmpty()) { - return getFrequencyEntries().get(0).tripTimes; + /** + * Return an arbitrary TripTimes. + * @param scheduledTripTimes all the scheduled-based trip times in a timetable. + * @param frequencies all the frequency-based trip times in a timetable. + * + */ + private static TripTimes getRepresentativeTripTimes( + Collection scheduledTripTimes, + Collection frequencies + ) { + if (!scheduledTripTimes.isEmpty()) { + return scheduledTripTimes.iterator().next(); + } else if (!frequencies.isEmpty()) { + return frequencies.iterator().next().tripTimes; } else { // Pattern is created only for real-time updates return null; diff --git a/src/main/java/org/opentripplanner/model/TimetableBuilder.java b/src/main/java/org/opentripplanner/model/TimetableBuilder.java new file mode 100644 index 00000000000..74e89d0d973 --- /dev/null +++ b/src/main/java/org/opentripplanner/model/TimetableBuilder.java @@ -0,0 +1,136 @@ +package org.opentripplanner.model; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.UnaryOperator; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.timetable.Direction; +import org.opentripplanner.transit.model.timetable.FrequencyEntry; +import org.opentripplanner.transit.model.timetable.TripTimes; + +public class TimetableBuilder { + + private TripPattern pattern; + private LocalDate serviceDate; + private final Map tripTimes = new HashMap<>(); + private final List frequencies = new ArrayList<>(); + + TimetableBuilder() {} + + TimetableBuilder(Timetable tt) { + pattern = tt.getPattern(); + serviceDate = tt.getServiceDate(); + frequencies.addAll(tt.getFrequencyEntries()); + addAllTripTimes(tt.getTripTimes()); + } + + public TimetableBuilder withTripPattern(TripPattern tripPattern) { + this.pattern = tripPattern; + return this; + } + + public TimetableBuilder withServiceDate(LocalDate serviceDate) { + this.serviceDate = serviceDate; + return this; + } + + /** + * Add a new trip-times to the timetable. If the associated trip already exists, an exception is + * thrown. This is considered a programming error. Use {@link #addOrUpdateTripTimes(TripTimes)} + * if you want to replace an existing trip. + */ + public TimetableBuilder addTripTimes(TripTimes tripTimes) { + var trip = tripTimes.getTrip(); + if (this.tripTimes.containsKey(trip.getId())) { + throw new IllegalStateException( + "Error! TripTimes for the same trip is added twice. Trip: " + trip + ); + } + return addOrUpdateTripTimes(tripTimes); + } + + /** + * Add or update the trip-times. If the trip has an associated trip-times, then the trip-times + * are replaced. If not, the trip-times it is added. Consider using + * {@link #addTripTimes(TripTimes)}. + */ + public TimetableBuilder addOrUpdateTripTimes(TripTimes tripTimes) { + this.tripTimes.put(tripTimes.getTrip().getId(), tripTimes); + return this; + } + + public TimetableBuilder addAllTripTimes(List tripTimes) { + for (TripTimes it : tripTimes) { + addTripTimes(it); + } + return this; + } + + public TimetableBuilder removeTripTimes(TripTimes tripTimesToRemove) { + tripTimes.remove(tripTimesToRemove.getTrip().getId()); + return this; + } + + public TimetableBuilder removeAllTripTimes(Collection tripTimesToBeRemoved) { + for (TripTimes it : tripTimesToBeRemoved) { + tripTimes.remove(it.getTrip().getId()); + } + return this; + } + + /** + * Apply the same update to all trip-times including scheduled and frequency based + * trip times. + *

+ */ + public TimetableBuilder updateAllTripTimes(UnaryOperator update) { + tripTimes.replaceAll((t, tt) -> update.apply(tt)); + frequencies.replaceAll(it -> + new FrequencyEntry( + it.startTime, + it.endTime, + it.headway, + it.exactTimes, + update.apply(it.tripTimes) + ) + ); + return this; + } + + public TimetableBuilder addFrequencyEntry(FrequencyEntry frequencyEntry) { + this.frequencies.add(frequencyEntry); + return this; + } + + /** + * The direction for all the trips in this timetable. + */ + public Direction getDirection() { + return Timetable.getDirection(tripTimes.values(), frequencies); + } + + public Timetable build() { + return new Timetable(this); + } + + List createImmutableOrderedListOfTripTimes() { + return tripTimes.values().stream().sorted().toList(); + } + + TripPattern getPattern() { + return pattern; + } + + LocalDate getServiceDate() { + return serviceDate; + } + + List getFrequencies() { + return frequencies; + } +} diff --git a/src/main/java/org/opentripplanner/model/TimetableSnapshot.java b/src/main/java/org/opentripplanner/model/TimetableSnapshot.java index d076bf9f1f0..8cd0707d655 100644 --- a/src/main/java/org/opentripplanner/model/TimetableSnapshot.java +++ b/src/main/java/org/opentripplanner/model/TimetableSnapshot.java @@ -298,22 +298,15 @@ public Result update(RealTimeTripUpdate realTimeTrip } Timetable tt = resolve(pattern, serviceDate); - // we need to perform the copy of Timetable here rather than in Timetable.update() - // to avoid repeatedly copying in case several updates are applied to the same timetable - tt = copyTimetable(pattern, serviceDate, tt); + TimetableBuilder ttb = tt.copyOf().withServiceDate(serviceDate); // Assume all trips in a pattern are from the same feed, which should be the case. - // Find trip index - Trip trip = updatedTripTimes.getTrip(); - int tripIndex = tt.getTripIndex(trip.getId()); - if (tripIndex == -1) { - // Trip not found, add it - tt.addTripTimes(updatedTripTimes); - } else { - // Set updated trip times of trip - tt.setTripTimes(tripIndex, updatedTripTimes); - } + ttb.addOrUpdateTripTimes(updatedTripTimes); + + Timetable updated = ttb.build(); + swapTimetable(pattern, tt, updated); + Trip trip = updatedTripTimes.getTrip(); if (pattern.isCreatedByRealtimeUpdater()) { // Remember this pattern for the added trip id and service date FeedScopedId tripId = trip.getId(); @@ -459,8 +452,11 @@ public boolean revertTripToScheduledTripPattern(FeedScopedId tripId, LocalDate s if (tripTimesToRemove != null) { for (Timetable originalTimetable : sortedTimetables) { if (originalTimetable.getTripTimes().contains(tripTimesToRemove)) { - Timetable updatedTimetable = copyTimetable(pattern, serviceDate, originalTimetable); - updatedTimetable.getTripTimes().remove(tripTimesToRemove); + Timetable updatedTimetable = originalTimetable + .copyOf() + .removeTripTimes(tripTimesToRemove) + .build(); + swapTimetable(pattern, originalTimetable, updatedTimetable); } } } @@ -579,36 +575,33 @@ private void addPatternToIndex(TripPattern tripPattern) { } /** - * Make a copy of the given timetable for a given pattern and service date. - * If the timetable was already copied-on write in this snapshot, the same instance will be - * returned. The SortedSet that holds the collection of Timetables for that pattern + * Replace the original Timetable by the updated one in the timetable index. + * The SortedSet that holds the collection of Timetables for that pattern * (sorted by service date) is shared between multiple snapshots and must be copied as well.
* Note on performance: if multiple Timetables are modified in a SortedSet, the SortedSet will be * copied multiple times. The impact on memory/garbage collection is assumed to be minimal * since the collection is small. * The SortedSet is made immutable to prevent change after snapshot publication. */ - private Timetable copyTimetable(TripPattern pattern, LocalDate serviceDate, Timetable tt) { - if (!dirtyTimetables.contains(tt)) { - Timetable old = tt; - tt = new Timetable(tt, serviceDate); - SortedSet sortedTimetables = timetables.get(pattern); - if (sortedTimetables == null) { - sortedTimetables = new TreeSet<>(new SortedTimetableComparator()); - } else { - SortedSet temp = new TreeSet<>(new SortedTimetableComparator()); - temp.addAll(sortedTimetables); - sortedTimetables = temp; - } - if (old.getServiceDate() != null) { - sortedTimetables.remove(old); - } - sortedTimetables.add(tt); - timetables.put(pattern, ImmutableSortedSet.copyOfSorted(sortedTimetables)); - dirtyTimetables.add(tt); - dirty = true; + private void swapTimetable(TripPattern pattern, Timetable original, Timetable updated) { + SortedSet sortedTimetables = timetables.get(pattern); + if (sortedTimetables == null) { + sortedTimetables = new TreeSet<>(new SortedTimetableComparator()); + } else { + SortedSet temp = new TreeSet<>(new SortedTimetableComparator()); + temp.addAll(sortedTimetables); + sortedTimetables = temp; + } + // This is a minor optimization: + // Since sortedTimetables contains only timetables created in real-time, no need to try to + // remove the original if it was not created by real-time. + if (original.isCreatedByRealTimeUpdater()) { + sortedTimetables.remove(original); } - return tt; + sortedTimetables.add(updated); + timetables.put(pattern, ImmutableSortedSet.copyOfSorted(sortedTimetables)); + dirtyTimetables.add(updated); + dirty = true; } protected static class SortedTimetableComparator implements Comparator { diff --git a/src/main/java/org/opentripplanner/model/calendar/ServiceDateInterval.java b/src/main/java/org/opentripplanner/model/calendar/ServiceDateInterval.java index 5d2657912fc..a879f6c790e 100644 --- a/src/main/java/org/opentripplanner/model/calendar/ServiceDateInterval.java +++ b/src/main/java/org/opentripplanner/model/calendar/ServiceDateInterval.java @@ -6,7 +6,6 @@ import java.time.LocalDate; import java.time.temporal.ChronoUnit; import java.util.Objects; -import javax.annotation.Nonnull; import org.opentripplanner.framework.time.ServiceDateUtils; /** @@ -52,7 +51,6 @@ public boolean isUnbounded() { * Return the interval start, inclusive. If the period start is unbounded the {@link * LocalDate#MIN} is returned. */ - @Nonnull public LocalDate getStart() { return start; } @@ -61,7 +59,6 @@ public LocalDate getStart() { * Return the interval end, inclusive. If the period start is unbounded the {@link * LocalDate#MAX} is returned. */ - @Nonnull public LocalDate getEnd() { return end; } diff --git a/src/main/java/org/opentripplanner/model/fare/RiderCategory.java b/src/main/java/org/opentripplanner/model/fare/RiderCategory.java index 43f777cd31a..df68d3a8ce6 100644 --- a/src/main/java/org/opentripplanner/model/fare/RiderCategory.java +++ b/src/main/java/org/opentripplanner/model/fare/RiderCategory.java @@ -1,13 +1,12 @@ package org.opentripplanner.model.fare; import java.util.Objects; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.lang.Sandbox; import org.opentripplanner.transit.model.framework.FeedScopedId; @Sandbox -public record RiderCategory(@Nonnull FeedScopedId id, @Nonnull String name, @Nullable String url) { +public record RiderCategory(FeedScopedId id, String name, @Nullable String url) { public RiderCategory { Objects.requireNonNull(id); Objects.requireNonNull(name); diff --git a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java index 373b99f0bc6..d62d0331a34 100644 --- a/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java +++ b/src/main/java/org/opentripplanner/model/impl/OtpTransitServiceBuilder.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.gtfs.mapping.StaySeatedNotAllowed; @@ -15,6 +16,7 @@ import org.opentripplanner.model.Frequency; import org.opentripplanner.model.OtpTransitService; import org.opentripplanner.model.ShapePoint; +import org.opentripplanner.model.Timetable; import org.opentripplanner.model.TripStopTimes; import org.opentripplanner.model.calendar.CalendarServiceData; import org.opentripplanner.model.calendar.ServiceCalendar; @@ -51,6 +53,7 @@ import org.opentripplanner.transit.model.site.Station; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; +import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.StopModelBuilder; import org.slf4j.Logger; @@ -375,21 +378,40 @@ private void removeStopTimesForNoneExistingTrips() { private void fixOrRemovePatternsWhichReferenceNoneExistingTrips() { int orgSize = tripPatterns.size(); List> removePatterns = new ArrayList<>(); + List updatedPatterns = new ArrayList<>(); for (Map.Entry e : tripPatterns.entries()) { TripPattern ptn = e.getValue(); - ptn.removeTrips(t -> !tripsById.containsKey(t.getId())); - if (ptn.scheduledTripsAsStream().findAny().isEmpty()) { + Set tripTimesToBeRemoved = ptn + .getScheduledTimetable() + .getTripTimes() + .stream() + .filter(tripTimes -> !tripsById.containsKey(tripTimes.getTrip().getId())) + .collect(Collectors.toUnmodifiableSet()); + if (!tripTimesToBeRemoved.isEmpty()) { removePatterns.add(e); + Timetable updatedTimetable = ptn + .getScheduledTimetable() + .copyOf() + .removeAllTripTimes(tripTimesToBeRemoved) + .build(); + TripPattern updatedPattern = ptn.copy().withScheduledTimeTable(updatedTimetable).build(); + if (!updatedTimetable.getTripTimes().isEmpty()) { + updatedPatterns.add(updatedPattern); + } else { + issueStore.add( + "RemovedEmptyTripPattern", + "Removed trip pattern %s as it contains no trips", + updatedPattern.getId() + ); + } } } for (Map.Entry it : removePatterns) { tripPatterns.remove(it.getKey(), it.getValue()); - issueStore.add( - "RemovedEmptyTripPattern", - "Removed trip pattern %s as it contains no trips", - it.getValue().getId() - ); + } + for (TripPattern tripPattern : updatedPatterns) { + tripPatterns.put(tripPattern.getStopPattern(), tripPattern); } logRemove("TripPattern", orgSize, tripPatterns.size(), "No trips for pattern exist."); } diff --git a/src/main/java/org/opentripplanner/model/modes/AllowMainAndSubModeFilter.java b/src/main/java/org/opentripplanner/model/modes/AllowMainAndSubModeFilter.java index ec197522590..1b34acd5327 100644 --- a/src/main/java/org/opentripplanner/model/modes/AllowMainAndSubModeFilter.java +++ b/src/main/java/org/opentripplanner/model/modes/AllowMainAndSubModeFilter.java @@ -1,7 +1,6 @@ package org.opentripplanner.model.modes; import java.util.Objects; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.transit.model.basic.MainAndSubMode; @@ -14,12 +13,12 @@ class AllowMainAndSubModeFilter implements AllowTransitModeFilter { private final SubMode subMode; - AllowMainAndSubModeFilter(@Nonnull TransitMode mainMode, @Nullable SubMode subMode) { + AllowMainAndSubModeFilter(TransitMode mainMode, @Nullable SubMode subMode) { this.mainMode = mainMode; this.subMode = subMode; } - AllowMainAndSubModeFilter(@Nonnull MainAndSubMode mode) { + AllowMainAndSubModeFilter(MainAndSubMode mode) { this(mode.mainMode(), mode.subMode()); } @@ -34,7 +33,6 @@ public boolean match(TransitMode transitMode, SubMode netexSubMode) { return mainMode == transitMode && subMode == netexSubMode; } - @Nonnull TransitMode mainMode() { return mainMode; } diff --git a/src/main/java/org/opentripplanner/model/plan/Leg.java b/src/main/java/org/opentripplanner/model/plan/Leg.java index 2a0b6726560..d9e3a4589d8 100644 --- a/src/main/java/org/opentripplanner/model/plan/Leg.java +++ b/src/main/java/org/opentripplanner/model/plan/Leg.java @@ -22,6 +22,7 @@ import org.opentripplanner.transit.model.organization.Agency; import org.opentripplanner.transit.model.organization.Operator; import org.opentripplanner.transit.model.site.FareZone; +import org.opentripplanner.transit.model.timetable.RealTimeState; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.booking.BookingInfo; @@ -244,6 +245,10 @@ default boolean getRealTime() { return false; } + default RealTimeState getRealTimeState() { + return null; + } + /** * Whether this Leg describes a flexible trip. The reason we need this is that FlexTrip does not * inherit from Trip, so that the information that the Trip is flexible would be lost when diff --git a/src/main/java/org/opentripplanner/model/plan/LegTime.java b/src/main/java/org/opentripplanner/model/plan/LegTime.java index 354ce4f4b0b..71ced95942a 100644 --- a/src/main/java/org/opentripplanner/model/plan/LegTime.java +++ b/src/main/java/org/opentripplanner/model/plan/LegTime.java @@ -3,24 +3,21 @@ import java.time.Duration; import java.time.ZonedDateTime; import java.util.Objects; -import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * A scheduled time of a transit vehicle at a certain location with a optional realtime information. */ -public record LegTime(@Nonnull ZonedDateTime scheduledTime, @Nullable RealTimeEstimate estimated) { +public record LegTime(ZonedDateTime scheduledTime, @Nullable RealTimeEstimate estimated) { public LegTime { Objects.requireNonNull(scheduledTime); } - @Nonnull public static LegTime of(ZonedDateTime realtime, int delaySecs) { var delay = Duration.ofSeconds(delaySecs); return new LegTime(realtime.minus(delay), new RealTimeEstimate(realtime, delay)); } - @Nonnull public static LegTime ofStatic(ZonedDateTime staticTime) { return new LegTime(staticTime, null); } diff --git a/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java b/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java index d94ec1895c2..7af091531e0 100644 --- a/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java +++ b/src/main/java/org/opentripplanner/model/plan/ScheduledTransitLeg.java @@ -10,7 +10,6 @@ import java.util.List; import java.util.Objects; import java.util.Set; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.LineString; @@ -34,6 +33,7 @@ import org.opentripplanner.transit.model.organization.Agency; import org.opentripplanner.transit.model.organization.Operator; import org.opentripplanner.transit.model.site.StopLocation; +import org.opentripplanner.transit.model.timetable.RealTimeState; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; @@ -181,7 +181,6 @@ public LegTime end() { } @Override - @Nonnull public TransitMode getMode() { return getTrip().getMode(); } @@ -227,6 +226,11 @@ public boolean getRealTime() { ); } + @Override + public RealTimeState getRealTimeState() { + return tripTimes.getRealTimeState(); + } + @Override public double getDistanceMeters() { return distanceMeters; diff --git a/src/main/java/org/opentripplanner/model/plan/TransitLeg.java b/src/main/java/org/opentripplanner/model/plan/TransitLeg.java index 736739ca970..f7ac3e42544 100644 --- a/src/main/java/org/opentripplanner/model/plan/TransitLeg.java +++ b/src/main/java/org/opentripplanner/model/plan/TransitLeg.java @@ -1,13 +1,11 @@ package org.opentripplanner.model.plan; -import javax.annotation.Nonnull; import org.opentripplanner.transit.model.basic.TransitMode; public interface TransitLeg extends Leg { /** * The mode (e.g., BUS) used when traversing this leg. */ - @Nonnull TransitMode getMode(); @Override diff --git a/src/main/java/org/opentripplanner/netex/NetexBundle.java b/src/main/java/org/opentripplanner/netex/NetexBundle.java index 3cd52cd246e..a0e0aae8500 100644 --- a/src/main/java/org/opentripplanner/netex/NetexBundle.java +++ b/src/main/java/org/opentripplanner/netex/NetexBundle.java @@ -3,6 +3,7 @@ import jakarta.xml.bind.JAXBException; import java.io.Closeable; import java.io.IOException; +import java.util.Collection; import java.util.List; import java.util.Set; import org.opentripplanner.datastore.api.CompositeDataSource; @@ -19,6 +20,7 @@ import org.opentripplanner.netex.mapping.NetexMapper; import org.opentripplanner.netex.validation.Validator; import org.opentripplanner.transit.model.framework.Deduplicator; +import org.opentripplanner.transit.model.framework.FeedScopedId; import org.rutebanken.netex.model.PublicationDeliveryStructure; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,6 +46,7 @@ public class NetexBundle implements Closeable { private final String feedId; private final Set ferryIdsNotAllowedForBicycle; + private final Collection routeToCentroidStopPlaceIds; private final double maxStopToShapeSnapDistance; private final boolean noTransfersOnIsolatedStops; private final Set ignoredFeatures; @@ -61,6 +64,7 @@ public NetexBundle( NetexDataSourceHierarchy hierarchy, OtpTransitServiceBuilder transitBuilder, Set ferryIdsNotAllowedForBicycle, + Collection routeToCentroidStopPlaceIds, double maxStopToShapeSnapDistance, boolean noTransfersOnIsolatedStops, Set ignorableFeatures @@ -70,6 +74,7 @@ public NetexBundle( this.hierarchy = hierarchy; this.transitBuilder = transitBuilder; this.ferryIdsNotAllowedForBicycle = ferryIdsNotAllowedForBicycle; + this.routeToCentroidStopPlaceIds = Set.copyOf(routeToCentroidStopPlaceIds); this.maxStopToShapeSnapDistance = maxStopToShapeSnapDistance; this.noTransfersOnIsolatedStops = noTransfersOnIsolatedStops; this.ignoredFeatures = Set.copyOf(ignorableFeatures); @@ -93,6 +98,7 @@ public OtpTransitServiceBuilder loadBundle( deduplicator, issueStore, ferryIdsNotAllowedForBicycle, + routeToCentroidStopPlaceIds, maxStopToShapeSnapDistance, noTransfersOnIsolatedStops ); diff --git a/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java b/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java index 0c6c75c4db3..723c707ecf1 100644 --- a/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java +++ b/src/main/java/org/opentripplanner/netex/config/NetexFeedParameters.java @@ -34,6 +34,7 @@ public class NetexFeedParameters implements DataSourceConfig { private static final Set IGNORED_FEATURES = Set.of(PARKING); private static final Set FERRY_IDS_NOT_ALLOWED_FOR_BICYCLE = Collections.emptySet(); + private static final Set ROUTE_TO_CENTROID_STATION_IDS = Collections.emptySet(); public static final NetexFeedParameters DEFAULT = new NetexFeedParameters(); @@ -196,6 +197,7 @@ public static class Builder { private String groupFilePattern; private String ignoreFilePattern; private final Set ferryIdsNotAllowedForBicycle = new HashSet<>(); + private final Set routeToCentroidStopPlaceIds = new HashSet<>(); private boolean noTransfersOnIsolatedStops; private final Set ignoredFeatures; @@ -251,6 +253,11 @@ public Builder addFerryIdsNotAllowedForBicycle(Collection ferryId) { return this; } + public Builder addRouteToCentroidStopPlaceIds(Collection stationIds) { + routeToCentroidStopPlaceIds.addAll(stationIds); + return this; + } + public Builder withNoTransfersOnIsolatedStops(boolean noTransfersOnIsolatedStops) { this.noTransfersOnIsolatedStops = noTransfersOnIsolatedStops; return this; diff --git a/src/main/java/org/opentripplanner/netex/configure/NetexConfigure.java b/src/main/java/org/opentripplanner/netex/configure/NetexConfigure.java index 50c49836246..5f4b665e362 100644 --- a/src/main/java/org/opentripplanner/netex/configure/NetexConfigure.java +++ b/src/main/java/org/opentripplanner/netex/configure/NetexConfigure.java @@ -73,6 +73,7 @@ public NetexBundle netexBundle( hierarchy(source, config), transitServiceBuilder, config.ferryIdsNotAllowedForBicycle(), + buildParams.transitRouteToStationCentroid(), buildParams.maxStopToShapeSnapDistance, config.noTransfersOnIsolatedStops(), config.ignoredFeatures() diff --git a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java index 025a2349874..909d7b1c69f 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/NetexMapper.java @@ -66,6 +66,7 @@ public class NetexMapper { private final CalendarServiceBuilder calendarServiceBuilder; private final TripCalendarBuilder tripCalendarBuilder; private final Set ferryIdsNotAllowedForBicycle; + private final Set routeToCentroidStopPlaceIds; private final double maxStopToShapeSnapDistance; private final boolean noTransfersOnIsolatedStops; @@ -93,6 +94,7 @@ public NetexMapper( Deduplicator deduplicator, DataImportIssueStore issueStore, Set ferryIdsNotAllowedForBicycle, + Collection routeToCentroidStopPlaceIds, double maxStopToShapeSnapDistance, boolean noTransfersOnIsolatedStops ) { @@ -101,6 +103,7 @@ public NetexMapper( this.idFactory = new FeedScopedIdFactory(feedId); this.issueStore = issueStore; this.ferryIdsNotAllowedForBicycle = ferryIdsNotAllowedForBicycle; + this.routeToCentroidStopPlaceIds = Set.copyOf(routeToCentroidStopPlaceIds); this.noTransfersOnIsolatedStops = noTransfersOnIsolatedStops; this.maxStopToShapeSnapDistance = maxStopToShapeSnapDistance; this.calendarServiceBuilder = new CalendarServiceBuilder(idFactory); @@ -309,7 +312,8 @@ private void mapStopPlaceAndQuays(TariffZoneMapper tariffZoneMapper) { transitBuilder.stopModel(), zoneId, issueStore, - noTransfersOnIsolatedStops + noTransfersOnIsolatedStops, + routeToCentroidStopPlaceIds ); for (String stopPlaceId : currentNetexIndex.getStopPlaceById().localKeys()) { Collection stopPlaceAllVersions = currentNetexIndex diff --git a/src/main/java/org/opentripplanner/netex/mapping/QuayMapper.java b/src/main/java/org/opentripplanner/netex/mapping/QuayMapper.java index 5e9285043d5..a304ca32f72 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/QuayMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/QuayMapper.java @@ -1,7 +1,6 @@ package org.opentripplanner.netex.mapping; import java.util.Collection; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; @@ -39,7 +38,7 @@ class QuayMapper { */ @Nullable RegularStop mapQuayToStop( - @Nonnull Quay quay, + Quay quay, Station parentStation, Collection fareZones, NetexMainAndSubMode transitMode, @@ -54,7 +53,7 @@ RegularStop mapQuayToStop( private RegularStop map( FeedScopedId id, - @Nonnull Quay quay, + Quay quay, Station parentStation, Collection fareZones, NetexMainAndSubMode transitMode, diff --git a/src/main/java/org/opentripplanner/netex/mapping/StationMapper.java b/src/main/java/org/opentripplanner/netex/mapping/StationMapper.java index e94cf23fc4a..8840f7e4bdf 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/StationMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/StationMapper.java @@ -7,6 +7,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; @@ -34,6 +35,9 @@ class StationMapper { private final ZoneId defaultTimeZone; private final boolean noTransfersOnIsolatedStops; + + private final Set routeToCentroidStopPlaceIds; + private final StopModelBuilder stopModelBuilder; StationMapper( @@ -41,12 +45,14 @@ class StationMapper { FeedScopedIdFactory idFactory, ZoneId defaultTimeZone, boolean noTransfersOnIsolatedStops, + Set routeToCentroidStopPlaceIds, StopModelBuilder stopModelBuilder ) { this.issueStore = issueStore; this.idFactory = idFactory; this.defaultTimeZone = defaultTimeZone; this.noTransfersOnIsolatedStops = noTransfersOnIsolatedStops; + this.routeToCentroidStopPlaceIds = routeToCentroidStopPlaceIds; this.stopModelBuilder = stopModelBuilder; } @@ -60,6 +66,7 @@ Station mapStopPlaceToStation(FeedScopedId id, StopPlace stopPlace) { .of(id) .withName(resolveName(stopPlace)) .withCoordinate(mapCoordinate(stopPlace)) + .withShouldRouteToCentroid(shouldRouteToCentroid(id)) .withDescription( NonLocalizedString.ofNullable(stopPlace.getDescription(), MultilingualString::getValue) ) @@ -81,6 +88,10 @@ Station mapStopPlaceToStation(FeedScopedId id, StopPlace stopPlace) { return builder.build(); } + private boolean shouldRouteToCentroid(FeedScopedId stopPlaceId) { + return routeToCentroidStopPlaceIds.contains(stopPlaceId); + } + private ZoneId ofZoneId(String stopPlaceId, String zoneId) { try { return ZoneId.of(zoneId); diff --git a/src/main/java/org/opentripplanner/netex/mapping/StopAndStationMapper.java b/src/main/java/org/opentripplanner/netex/mapping/StopAndStationMapper.java index dd6d6a6f9f7..c5618a03a1a 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/StopAndStationMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/StopAndStationMapper.java @@ -22,6 +22,7 @@ import org.opentripplanner.netex.mapping.support.NetexMainAndSubMode; import org.opentripplanner.netex.mapping.support.StopPlaceVersionAndValidityComparator; import org.opentripplanner.transit.model.basic.Accessibility; +import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.site.FareZone; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.Station; @@ -70,7 +71,8 @@ class StopAndStationMapper { StopModelBuilder stopModelBuilder, ZoneId defaultTimeZone, DataImportIssueStore issueStore, - boolean noTransfersOnIsolatedStops + boolean noTransfersOnIsolatedStops, + Set routeToCentroidStopPlaceIds ) { this.stationMapper = new StationMapper( @@ -78,6 +80,7 @@ class StopAndStationMapper { idFactory, defaultTimeZone, noTransfersOnIsolatedStops, + routeToCentroidStopPlaceIds, stopModelBuilder ); this.quayMapper = new QuayMapper(idFactory, issueStore, stopModelBuilder); diff --git a/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java b/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java index 740224b4489..c7b85e72af8 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java @@ -255,8 +255,10 @@ Optional mapTripPattern(JourneyPattern_VersionStructure .withHopGeometries( serviceLinkMapper.getGeometriesByJourneyPattern(journeyPattern, stopPattern) ) + .withScheduledTimeTableBuilder(builder -> + builder.addAllTripTimes(createTripTimes(trips, tripStopTimes)) + ) .build(); - createTripTimes(trips, tripStopTimes).forEach(tripPattern::add); return Optional.of( new TripPatternMapperResult( diff --git a/src/main/java/org/opentripplanner/netex/mapping/calendar/OperatingDayMapper.java b/src/main/java/org/opentripplanner/netex/mapping/calendar/OperatingDayMapper.java index 63c554c95fa..be398d34902 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/calendar/OperatingDayMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/calendar/OperatingDayMapper.java @@ -1,7 +1,6 @@ package org.opentripplanner.netex.mapping.calendar; import java.time.LocalDate; -import javax.annotation.Nonnull; import org.rutebanken.netex.model.OperatingDay; class OperatingDayMapper { @@ -9,7 +8,7 @@ class OperatingDayMapper { /** Utility class, prevent instantiation. */ private OperatingDayMapper() {} - static LocalDate map(@Nonnull OperatingDay opDay) { + static LocalDate map(OperatingDay opDay) { return opDay.getCalendarDate().toLocalDate(); } } diff --git a/src/main/java/org/opentripplanner/netex/mapping/support/NetexObjectDecorator.java b/src/main/java/org/opentripplanner/netex/mapping/support/NetexObjectDecorator.java index e096cc499b7..1f4b8b0c786 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/support/NetexObjectDecorator.java +++ b/src/main/java/org/opentripplanner/netex/mapping/support/NetexObjectDecorator.java @@ -1,7 +1,6 @@ package org.opentripplanner.netex.mapping.support; import java.util.function.Consumer; -import javax.annotation.Nonnull; import org.rutebanken.netex.model.VersionOfObjectRefStructure; import org.slf4j.Logger; @@ -46,7 +45,7 @@ public static void foo() {} * the right scope - this class is just a utility class, or the messenger. * @param ref the unexpected reference to an unmapped object. */ - public static void logUnmappedEntityRef(Logger log, @Nonnull VersionOfObjectRefStructure ref) { + public static void logUnmappedEntityRef(Logger log, VersionOfObjectRefStructure ref) { log.warn("Unexpected entity {} in NeTEx import. The entity is ignored.", ref); } } diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java index b53739bf6a1..6c5af2b26c6 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java @@ -11,7 +11,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; @@ -199,7 +198,6 @@ public String getTag(String tag) { * * @return A tags value converted to lower case. An empty Optional if tags is not present. */ - @Nonnull public Optional getTagOpt(String network) { return Optional.ofNullable(getTag(network)); } @@ -513,7 +511,6 @@ public String url() { *

* Values are split by semicolons. */ - @Nonnull public Set getMultiTagValues(Set refTags) { return refTags .stream() @@ -622,7 +619,6 @@ private boolean isTagDeniedAccess(String tagName) { * Returns level tag (i.e. building floor) or layer tag values, defaults to "0" * Some entities can have a semicolon separated list of levels (e.g. elevators) */ - @Nonnull public Set getLevels() { var levels = getMultiTagValues(LEVEL_TAGS); if (levels.isEmpty()) { diff --git a/src/main/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapper.java b/src/main/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapper.java index c5d1c0d1582..03af12985c0 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapper.java +++ b/src/main/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapper.java @@ -613,7 +613,10 @@ public void populateProperties(WayPropertySet props) { // slope overrides props.setSlopeOverride(new BestMatchSpecifier("bridge=*"), true); props.setSlopeOverride(new BestMatchSpecifier("embankment=*"), true); + props.setSlopeOverride(new BestMatchSpecifier("cutting=*"), true); props.setSlopeOverride(new BestMatchSpecifier("tunnel=*"), true); + props.setSlopeOverride(new BestMatchSpecifier("location=underground"), true); + props.setSlopeOverride(new BestMatchSpecifier("indoor=yes"), true); } public void populateNotesAndNames(WayPropertySet props) { diff --git a/src/main/java/org/opentripplanner/openstreetmap/wayproperty/WayProperties.java b/src/main/java/org/opentripplanner/openstreetmap/wayproperty/WayProperties.java index 4facfd041c8..fd8b67bada2 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/wayproperty/WayProperties.java +++ b/src/main/java/org/opentripplanner/openstreetmap/wayproperty/WayProperties.java @@ -2,7 +2,6 @@ import java.util.Objects; import java.util.Optional; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.street.model.StreetTraversalPermission; @@ -13,7 +12,6 @@ */ public class WayProperties { - @Nonnull private final StreetTraversalPermission permission; @Nullable @@ -31,7 +29,6 @@ public class WayProperties { /** * The value for the bicycle safety. If none has been set a default value of 1 is returned. */ - @Nonnull public SafetyFeatures bicycleSafety() { return Objects.requireNonNullElse(bicycleSafetyFeatures, SafetyFeatures.DEFAULT); } @@ -39,12 +36,10 @@ public SafetyFeatures bicycleSafety() { /** * The value for the walk safety. If none has been set a default value of 1 is returned. */ - @Nonnull public SafetyFeatures walkSafety() { return Objects.requireNonNullElse(walkSafetyFeatures, SafetyFeatures.DEFAULT); } - @Nonnull public StreetTraversalPermission getPermission() { return permission; } @@ -52,7 +47,6 @@ public StreetTraversalPermission getPermission() { /** * An optional value for the walk safety. If none has been set an empty Optional is returned. */ - @Nonnull protected Optional walkSafetyOpt() { return Optional.ofNullable(walkSafetyFeatures); } @@ -60,7 +54,6 @@ protected Optional walkSafetyOpt() { /** * An optional value for the bicycle safety. If none has been set an empty Optional is returned. */ - @Nonnull protected Optional bicycleSafetyOpt() { return Optional.ofNullable(bicycleSafetyFeatures); } diff --git a/src/main/java/org/opentripplanner/raptor/api/path/AccessPathLeg.java b/src/main/java/org/opentripplanner/raptor/api/path/AccessPathLeg.java index b76e0a1df4d..cb4a8030ff6 100644 --- a/src/main/java/org/opentripplanner/raptor/api/path/AccessPathLeg.java +++ b/src/main/java/org/opentripplanner/raptor/api/path/AccessPathLeg.java @@ -1,7 +1,6 @@ package org.opentripplanner.raptor.api.path; import java.util.Objects; -import javax.annotation.Nonnull; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; import org.opentripplanner.raptor.api.model.RaptorTripSchedule; @@ -20,11 +19,11 @@ public final class AccessPathLeg implements PathLe private final PathLeg next; public AccessPathLeg( - @Nonnull RaptorAccessEgress access, + RaptorAccessEgress access, int fromTime, int toTime, int c1, - @Nonnull PathLeg next + PathLeg next ) { this.access = access; this.fromTime = fromTime; diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/path/PathParetoSetComparators.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/path/PathParetoSetComparators.java index 192f957ae8f..22212517ce1 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/path/PathParetoSetComparators.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/path/PathParetoSetComparators.java @@ -8,7 +8,6 @@ import static org.opentripplanner.raptor.api.path.RaptorPath.compareNumberOfTransfers; import java.util.Objects; -import javax.annotation.Nonnull; import org.opentripplanner.raptor.api.model.DominanceFunction; import org.opentripplanner.raptor.api.model.RaptorTripSchedule; import org.opentripplanner.raptor.api.model.RelaxFunction; @@ -128,9 +127,7 @@ > ParetoComparator> comparatorTimetableAndC1() { private static < T extends RaptorTripSchedule - > ParetoComparator> comparatorTimetableAndRelaxedC1( - @Nonnull final RelaxFunction relaxCost - ) { + > ParetoComparator> comparatorTimetableAndRelaxedC1(final RelaxFunction relaxCost) { return (l, r) -> compareIterationDepartureTime(l, r) || compareArrivalTime(l, r) || @@ -161,9 +158,7 @@ > ParetoComparator> comparatorDepartureTimeAndC1() { private static < T extends RaptorTripSchedule - > ParetoComparator> comparatorArrivalTimeAndRelaxedC1( - @Nonnull RelaxFunction relaxCost - ) { + > ParetoComparator> comparatorArrivalTimeAndRelaxedC1(RelaxFunction relaxCost) { return (l, r) -> compareArrivalTime(l, r) || compareNumberOfTransfers(l, r) || @@ -173,9 +168,7 @@ > ParetoComparator> comparatorArrivalTimeAndRelaxedC1( private static < T extends RaptorTripSchedule - > ParetoComparator> comparatorDepartureTimeAndRelaxedC1( - @Nonnull RelaxFunction relaxCost - ) { + > ParetoComparator> comparatorDepartureTimeAndRelaxedC1(RelaxFunction relaxCost) { return (l, r) -> compareDepartureTime(l, r) || compareNumberOfTransfers(l, r) || @@ -185,9 +178,7 @@ > ParetoComparator> comparatorDepartureTimeAndRelaxedC1( private static < T extends RaptorTripSchedule - > ParetoComparator> comparatorTimetableAndC1AndC2( - @Nonnull DominanceFunction c2Comp - ) { + > ParetoComparator> comparatorTimetableAndC1AndC2(DominanceFunction c2Comp) { return (l, r) -> compareIterationDepartureTime(l, r) || compareArrivalTime(l, r) || @@ -200,8 +191,8 @@ > ParetoComparator> comparatorTimetableAndC1AndC2( private static < T extends RaptorTripSchedule > ParetoComparator> comparatorTimetableAndRelaxedC1IfC2IsOptimal( - @Nonnull RelaxFunction relaxCost, - @Nonnull DominanceFunction c2Comp + RelaxFunction relaxCost, + DominanceFunction c2Comp ) { return (l, r) -> compareIterationDepartureTime(l, r) || @@ -213,7 +204,7 @@ > ParetoComparator> comparatorTimetableAndRelaxedC1IfC2IsOptimal( private static < T extends RaptorTripSchedule - > ParetoComparator> comparatorWithC1AndC2(@Nonnull DominanceFunction c2Comp) { + > ParetoComparator> comparatorWithC1AndC2(DominanceFunction c2Comp) { return (l, r) -> compareArrivalTime(l, r) || compareNumberOfTransfers(l, r) || @@ -224,9 +215,7 @@ > ParetoComparator> comparatorWithC1AndC2(@Nonnull DominanceFuncti private static < T extends RaptorTripSchedule - > ParetoComparator> comparatorDepartureTimeAndC1AndC2( - @Nonnull DominanceFunction c2Comp - ) { + > ParetoComparator> comparatorDepartureTimeAndC1AndC2(DominanceFunction c2Comp) { return (l, r) -> compareDepartureTime(l, r) || compareNumberOfTransfers(l, r) || @@ -238,8 +227,8 @@ > ParetoComparator> comparatorDepartureTimeAndC1AndC2( private static < T extends RaptorTripSchedule > ParetoComparator> comparatorArrivalTimeAndRelaxedC1IfC2IsOptimal( - @Nonnull RelaxFunction relaxCost, - @Nonnull DominanceFunction c2Comp + RelaxFunction relaxCost, + DominanceFunction c2Comp ) { return (l, r) -> compareArrivalTime(l, r) || @@ -251,8 +240,8 @@ > ParetoComparator> comparatorArrivalTimeAndRelaxedC1IfC2IsOptimal private static < T extends RaptorTripSchedule > ParetoComparator> comparatorDepartureTimeAndRelaxedC1IfC2IsOptimal( - @Nonnull RelaxFunction relaxCost, - @Nonnull DominanceFunction c2Comp + RelaxFunction relaxCost, + DominanceFunction c2Comp ) { return (l, r) -> compareDepartureTime(l, r) || @@ -262,10 +251,10 @@ > ParetoComparator> comparatorDepartureTimeAndRelaxedC1IfC2IsOptim } private static boolean compareC1RelaxedIfC2IsOptimal( - @Nonnull RaptorPath l, - @Nonnull RaptorPath r, - @Nonnull RelaxFunction relaxCost, - @Nonnull DominanceFunction c2Comp + RaptorPath l, + RaptorPath r, + RelaxFunction relaxCost, + DominanceFunction c2Comp ) { return c2Comp.leftDominateRight(l.c2(), r.c2()) ? compareC1(relaxCost, l, r) : compareC1(l, r); } diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/StopsCursor.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/StopsCursor.java index f72047597a7..32406951eda 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/StopsCursor.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/stoparrivals/view/StopsCursor.java @@ -1,7 +1,6 @@ package org.opentripplanner.raptor.rangeraptor.standard.stoparrivals.view; import java.util.function.ToIntFunction; -import javax.annotation.Nonnull; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; import org.opentripplanner.raptor.api.model.RaptorConstants; import org.opentripplanner.raptor.api.model.RaptorTransfer; @@ -139,7 +138,7 @@ public ArrivalView stop(int round, int stop, boolean stopReachedOnBoard) { * Set cursor to stop followed by the give transit leg - this allows access to be time-shifted * according to the next transit boarding/departure time. */ - public ArrivalView stop(int round, int stop, @Nonnull Transit nextTransitLeg) { + public ArrivalView stop(int round, int stop, Transit nextTransitLeg) { var arrival = arrivals.get(round, stop); if (arrival.arrivedByAccessOnStreet()) { diff --git a/src/main/java/org/opentripplanner/raptor/spi/EmptyBoardOrAlightEvent.java b/src/main/java/org/opentripplanner/raptor/spi/EmptyBoardOrAlightEvent.java index 9d29fc50d38..07984903f2a 100644 --- a/src/main/java/org/opentripplanner/raptor/spi/EmptyBoardOrAlightEvent.java +++ b/src/main/java/org/opentripplanner/raptor/spi/EmptyBoardOrAlightEvent.java @@ -1,7 +1,6 @@ package org.opentripplanner.raptor.spi; import java.util.function.Consumer; -import javax.annotation.Nonnull; import org.opentripplanner.raptor.api.model.RaptorConstants; import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; import org.opentripplanner.raptor.api.model.RaptorTripSchedule; @@ -28,7 +27,6 @@ public int time() { throw new UnsupportedOperationException(); } - @Nonnull @Override public RaptorTransferConstraint transferConstraint() { return RaptorTransferConstraint.REGULAR_TRANSFER; diff --git a/src/main/java/org/opentripplanner/raptor/spi/RaptorBoardOrAlightEvent.java b/src/main/java/org/opentripplanner/raptor/spi/RaptorBoardOrAlightEvent.java index 5913f950969..bf71011a330 100644 --- a/src/main/java/org/opentripplanner/raptor/spi/RaptorBoardOrAlightEvent.java +++ b/src/main/java/org/opentripplanner/raptor/spi/RaptorBoardOrAlightEvent.java @@ -1,7 +1,6 @@ package org.opentripplanner.raptor.spi; import java.util.function.Consumer; -import javax.annotation.Nonnull; import org.opentripplanner.raptor.api.model.RaptorConstants; import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; import org.opentripplanner.raptor.api.model.RaptorTripSchedule; @@ -65,7 +64,6 @@ default int boardStopIndex() { * constraints assisiated with the boarding the {@link RaptorTransferConstraint#isRegularTransfer()} * is {@code true}. */ - @Nonnull RaptorTransferConstraint transferConstraint(); /** diff --git a/src/main/java/org/opentripplanner/raptor/spi/RaptorTransitDataProvider.java b/src/main/java/org/opentripplanner/raptor/spi/RaptorTransitDataProvider.java index f0f103070a7..a442f0a3216 100644 --- a/src/main/java/org/opentripplanner/raptor/spi/RaptorTransitDataProvider.java +++ b/src/main/java/org/opentripplanner/raptor/spi/RaptorTransitDataProvider.java @@ -120,7 +120,6 @@ default void setup() {} * method is used by Raptor to translate from the stop index to a string which should be short and * identify the stop given the related pattern, for example the stop name would be great. */ - @Nonnull RaptorStopNameResolver stopNameResolver(); /** diff --git a/src/main/java/org/opentripplanner/routing/alertpatch/TransitAlert.java b/src/main/java/org/opentripplanner/routing/alertpatch/TransitAlert.java index d7a3c93abe5..4555573c8ca 100644 --- a/src/main/java/org/opentripplanner/routing/alertpatch/TransitAlert.java +++ b/src/main/java/org/opentripplanner/routing/alertpatch/TransitAlert.java @@ -8,7 +8,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; @@ -214,7 +213,7 @@ public boolean noServiceAt(Instant instant) { } @Override - public boolean sameAs(@Nonnull TransitAlert other) { + public boolean sameAs(TransitAlert other) { return ( getId().equals(other.getId()) && Objects.equals(headerText, other.headerText) && @@ -237,7 +236,6 @@ public boolean sameAs(@Nonnull TransitAlert other) { ); } - @Nonnull @Override public TransitBuilder copy() { return new TransitAlertBuilder(this); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java index 94905bb840a..32f5ccf533b 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/StatesToWalkStepsMapper.java @@ -7,7 +7,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.opentripplanner.framework.geometry.DirectionUtils; @@ -296,7 +295,6 @@ private void processState(State backState, State forwardState) { current.addEdge(edge); } - @Nonnull private static RelativeDirection relativeDirectionForTransitLink(StreetTransitEntranceLink link) { if (link.isExit()) { return EXIT_STATION; 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 0654a73700d..e72d8ee1427 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 @@ -255,20 +255,19 @@ private Collection fetchAccessEgresses(AccessEgre .valueOf(streetRequest.mode()); int stopCountLimit = accessRequest.preferences().street().accessEgress().maxStopCount(); - var nearbyStops = AccessEgressRouter.streetSearch( + var nearbyStops = AccessEgressRouter.findAccessEgresses( accessRequest, temporaryVerticesContainer, streetRequest, serverContext.dataOverlayContext(accessRequest), - type.isEgress(), + type, durationLimit, stopCountLimit ); + var accessEgresses = AccessEgressMapper.mapNearbyStops(nearbyStops, type); + accessEgresses = timeshiftRideHailing(streetRequest, type, accessEgresses); - List results = new ArrayList<>( - AccessEgressMapper.mapNearbyStops(nearbyStops, type.isEgress()) - ); - results = timeshiftRideHailing(streetRequest, type, results); + var results = new ArrayList<>(accessEgresses); // Special handling of flex accesses if (OTPFeature.FlexRouting.isOn() && streetRequest.mode() == StreetMode.FLEXIBLE) { @@ -279,10 +278,10 @@ private Collection fetchAccessEgresses(AccessEgre additionalSearchDays, serverContext.flexParameters(), serverContext.dataOverlayContext(accessRequest), - type.isEgress() + type ); - results.addAll(AccessEgressMapper.mapFlexAccessEgresses(flexAccessList, type.isEgress())); + results.addAll(AccessEgressMapper.mapFlexAccessEgresses(flexAccessList, type)); } return results; @@ -368,7 +367,8 @@ private TemporaryVerticesContainer createTemporaryVerticesContainer( ) { return new TemporaryVerticesContainer( serverContext.graph(), - request, + request.from(), + request.to(), request.journey().access().mode(), request.journey().egress().mode() ); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/AccessEgressRouter.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/AccessEgressRouter.java index f6a0526fa66..9b592fad3aa 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/AccessEgressRouter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/AccessEgressRouter.java @@ -2,14 +2,17 @@ import java.time.Duration; import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.Nullable; import org.opentripplanner.ext.dataoverlay.routing.DataOverlayContext; import org.opentripplanner.framework.application.OTPRequestTimeoutException; +import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.graph_builder.module.nearbystops.StreetNearbyStopFinder; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.request.StreetRequest; import org.opentripplanner.routing.graphfinder.NearbyStop; import org.opentripplanner.street.search.TemporaryVerticesContainer; -import org.opentripplanner.transit.service.TransitService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,34 +26,70 @@ public class AccessEgressRouter { private AccessEgressRouter() {} /** - * @param fromTarget whether to route from or towards the point provided in the routing request - * (access or egress) - * @return Transfer objects by access/egress stop + * Find accesses or egresses. */ - public static Collection streetSearch( + public static Collection findAccessEgresses( RouteRequest request, TemporaryVerticesContainer verticesContainer, StreetRequest streetRequest, - DataOverlayContext dataOverlayContext, - boolean fromTarget, + @Nullable DataOverlayContext dataOverlayContext, + AccessEgressType accessOrEgress, Duration durationLimit, int maxStopCount ) { OTPRequestTimeoutException.checkForTimeout(); - var nearbyStopFinder = new StreetNearbyStopFinder( - durationLimit, - maxStopCount, - dataOverlayContext - ); - Collection nearbyStopList = nearbyStopFinder.findNearbyStops( - fromTarget ? verticesContainer.getToVertices() : verticesContainer.getFromVertices(), - fromTarget, + + // Note: We calculate access/egresses in two parts. First we fetch the stops with zero distance. + // Then we do street search. This is because some stations might use the centroid for street + // routing, but should still give zero distance access/egresses to its child-stops. + var zeroDistanceAccessEgress = findAccessEgressWithZeroDistance( + verticesContainer, request, - streetRequest + streetRequest, + accessOrEgress ); - LOG.debug("Found {} {} stops", nearbyStopList.size(), fromTarget ? "egress" : "access"); + // When looking for street accesses/egresses we ignore the already found direct accesses/egresses + var ignoreVertices = zeroDistanceAccessEgress + .stream() + .map(nearbyStop -> nearbyStop.state.getVertex()) + .collect(Collectors.toSet()); + + var originVertices = accessOrEgress.isAccess() + ? verticesContainer.getFromVertices() + : verticesContainer.getToVertices(); + var streetAccessEgress = new StreetNearbyStopFinder( + durationLimit, + maxStopCount, + dataOverlayContext, + ignoreVertices + ) + .findNearbyStops(originVertices, request, streetRequest, accessOrEgress.isEgress()); + + var results = ListUtils.combine(zeroDistanceAccessEgress, streetAccessEgress); + LOG.debug("Found {} {} stops", results.size(), accessOrEgress); + return results; + } - return nearbyStopList; + /** + * Return a list of direct accesses/egresses that do not require any street search. This will + * return an empty list if the source/destination is not a stopId. + */ + private static List findAccessEgressWithZeroDistance( + TemporaryVerticesContainer verticesContainer, + RouteRequest routeRequest, + StreetRequest streetRequest, + AccessEgressType accessOrEgress + ) { + var transitStopVertices = accessOrEgress.isAccess() + ? verticesContainer.getFromStopVertices() + : verticesContainer.getToStopVertices(); + + return NearbyStop.nearbyStopsForTransitStopVerticesFiltered( + transitStopVertices, + accessOrEgress.isEgress(), + routeRequest, + streetRequest + ); } } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/DirectFlexRouter.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/DirectFlexRouter.java index 8e4cf1b222f..6d54973bff9 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/DirectFlexRouter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/DirectFlexRouter.java @@ -28,27 +28,28 @@ public static List route( try ( var temporaryVertices = new TemporaryVerticesContainer( serverContext.graph(), - request, + request.from(), + request.to(), request.journey().direct().mode(), request.journey().direct().mode() ) ) { // Prepare access/egress transfers - Collection accessStops = AccessEgressRouter.streetSearch( + Collection accessStops = AccessEgressRouter.findAccessEgresses( request, temporaryVertices, request.journey().direct(), serverContext.dataOverlayContext(request), - false, + AccessEgressType.ACCESS, serverContext.flexParameters().maxAccessWalkDuration(), 0 ); - Collection egressStops = AccessEgressRouter.streetSearch( + Collection egressStops = AccessEgressRouter.findAccessEgresses( request, temporaryVertices, request.journey().direct(), serverContext.dataOverlayContext(request), - true, + AccessEgressType.EGRESS, serverContext.flexParameters().maxEgressWalkDuration(), 0 ); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/DirectStreetRouter.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/DirectStreetRouter.java index 11463902dcd..609d6ec5355 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/DirectStreetRouter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/DirectStreetRouter.java @@ -30,7 +30,8 @@ public static List route(OtpServerRequestContext serverContext, Route try ( var temporaryVertices = new TemporaryVerticesContainer( serverContext.graph(), - directRequest, + directRequest.from(), + directRequest.to(), request.journey().direct().mode(), request.journey().direct().mode() ) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/FlexAccessEgressRouter.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/FlexAccessEgressRouter.java index 68dda38211a..f5aa9142fac 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/FlexAccessEgressRouter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/FlexAccessEgressRouter.java @@ -27,31 +27,31 @@ public static Collection routeAccessEgress( AdditionalSearchDays searchDays, FlexParameters config, DataOverlayContext dataOverlayContext, - boolean isEgress + AccessEgressType accessOrEgress ) { OTPRequestTimeoutException.checkForTimeout(); TransitService transitService = serverContext.transitService(); - Collection accessStops = !isEgress - ? AccessEgressRouter.streetSearch( + Collection accessStops = accessOrEgress.isAccess() + ? AccessEgressRouter.findAccessEgresses( request, verticesContainer, new StreetRequest(StreetMode.WALK), dataOverlayContext, - false, + AccessEgressType.ACCESS, serverContext.flexParameters().maxAccessWalkDuration(), 0 ) : List.of(); - Collection egressStops = isEgress - ? AccessEgressRouter.streetSearch( + Collection egressStops = accessOrEgress.isEgress() + ? AccessEgressRouter.findAccessEgresses( request, verticesContainer, new StreetRequest(StreetMode.WALK), dataOverlayContext, - true, + AccessEgressType.EGRESS, serverContext.flexParameters().maxEgressWalkDuration(), 0 ) @@ -69,6 +69,8 @@ public static Collection routeAccessEgress( egressStops ); - return isEgress ? flexRouter.createFlexEgresses() : flexRouter.createFlexAccesses(); + return accessOrEgress.isEgress() + ? flexRouter.createFlexEgresses() + : flexRouter.createFlexAccesses(); } } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/FlexAccessEgressAdapter.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/FlexAccessEgressAdapter.java index 04f51e76681..64650ab9e92 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/FlexAccessEgressAdapter.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/FlexAccessEgressAdapter.java @@ -4,6 +4,7 @@ import org.opentripplanner.framework.model.TimeAndCost; import org.opentripplanner.model.StopTime; import org.opentripplanner.raptor.api.model.RaptorConstants; +import org.opentripplanner.routing.algorithm.raptoradapter.router.street.AccessEgressType; /** * This class is used to adapt the FlexAccessEgress into a time-dependent multi-leg DefaultAccessEgress. @@ -12,10 +13,15 @@ public class FlexAccessEgressAdapter extends DefaultAccessEgress { private final FlexAccessEgress flexAccessEgress; - public FlexAccessEgressAdapter(FlexAccessEgress flexAccessEgress, boolean isEgress) { + public FlexAccessEgressAdapter( + FlexAccessEgress flexAccessEgress, + AccessEgressType accessOrEgress + ) { super( flexAccessEgress.stop().getIndex(), - isEgress ? flexAccessEgress.lastState().reverse() : flexAccessEgress.lastState() + accessOrEgress.isEgress() + ? flexAccessEgress.lastState().reverse() + : flexAccessEgress.lastState() ); this.flexAccessEgress = flexAccessEgress; } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/constrainedtransfer/ConstrainedTransferBoarding.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/constrainedtransfer/ConstrainedTransferBoarding.java index 63a0d50aa03..bf5de51e2c8 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/constrainedtransfer/ConstrainedTransferBoarding.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/constrainedtransfer/ConstrainedTransferBoarding.java @@ -1,7 +1,6 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.constrainedtransfer; import java.util.function.Consumer; -import javax.annotation.Nonnull; import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; import org.opentripplanner.raptor.api.model.RaptorTripSchedule; import org.opentripplanner.raptor.spi.RaptorBoardOrAlightEvent; @@ -20,7 +19,7 @@ public class ConstrainedTransferBoarding private final int earliestBoardTime; public ConstrainedTransferBoarding( - @Nonnull RaptorTransferConstraint constraint, + RaptorTransferConstraint constraint, int tripIndex, T trip, int stopPositionInPattern, @@ -41,7 +40,6 @@ public int tripIndex() { } @Override - @Nonnull public T trip() { return trip; } @@ -62,7 +60,6 @@ public int earliestBoardTime() { } @Override - @Nonnull public RaptorTransferConstraint transferConstraint() { return constraint; } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/PatternCostCalculator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/PatternCostCalculator.java index 734355f0ac5..d611c73b7ad 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/PatternCostCalculator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/PatternCostCalculator.java @@ -1,7 +1,6 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.cost; import java.util.BitSet; -import javax.annotation.Nonnull; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; import org.opentripplanner.raptor.spi.RaptorCostCalculator; @@ -13,9 +12,9 @@ class PatternCostCalculator implements RaptorCost private final RaptorCostLinearFunction unpreferredCost; PatternCostCalculator( - @Nonnull RaptorCostCalculator delegate, - @Nonnull BitSet unpreferredPatterns, - @Nonnull RaptorCostLinearFunction unpreferredCost + RaptorCostCalculator delegate, + BitSet unpreferredPatterns, + RaptorCostLinearFunction unpreferredCost ) { this.unpreferredPatterns = unpreferredPatterns; this.delegate = delegate; diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/WheelchairCostCalculator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/WheelchairCostCalculator.java index d0697f78692..d68d2230a5c 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/WheelchairCostCalculator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/WheelchairCostCalculator.java @@ -1,6 +1,5 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.cost; -import javax.annotation.Nonnull; import org.opentripplanner.raptor.api.model.RaptorAccessEgress; import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; import org.opentripplanner.raptor.spi.RaptorCostCalculator; @@ -14,8 +13,8 @@ public class WheelchairCostCalculator private final int[] wheelchairBoardingCost; public WheelchairCostCalculator( - @Nonnull RaptorCostCalculator delegate, - @Nonnull AccessibilityPreferences wheelchairAccessibility + RaptorCostCalculator delegate, + AccessibilityPreferences wheelchairAccessibility ) { this.delegate = delegate; this.wheelchairBoardingCost = createWheelchairCost(wheelchairAccessibility); diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/FrequencyBoardOrAlightEvent.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/FrequencyBoardOrAlightEvent.java index 927b1d966ed..b2cbb51fb00 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/FrequencyBoardOrAlightEvent.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/FrequencyBoardOrAlightEvent.java @@ -1,7 +1,6 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.frequency; import java.time.LocalDate; -import javax.annotation.Nonnull; import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; import org.opentripplanner.raptor.api.model.RaptorTripPattern; import org.opentripplanner.raptor.spi.RaptorBoardOrAlightEvent; @@ -90,7 +89,6 @@ public int earliestBoardTime() { } @Override - @Nonnull public RaptorTransferConstraint transferConstraint() { return RaptorTransferConstraint.REGULAR_TRANSFER; } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/AccessEgressMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/AccessEgressMapper.java index fd7619ac297..2205a2feb7b 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/AccessEgressMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/AccessEgressMapper.java @@ -5,6 +5,7 @@ import java.util.Objects; import java.util.stream.Collectors; import org.opentripplanner.ext.flex.FlexAccessEgress; +import org.opentripplanner.routing.algorithm.raptoradapter.router.street.AccessEgressType; import org.opentripplanner.routing.algorithm.raptoradapter.transit.DefaultAccessEgress; import org.opentripplanner.routing.algorithm.raptoradapter.transit.FlexAccessEgressAdapter; import org.opentripplanner.routing.algorithm.raptoradapter.transit.RoutingAccessEgress; @@ -15,33 +16,36 @@ public class AccessEgressMapper { public static List mapNearbyStops( Collection accessStops, - boolean isEgress + AccessEgressType accessOrEgress ) { return accessStops .stream() - .map(stopAtDistance -> mapNearbyStop(stopAtDistance, isEgress)) + .map(nearbyStop -> mapNearbyStop(nearbyStop, accessOrEgress)) .filter(Objects::nonNull) .collect(Collectors.toList()); } public static Collection mapFlexAccessEgresses( Collection flexAccessEgresses, - boolean isEgress + AccessEgressType accessOrEgress ) { return flexAccessEgresses .stream() - .map(flexAccessEgress -> new FlexAccessEgressAdapter(flexAccessEgress, isEgress)) + .map(flexAccessEgress -> new FlexAccessEgressAdapter(flexAccessEgress, accessOrEgress)) .collect(Collectors.toList()); } - private static RoutingAccessEgress mapNearbyStop(NearbyStop nearbyStop, boolean isEgress) { + private static RoutingAccessEgress mapNearbyStop( + NearbyStop nearbyStop, + AccessEgressType accessOrEgress + ) { if (!(nearbyStop.stop instanceof RegularStop)) { return null; } return new DefaultAccessEgress( nearbyStop.stop.getIndex(), - isEgress ? nearbyStop.state.reverse() : nearbyStop.state + accessOrEgress.isEgress() ? nearbyStop.state.reverse() : nearbyStop.state ); } } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java index d375b4c546c..13f8facef3d 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerMapper.java @@ -7,15 +7,12 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import javax.annotation.Nullable; import org.opentripplanner.framework.application.OTPFeature; -import org.opentripplanner.model.Timetable; import org.opentripplanner.routing.algorithm.raptoradapter.transit.Transfer; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitLayer; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitTuningParameters; @@ -26,7 +23,6 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.request.RaptorRequestTransferCache; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.StopTransferPriority; -import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; @@ -64,16 +60,6 @@ public static TransitLayer map( return new TransitLayerMapper(transitModel).map(tuningParameters); } - // TODO We could save time by either pre-sorting these, or by using a sorting algorithm that is - // optimized for sorting nearly-sorted lists. - static List getSortedTripTimes(Timetable timetable) { - return timetable - .getTripTimes() - .stream() - .sorted(Comparator.comparing(TripTimes::sortIndex)) - .collect(Collectors.toList()); - } - private TransitLayer map(TransitTuningParameters tuningParameters) { HashMap> tripPatternsByStopByDate; List> transferByStopIndex; diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TripPatternForDateMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TripPatternForDateMapper.java index cd00b9356dc..256a268e2c7 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TripPatternForDateMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TripPatternForDateMapper.java @@ -3,12 +3,9 @@ import gnu.trove.set.TIntSet; import java.time.LocalDate; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.stream.Collectors; import javax.annotation.Nullable; import org.opentripplanner.model.Timetable; @@ -33,8 +30,6 @@ public class TripPatternForDateMapper { private static final Logger LOG = LoggerFactory.getLogger(TripPatternForDateMapper.class); - private final ConcurrentMap> sortedTripTimesForTimetable = new ConcurrentHashMap<>(); - private final Map serviceCodesRunningForDate; /** @@ -69,19 +64,7 @@ public TripPatternForDate map(Timetable timetable, LocalDate serviceDate) { List times = new ArrayList<>(); - // The TripTimes are not sorted by departure time in the source timetable because - // OTP1 performs a simple/ linear search. Raptor results depend on trips being - // sorted. We reuse the same timetables many times on different days, so cache the - // sorted versions to avoid repeated compute-intensive sorting. Anecdotally this - // reduces mapping time by more than half, but it is still rather slow. NL Mapping - // takes 32 seconds sorting every timetable, 9 seconds with cached sorting, and 6 - // seconds with no timetable sorting at all. - List sortedTripTimes = sortedTripTimesForTimetable.computeIfAbsent( - timetable, - TransitLayerMapper::getSortedTripTimes - ); - - for (TripTimes tripTimes : sortedTripTimes) { + for (TripTimes tripTimes : timetable.getTripTimes()) { if (!serviceCodesRunning.contains(tripTimes.getServiceCode())) { continue; } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRequestTransferCache.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRequestTransferCache.java index 99ad83d3f4d..5e274c4120f 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRequestTransferCache.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRequestTransferCache.java @@ -6,7 +6,6 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.ExecutionException; -import javax.annotation.Nonnull; import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.routing.algorithm.raptoradapter.transit.RaptorTransferIndex; import org.opentripplanner.routing.algorithm.raptoradapter.transit.Transfer; @@ -57,8 +56,7 @@ public RaptorTransferIndex get(List> transfersByStopIndex, RouteR private CacheLoader cacheLoader() { return new CacheLoader<>() { @Override - @Nonnull - public RaptorTransferIndex load(@Nonnull CacheKey cacheKey) { + public RaptorTransferIndex load(CacheKey cacheKey) { LOG.info("Adding runtime request to cache: {}", cacheKey.options); return RaptorTransferIndex.create(cacheKey.transfersByStopIndex, cacheKey.request); } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java index c58e18385bf..0182598ca35 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitData.java @@ -4,7 +4,6 @@ import java.util.BitSet; import java.util.Iterator; import java.util.List; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.framework.time.ServiceDateUtils; @@ -200,7 +199,6 @@ public RaptorConstrainedTransfer findConstrainedTransfer( }; } - @Nonnull @Override public RaptorStopNameResolver stopNameResolver() { return (int stopIndex) -> { diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleAlightSearch.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleAlightSearch.java index 7be4f1eecf9..5ece76aedb5 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleAlightSearch.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleAlightSearch.java @@ -1,7 +1,6 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.request; import java.util.function.IntUnaryOperator; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.raptor.api.model.RaptorConstants; @@ -76,7 +75,6 @@ public int stopPositionInPattern() { } @Override - @Nonnull public RaptorTransferConstraint transferConstraint() { return RaptorTransferConstraint.REGULAR_TRANSFER; } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleBoardSearch.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleBoardSearch.java index 78bc2101775..db08dd2dada 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleBoardSearch.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleBoardSearch.java @@ -1,7 +1,6 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit.request; import java.util.function.IntUnaryOperator; -import javax.annotation.Nonnull; import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.raptor.api.model.RaptorConstants; import org.opentripplanner.raptor.api.model.RaptorTransferConstraint; @@ -74,7 +73,6 @@ public int stopPositionInPattern() { } @Override - @Nonnull public RaptorTransferConstraint transferConstraint() { return RaptorTransferConstraint.REGULAR_TRANSFER; } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TransferGenerator.java b/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TransferGenerator.java index f06e2142073..0e5e5ebd8e0 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TransferGenerator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/transferoptimization/services/TransferGenerator.java @@ -5,7 +5,6 @@ import java.util.Comparator; import java.util.Iterator; import java.util.List; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.model.transfer.ConstrainedTransfer; import org.opentripplanner.raptor.api.model.RaptorTransfer; @@ -229,12 +228,10 @@ private int calcRegularTransferEarliestBoardTime( return from.time() + transferDuration; } - @Nonnull private StopTime getFromStopTime(final TransitPathLeg leg) { return StopTime.stopTime(leg.fromStop(), leg.fromTime()); } - @Nonnull private TripStopTime findMinimumToStopTime(List> transfers) { return transfers .stream() diff --git a/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegs.java b/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegs.java index 6b51c0b1282..465ed230884 100644 --- a/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegs.java +++ b/src/main/java/org/opentripplanner/routing/alternativelegs/AlternativeLegs.java @@ -16,7 +16,6 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.model.Timetable; import org.opentripplanner.model.TripTimeOnDate; @@ -138,7 +137,6 @@ public static List getAlternativeLegs( * This has been copied and slightly modified from StopTimesHelper. * TODO: Adapt after new transit model is in place */ - @Nonnull private static Stream generateLegs( TransitService transitService, TripPatternBetweenStops tripPatternBetweenStops, @@ -224,7 +222,6 @@ private static Stream generateLegs( return res.stream(); } - @Nonnull private static ScheduledTransitLeg mapToLeg( ZoneId timeZone, TripPattern pattern, @@ -264,7 +261,6 @@ private static ScheduledTransitLeg mapToLeg( .build(); } - @Nonnull private static Stream withBoardingAlightingPositions( Collection origins, Collection destinations, diff --git a/src/main/java/org/opentripplanner/routing/api/request/RequestModes.java b/src/main/java/org/opentripplanner/routing/api/request/RequestModes.java index 72b06f62979..8afb3082e1b 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/RequestModes.java +++ b/src/main/java/org/opentripplanner/routing/api/request/RequestModes.java @@ -3,7 +3,6 @@ import static org.opentripplanner.routing.api.request.StreetMode.NOT_SET; import java.util.Objects; -import javax.annotation.Nonnull; import org.opentripplanner.framework.tostring.ToStringBuilder; public class RequestModes { @@ -18,16 +17,12 @@ public class RequestModes { StreetMode.WALK ); - @Nonnull public final StreetMode accessMode; - @Nonnull public final StreetMode egressMode; - @Nonnull public final StreetMode directMode; - @Nonnull public final StreetMode transferMode; private RequestModes( diff --git a/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java b/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java index 328ecb73e52..fe32a489710 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java +++ b/src/main/java/org/opentripplanner/routing/api/request/RouteRequest.java @@ -111,8 +111,9 @@ public RoutingPreferences preferences() { return preferences; } - public void withPreferences(Consumer body) { + public RouteRequest withPreferences(Consumer body) { this.preferences = preferences.copyOf().apply(body).build(); + return this; } /** diff --git a/src/main/java/org/opentripplanner/routing/api/request/preference/RoutingPreferences.java b/src/main/java/org/opentripplanner/routing/api/request/preference/RoutingPreferences.java index 58ca5b180bf..5e5475d12ac 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/preference/RoutingPreferences.java +++ b/src/main/java/org/opentripplanner/routing/api/request/preference/RoutingPreferences.java @@ -6,7 +6,6 @@ import java.io.Serializable; import java.util.Objects; import java.util.function.Consumer; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.street.search.TraverseMode; @@ -81,7 +80,6 @@ public StreetPreferences street() { /** * Preferences for how strict wheel-accessibility settings are */ - @Nonnull public WheelchairPreferences wheelchair() { return wheelchair; } @@ -108,7 +106,6 @@ public VehicleParkingPreferences parking(TraverseMode mode) { /** * Get rental preferences for the traverse mode. Note, only car, scooter and bike are supported. */ - @Nonnull public VehicleRentalPreferences rental(TraverseMode mode) { return switch (mode) { case BICYCLE -> bike.rental(); @@ -131,7 +128,6 @@ public VehicleRentalPreferences rental(StreetMode mode) { }; } - @Nonnull public ItineraryFilterPreferences itineraryFilter() { return itineraryFilter; } diff --git a/src/main/java/org/opentripplanner/routing/api/request/preference/StreetPreferences.java b/src/main/java/org/opentripplanner/routing/api/request/preference/StreetPreferences.java index 6ea6ee1fc3a..166c950a0e7 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/preference/StreetPreferences.java +++ b/src/main/java/org/opentripplanner/routing/api/request/preference/StreetPreferences.java @@ -9,7 +9,6 @@ import java.util.Map; import java.util.Objects; import java.util.function.Consumer; -import javax.annotation.Nonnull; import org.opentripplanner.framework.lang.DoubleUtils; import org.opentripplanner.framework.model.Units; import org.opentripplanner.framework.tostring.ToStringBuilder; @@ -100,7 +99,6 @@ public DurationForEnum maxDirectDuration() { * CAR). So the default timeout for a street search is set quite high. This is used to abort the * search if the max distance is not reached within the timeout. */ - @Nonnull public Duration routingTimeout() { return routingTimeout; } diff --git a/src/main/java/org/opentripplanner/routing/api/request/preference/filter/VehicleParkingFilter.java b/src/main/java/org/opentripplanner/routing/api/request/preference/filter/VehicleParkingFilter.java index c4f5cee422f..3fb6d6b0a62 100644 --- a/src/main/java/org/opentripplanner/routing/api/request/preference/filter/VehicleParkingFilter.java +++ b/src/main/java/org/opentripplanner/routing/api/request/preference/filter/VehicleParkingFilter.java @@ -4,7 +4,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import javax.annotation.Nonnull; import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.routing.vehicle_parking.VehicleParking; @@ -87,7 +86,6 @@ public int hashCode() { return Arrays.hashCode(not) + Arrays.hashCode(select); } - @Nonnull private static VehicleParkingSelect[] makeFilter(Collection select) { return select.stream().filter(f -> !f.isEmpty()).toArray(VehicleParkingSelect[]::new); } diff --git a/src/main/java/org/opentripplanner/routing/graph/Graph.java b/src/main/java/org/opentripplanner/routing/graph/Graph.java index fa3d12b92f7..1fbea77e759 100644 --- a/src/main/java/org/opentripplanner/routing/graph/Graph.java +++ b/src/main/java/org/opentripplanner/routing/graph/Graph.java @@ -12,7 +12,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.prefs.Preferences; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.locationtech.jts.geom.Geometry; import org.opentripplanner.ext.dataoverlay.configuration.DataOverlayParameterBindings; @@ -363,7 +362,6 @@ public void setDistanceBetweenElevationSamples(double distanceBetweenElevationSa CompactElevationProfile.setDistanceBetweenSamplesM(distanceBetweenElevationSamples); } - @Nonnull public VehicleParkingService getVehicleParkingService() { return vehicleParkingService; } diff --git a/src/main/java/org/opentripplanner/routing/graph/index/StreetIndex.java b/src/main/java/org/opentripplanner/routing/graph/index/StreetIndex.java index 93e333076b0..362c87bff7d 100644 --- a/src/main/java/org/opentripplanner/routing/graph/index/StreetIndex.java +++ b/src/main/java/org/opentripplanner/routing/graph/index/StreetIndex.java @@ -1,6 +1,7 @@ package org.opentripplanner.routing.graph.index; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -31,6 +32,7 @@ import org.opentripplanner.street.model.edge.TemporaryFreeEdge; import org.opentripplanner.street.model.edge.TemporaryPartialStreetEdge; import org.opentripplanner.street.model.edge.TemporaryPartialStreetEdgeBuilder; +import org.opentripplanner.street.model.vertex.StationCentroidVertex; import org.opentripplanner.street.model.vertex.StreetVertex; import org.opentripplanner.street.model.vertex.TemporaryStreetLocation; import org.opentripplanner.street.model.vertex.TransitStopVertex; @@ -61,6 +63,11 @@ public class StreetIndex { private final Map transitStopVertices; + /** + * This list contains transitStationVertices for the stations that are configured to route to centroid + */ + private final Map stationCentroidVertices; + private final EdgeSpatialIndex edgeSpatialIndex; private final HashGridSpatialIndex verticesTree; @@ -73,6 +80,7 @@ public StreetIndex(Graph graph, StopModel stopModel) { this.verticesTree = new HashGridSpatialIndex<>(); this.vertexLinker = new VertexLinker(graph, stopModel, edgeSpatialIndex); this.transitStopVertices = toImmutableMap(graph.getVerticesOfType(TransitStopVertex.class)); + this.stationCentroidVertices = createStationCentroidVertexMap(graph); postSetup(graph.getVertices()); } @@ -174,7 +182,8 @@ public Collection getEdgesForEnvelope(Envelope envelope) { * * @param endVertex: whether this is a start vertex (if it's false) or end vertex (if it's true) */ - public Set getVerticesForLocation( + @Nullable + public Set getStreetVerticesForLocation( GenericLocation location, StreetMode streetMode, boolean endVertex, @@ -200,16 +209,24 @@ public Set getVerticesForLocation( } else { // Check if Stop/StopCollection is found by FeedScopeId if (location.stopId != null) { - Set transitStopVertices = getStopVerticesById(location.stopId); - if (transitStopVertices != null && !transitStopVertices.isEmpty()) { - return transitStopVertices; + var streetVertices = getStreetVerticesById(location.stopId); + if (!streetVertices.isEmpty()) { + return streetVertices; } } } // Check if coordinate is provided and connect it to graph if (location.getCoordinate() != null) { - return Set.of(createVertexFromLocation(location, streetMode, endVertex, tempEdges)); + return Set.of( + createVertexFromCoordinate( + location.getCoordinate(), + location.label, + streetMode, + endVertex, + tempEdges + ) + ); } return null; @@ -227,28 +244,24 @@ public String toString() { } /** - * Finds the appropriate vertex for this location. + * Create the appropriate vertex for this coordinate. * * @param endVertex: whether this is a start vertex (if it's false) or end vertex (if it's true) */ - public Vertex getVertexForLocationForTest( - GenericLocation location, + public Vertex createVertexForCoordinateForTest( + Coordinate location, StreetMode streetMode, boolean endVertex, Set tempEdges ) { - // Check if coordinate is provided and connect it to graph - if (location.getCoordinate() == null) { - return null; - } - return createVertexFromLocation(location, streetMode, endVertex, tempEdges); + return createVertexFromCoordinate(location, null, streetMode, endVertex, tempEdges); } /** * @param id Id of Stop, Station, MultiModalStation or GroupOfStations * @return The associated TransitStopVertex or all underlying TransitStopVertices */ - private Set getStopVerticesById(FeedScopedId id) { + public Set getStopOrChildStopsVertices(FeedScopedId id) { return stopModel .findStopOrChildStops(id) .stream() @@ -258,6 +271,20 @@ private Set getStopVerticesById(FeedScopedId id) { .collect(Collectors.toSet()); } + /** + * Get the street vertices for an id. If the id corresponds to a regular stop we will return the + * coordinate for the stop. + * If the id corresponds to a station we will either return the coordinates of the child stops or + * the station centroid if the station is configured to route to centroid. + */ + public Set getStreetVerticesById(FeedScopedId id) { + var stationVertex = stationCentroidVertices.get(id); + if (stationVertex != null) { + return Set.of(stationVertex); + } + return Collections.unmodifiableSet(getStopOrChildStopsVertices(id)); + } + private static void createHalfLocationForTest( TemporaryStreetLocation base, I18NString name, @@ -326,32 +353,33 @@ private static LineString edgeGeometryOrStraightLine(Edge e) { return geometry; } - private Vertex createVertexFromLocation( - GenericLocation location, + private Vertex createVertexFromCoordinate( + Coordinate coordinate, + @Nullable String label, StreetMode streetMode, boolean endVertex, Set tempEdges ) { if (endVertex) { - LOG.debug("Finding end vertex for {}", location); + LOG.debug("Creating end vertex for {}", coordinate); } else { - LOG.debug("Finding start vertex for {}", location); + LOG.debug("Creating start vertex for {}", coordinate); } I18NString name; - if (location.label == null || location.label.isEmpty()) { + if (label == null || label.isEmpty()) { if (endVertex) { name = new LocalizedString("destination"); } else { name = new LocalizedString("origin"); } } else { - name = new NonLocalizedString(location.label); + name = new NonLocalizedString(label); } TemporaryStreetLocation temporaryStreetLocation = new TemporaryStreetLocation( UUID.randomUUID().toString(), - location.getCoordinate(), + coordinate, name, endVertex ); @@ -385,7 +413,7 @@ private Vertex createVertexFromLocation( temporaryStreetLocation.getIncoming().isEmpty() && temporaryStreetLocation.getOutgoing().isEmpty() ) { - LOG.warn("Couldn't link {}", location); + LOG.warn("Couldn't link {}", coordinate); } temporaryStreetLocation.setWheelchairAccessible(true); @@ -435,4 +463,14 @@ private static Map toImmutableMap( } return Map.copyOf(map); } + + private static Map createStationCentroidVertexMap( + Graph graph + ) { + return graph + .getVerticesOfType(StationCentroidVertex.class) + .stream() + .filter(vertex -> vertex.getStation().shouldRouteToCentroid()) + .collect(Collectors.toUnmodifiableMap(v -> v.getStation().getId(), v -> v)); + } } diff --git a/src/main/java/org/opentripplanner/routing/graphfinder/NearbyStop.java b/src/main/java/org/opentripplanner/routing/graphfinder/NearbyStop.java index ada17120c6b..a6581812ee4 100644 --- a/src/main/java/org/opentripplanner/routing/graphfinder/NearbyStop.java +++ b/src/main/java/org/opentripplanner/routing/graphfinder/NearbyStop.java @@ -1,11 +1,20 @@ package org.opentripplanner.routing.graphfinder; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; import org.opentripplanner.astar.model.GraphPath; +import org.opentripplanner.routing.api.request.RouteRequest; +import org.opentripplanner.routing.api.request.request.StreetRequest; import org.opentripplanner.street.model.edge.Edge; +import org.opentripplanner.street.model.vertex.TransitStopVertex; +import org.opentripplanner.street.model.vertex.Vertex; +import org.opentripplanner.street.search.request.StreetSearchRequestMapper; import org.opentripplanner.street.search.state.State; import org.opentripplanner.transit.model.site.StopLocation; @@ -43,6 +52,62 @@ public static NearbyStop nearbyStopForState(State state, StopLocation stop) { return new NearbyStop(stop, effectiveWalkDistance, edges, state); } + /** + * Create a NearbyStop with zero distance and no edges. + */ + public static NearbyStop ofZeroDistance(StopLocation stop, State state) { + return new NearbyStop(stop, 0d, Collections.emptyList(), state); + } + + /** + * Create zero distance NearbyStops given a list of TransitStopVertices + */ + public static List nearbyStopsForTransitStopVertices( + Set stopVertices, + boolean reverseDirection, + RouteRequest routeRequest, + StreetRequest streetRequest + ) { + if (stopVertices.isEmpty()) { + return List.of(); + } + + var streetSearchRequest = StreetSearchRequestMapper + .mapToTransferRequest(routeRequest) + .withArriveBy(reverseDirection) + .withMode(streetRequest.mode()) + .build(); + + return stopVertices + .stream() + .map(s -> ofZeroDistance(s.getStop(), new State(s, streetSearchRequest))) + .toList(); + } + + /** + * Given a list of Vertices, find the TransitStopVertices and create zero distance NearbyStops + * for them. + */ + public static List nearbyStopsForTransitStopVerticesFiltered( + Collection vertices, + boolean reverseDirection, + RouteRequest routeRequest, + StreetRequest streetRequest + ) { + var transitStops = vertices + .stream() + .filter(v -> v instanceof TransitStopVertex) + .map(v -> (TransitStopVertex) v) + .collect(Collectors.toSet()); + + return nearbyStopsForTransitStopVertices( + transitStops, + reverseDirection, + routeRequest, + streetRequest + ); + } + /** * Return {@code true} if this instance has a lower weight/cost than the given {@code other}. * If the state is not set, the distance is used for comparison instead. If the diff --git a/src/main/java/org/opentripplanner/routing/graphfinder/StreetGraphFinder.java b/src/main/java/org/opentripplanner/routing/graphfinder/StreetGraphFinder.java index 71f65209ddf..061e692fa3f 100644 --- a/src/main/java/org/opentripplanner/routing/graphfinder/StreetGraphFinder.java +++ b/src/main/java/org/opentripplanner/routing/graphfinder/StreetGraphFinder.java @@ -95,7 +95,8 @@ private void findClosestUsingStreets( try ( var temporaryVertices = new TemporaryVerticesContainer( graph, - rr, + rr.from(), + rr.to(), StreetMode.WALK, StreetMode.WALK ) diff --git a/src/main/java/org/opentripplanner/service/realtimevehicles/RealtimeVehicleService.java b/src/main/java/org/opentripplanner/service/realtimevehicles/RealtimeVehicleService.java index 949b196faeb..d697853880d 100644 --- a/src/main/java/org/opentripplanner/service/realtimevehicles/RealtimeVehicleService.java +++ b/src/main/java/org/opentripplanner/service/realtimevehicles/RealtimeVehicleService.java @@ -1,7 +1,6 @@ package org.opentripplanner.service.realtimevehicles; import java.util.List; -import javax.annotation.Nonnull; import org.opentripplanner.service.realtimevehicles.model.RealtimeVehicle; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.OccupancyStatus; @@ -12,11 +11,11 @@ public interface RealtimeVehicleService { * Get the realtime vehicles for a certain trip pattern. Service contains all the vehicles that * exist in input feeds but doesn't store any historical data. */ - List getRealtimeVehicles(@Nonnull TripPattern pattern); + List getRealtimeVehicles(TripPattern pattern); /** * Get the latest occupancy status for a certain trip. Service contains all the vehicles that * exist in input feeds but doesn't store any historical data. */ - OccupancyStatus getVehicleOccupancyStatus(@Nonnull Trip trip); + OccupancyStatus getVehicleOccupancyStatus(Trip trip); } diff --git a/src/main/java/org/opentripplanner/service/realtimevehicles/internal/DefaultRealtimeVehicleService.java b/src/main/java/org/opentripplanner/service/realtimevehicles/internal/DefaultRealtimeVehicleService.java index 0058cdd9e15..17a4d22e60c 100644 --- a/src/main/java/org/opentripplanner/service/realtimevehicles/internal/DefaultRealtimeVehicleService.java +++ b/src/main/java/org/opentripplanner/service/realtimevehicles/internal/DefaultRealtimeVehicleService.java @@ -9,7 +9,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import javax.annotation.Nonnull; import org.opentripplanner.service.realtimevehicles.RealtimeVehicleRepository; import org.opentripplanner.service.realtimevehicles.RealtimeVehicleService; import org.opentripplanner.service.realtimevehicles.model.RealtimeVehicle; @@ -58,7 +57,7 @@ public void clearRealtimeVehicles(TripPattern pattern) { * @see DefaultRealtimeVehicleService#setRealtimeVehicles(TripPattern, List) */ @Override - public List getRealtimeVehicles(@Nonnull TripPattern pattern) { + public List getRealtimeVehicles(TripPattern pattern) { if (pattern.getOriginalTripPattern() != null) { pattern = pattern.getOriginalTripPattern(); } @@ -66,9 +65,8 @@ public List getRealtimeVehicles(@Nonnull TripPattern pattern) { return vehicles.getOrDefault(pattern, List.of()); } - @Nonnull @Override - public OccupancyStatus getVehicleOccupancyStatus(@Nonnull Trip trip) { + public OccupancyStatus getVehicleOccupancyStatus(Trip trip) { return getOccupancyStatus(trip.getId(), transitService.getPatternForTrip(trip)); } diff --git a/src/main/java/org/opentripplanner/service/realtimevehicles/model/RealtimeVehicle.java b/src/main/java/org/opentripplanner/service/realtimevehicles/model/RealtimeVehicle.java index e06ae5ebaa7..fb0cbb28dbe 100644 --- a/src/main/java/org/opentripplanner/service/realtimevehicles/model/RealtimeVehicle.java +++ b/src/main/java/org/opentripplanner/service/realtimevehicles/model/RealtimeVehicle.java @@ -2,7 +2,6 @@ import java.time.Instant; import java.util.Optional; -import javax.annotation.Nonnull; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.site.StopLocation; @@ -91,7 +90,6 @@ public Optional stop() { return Optional.ofNullable(stop); } - @Nonnull public Trip trip() { return trip; } diff --git a/src/main/java/org/opentripplanner/service/vehiclerental/street/StreetVehicleRentalLink.java b/src/main/java/org/opentripplanner/service/vehiclerental/street/StreetVehicleRentalLink.java index 385b347d24a..99775b6112d 100644 --- a/src/main/java/org/opentripplanner/service/vehiclerental/street/StreetVehicleRentalLink.java +++ b/src/main/java/org/opentripplanner/service/vehiclerental/street/StreetVehicleRentalLink.java @@ -1,6 +1,5 @@ package org.opentripplanner.service.vehiclerental.street; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.vertex.StreetVertex; @@ -38,12 +37,7 @@ public static StreetVehicleRentalLink createStreetVehicleRentalLink( return connectToGraph(new StreetVehicleRentalLink(fromv, tov)); } - public String toString() { - return "StreetVehicleRentalLink(" + fromv + " -> " + tov + ")"; - } - @Override - @Nonnull public State[] traverse(State s0) { // Disallow traversing two StreetBikeRentalLinks in a row. // This prevents the router from using bike rental stations as shortcuts to get around diff --git a/src/main/java/org/opentripplanner/service/vehiclerental/street/VehicleRentalEdge.java b/src/main/java/org/opentripplanner/service/vehiclerental/street/VehicleRentalEdge.java index dc9fed8b9a3..0935909e0c7 100644 --- a/src/main/java/org/opentripplanner/service/vehiclerental/street/VehicleRentalEdge.java +++ b/src/main/java/org/opentripplanner/service/vehiclerental/street/VehicleRentalEdge.java @@ -2,7 +2,6 @@ import java.util.Collections; import java.util.Set; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; @@ -33,7 +32,6 @@ public static VehicleRentalEdge createVehicleRentalEdge( } @Override - @Nonnull public State[] traverse(State s0) { if (!s0.getRequest().mode().includesRenting()) { return State.empty(); diff --git a/src/main/java/org/opentripplanner/service/vehiclerental/street/VehicleRentalPlaceVertex.java b/src/main/java/org/opentripplanner/service/vehiclerental/street/VehicleRentalPlaceVertex.java index 5a04d56d60c..903747b0206 100644 --- a/src/main/java/org/opentripplanner/service/vehiclerental/street/VehicleRentalPlaceVertex.java +++ b/src/main/java/org/opentripplanner/service/vehiclerental/street/VehicleRentalPlaceVertex.java @@ -1,6 +1,5 @@ package org.opentripplanner.service.vehiclerental.street; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; import org.opentripplanner.street.model.vertex.Vertex; @@ -20,7 +19,6 @@ public VehicleRentalPlaceVertex(VehicleRentalPlace station) { this.station = station; } - @Nonnull @Override public I18NString getName() { return station.getName(); diff --git a/src/main/java/org/opentripplanner/service/worldenvelope/WorldEnvelopeRepository.java b/src/main/java/org/opentripplanner/service/worldenvelope/WorldEnvelopeRepository.java index 5aba781f79c..bdf9df4f8d3 100644 --- a/src/main/java/org/opentripplanner/service/worldenvelope/WorldEnvelopeRepository.java +++ b/src/main/java/org/opentripplanner/service/worldenvelope/WorldEnvelopeRepository.java @@ -2,7 +2,6 @@ import java.io.Serializable; import java.util.Optional; -import javax.annotation.Nonnull; import org.opentripplanner.service.worldenvelope.model.WorldEnvelope; /** @@ -27,5 +26,5 @@ public interface WorldEnvelopeRepository extends Serializable { Optional retrieveEnvelope(); - void saveEnvelope(@Nonnull WorldEnvelope envelope); + void saveEnvelope(WorldEnvelope envelope); } diff --git a/src/main/java/org/opentripplanner/service/worldenvelope/internal/DefaultWorldEnvelopeRepository.java b/src/main/java/org/opentripplanner/service/worldenvelope/internal/DefaultWorldEnvelopeRepository.java index 1fea3c4aeda..226122c803b 100644 --- a/src/main/java/org/opentripplanner/service/worldenvelope/internal/DefaultWorldEnvelopeRepository.java +++ b/src/main/java/org/opentripplanner/service/worldenvelope/internal/DefaultWorldEnvelopeRepository.java @@ -4,7 +4,6 @@ import jakarta.inject.Singleton; import java.io.Serializable; import java.util.Optional; -import javax.annotation.Nonnull; import org.opentripplanner.service.worldenvelope.WorldEnvelopeRepository; import org.opentripplanner.service.worldenvelope.model.WorldEnvelope; @@ -26,7 +25,7 @@ public Optional retrieveEnvelope() { } @Override - public void saveEnvelope(@Nonnull WorldEnvelope envelope) { + public void saveEnvelope(WorldEnvelope envelope) { this.envelope = envelope; } } diff --git a/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java b/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java index 3a577611617..7ec71e589c7 100644 --- a/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java +++ b/src/main/java/org/opentripplanner/standalone/api/OtpServerRequestContext.java @@ -129,6 +129,7 @@ default GraphFinder graphFinder() { VectorTileConfig vectorTileConfig(); + @Nullable default DataOverlayContext dataOverlayContext(RouteRequest request) { return OTPFeature.DataOverlay.isOnElseNull(() -> new DataOverlayContext( diff --git a/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java b/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java index a3c6f313799..ef2931b987e 100644 --- a/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java @@ -6,6 +6,7 @@ import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_1; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_2; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_5; +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_7; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.MissingNode; @@ -16,7 +17,6 @@ import java.util.List; import java.util.Set; import java.util.regex.Pattern; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.datastore.api.OtpDataStoreConfig; import org.opentripplanner.ext.dataoverlay.configuration.DataOverlayConfig; @@ -45,6 +45,7 @@ import org.opentripplanner.standalone.config.buildconfig.TransitFeeds; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; import org.opentripplanner.standalone.config.sandbox.DataOverlayConfigMapper; +import org.opentripplanner.transit.model.framework.FeedScopedId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -173,16 +174,12 @@ public class BuildConfig implements OtpDataStoreConfig { public final double maxElevationPropagationMeters; public final boolean readCachedElevations; public final boolean writeCachedElevations; - public final boolean includeEllipsoidToGeoidDifference; - public final boolean multiThreadElevationCalculations; - public final LocalDate transitServiceStart; - public final LocalDate transitServiceEnd; public final ZoneId transitModelTimeZone; - + private final List transitRouteToStationCentroid; public final URI stopConsolidation; /** @@ -438,6 +435,28 @@ to check in to a flight (2-3 hours for international flights) than to alight and .asDateOrRelativePeriod("P3Y", confZone); } + transitRouteToStationCentroid = + root + .of("transitRouteToStationCentroid") + .since(V2_7) + .summary("List stations that should route to centroid.") + .description( + """ +This field contains a list of station ids for which street legs will start/end at the station +centroid instead of the child stops. + +When searching from/to a station the default behaviour is to route from/to any of the stations child +stops. This can cause strange results for stations that have stops spread over a large area. + +For some stations you might instead wish to use the centroid of the station as the +origin/destination. In this case the centroid will be used both for direct street search and for +access/egress street search where the station is used as the start/end of the access/egress. But +transit that starts/ends at the station will work as usual without any additional street leg from/to +the centroid. +""" + ) + .asFeedScopedIds(List.of()); + writeCachedElevations = root .of("writeCachedElevations") @@ -655,13 +674,11 @@ public List demFiles() { return dem.demFiles(); } - @Nonnull @Override public List gtfsFiles() { return transitFeeds.gtfsFiles(); } - @Nonnull @Override public List netexFiles() { return transitFeeds.netexFiles(); @@ -718,6 +735,10 @@ public ServiceDateInterval getTransitServicePeriod() { return new ServiceDateInterval(transitServiceStart, transitServiceEnd); } + public List transitRouteToStationCentroid() { + return transitRouteToStationCentroid; + } + public int getSubwayAccessTimeSeconds() { // Convert access time in minutes to seconds return (int) (subwayAccessTime * 60.0); diff --git a/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransitFeeds.java b/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransitFeeds.java index 91485f09e05..c4a57213afa 100644 --- a/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransitFeeds.java +++ b/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransitFeeds.java @@ -2,14 +2,13 @@ import java.net.URI; import java.util.List; -import javax.annotation.Nonnull; import org.opentripplanner.graph_builder.model.DataSourceConfig; import org.opentripplanner.gtfs.graphbuilder.GtfsFeedParameters; import org.opentripplanner.netex.config.NetexFeedParameters; public record TransitFeeds( - @Nonnull List gtfsFeeds, - @Nonnull List netexFeeds + List gtfsFeeds, + List netexFeeds ) { public TransitFeeds(List gtfsFeeds, List netexFeeds) { this.netexFeeds = netexFeeds; diff --git a/src/main/java/org/opentripplanner/standalone/config/framework/json/ConfigType.java b/src/main/java/org/opentripplanner/standalone/config/framework/json/ConfigType.java index f62ec56dbfc..fec5cb05a51 100644 --- a/src/main/java/org/opentripplanner/standalone/config/framework/json/ConfigType.java +++ b/src/main/java/org/opentripplanner/standalone/config/framework/json/ConfigType.java @@ -5,7 +5,6 @@ import java.util.Arrays; import java.util.EnumSet; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.lang.StringUtils; import org.opentripplanner.framework.text.MarkdownFormatter; @@ -157,7 +156,7 @@ public String docName() { /** * Quote the given {@code value} is the JSON type is a {@code string}. */ - public String quote(@Nonnull Object value) { + public String quote(Object value) { return type == JsonType.string ? quoteText(value) : value.toString(); } diff --git a/src/main/java/org/opentripplanner/standalone/config/framework/json/NodeAdapter.java b/src/main/java/org/opentripplanner/standalone/config/framework/json/NodeAdapter.java index 51303ea4fc5..273c630d2bf 100644 --- a/src/main/java/org/opentripplanner/standalone/config/framework/json/NodeAdapter.java +++ b/src/main/java/org/opentripplanner/standalone/config/framework/json/NodeAdapter.java @@ -12,7 +12,6 @@ import java.util.Set; import java.util.function.Consumer; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.opentripplanner.framework.application.OtpAppException; /** @@ -63,21 +62,21 @@ public class NodeAdapter { private final int level; - private NodeAdapter(@Nonnull JsonNode node, String source, String contextPath, int level) { + private NodeAdapter(JsonNode node, String source, String contextPath, int level) { this.json = node; this.source = source; this.contextPath = contextPath; this.level = level; } - public NodeAdapter(@Nonnull JsonNode node, String source) { + public NodeAdapter(JsonNode node, String source) { this(node, source, null, 0); } /** * Constructor for nested configuration nodes. */ - private NodeAdapter(@Nonnull JsonNode node, @Nonnull NodeAdapter parent, String paramName) { + private NodeAdapter(JsonNode node, NodeAdapter parent, String paramName) { this(node, parent.source, parent.fullPath(paramName), parent.level + 1); parent.childrenByName.put(paramName, this); } diff --git a/src/main/java/org/opentripplanner/standalone/config/framework/json/NodeInfo.java b/src/main/java/org/opentripplanner/standalone/config/framework/json/NodeInfo.java index 06675475fd1..ce29639b924 100644 --- a/src/main/java/org/opentripplanner/standalone/config/framework/json/NodeInfo.java +++ b/src/main/java/org/opentripplanner/standalone/config/framework/json/NodeInfo.java @@ -5,7 +5,6 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.lang.StringUtils; import org.opentripplanner.framework.tostring.ValueObjectToStringBuilder; @@ -187,7 +186,7 @@ public String toString() { * */ @Override - public int compareTo(@Nonnull NodeInfo other) { + public int compareTo(NodeInfo other) { // Put type qualifier first if (isTypeQualifier()) { return -1; diff --git a/src/main/java/org/opentripplanner/standalone/config/framework/json/OtpVersion.java b/src/main/java/org/opentripplanner/standalone/config/framework/json/OtpVersion.java index 70b8e261ee4..ea9dbb4d6ba 100644 --- a/src/main/java/org/opentripplanner/standalone/config/framework/json/OtpVersion.java +++ b/src/main/java/org/opentripplanner/standalone/config/framework/json/OtpVersion.java @@ -11,7 +11,8 @@ public enum OtpVersion { V2_3("2.3"), V2_4("2.4"), V2_5("2.5"), - V2_6("2.6"); + V2_6("2.6"), + V2_7("2.7"); private final String text; diff --git a/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/VehicleParkingUpdaterConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/VehicleParkingUpdaterConfig.java index 88b47aac4eb..cbfae0c061b 100644 --- a/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/VehicleParkingUpdaterConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/VehicleParkingUpdaterConfig.java @@ -11,7 +11,6 @@ import org.opentripplanner.ext.vehicleparking.bikeep.BikeepUpdaterParameters; import org.opentripplanner.ext.vehicleparking.bikely.BikelyUpdaterParameters; import org.opentripplanner.ext.vehicleparking.hslpark.HslParkUpdaterParameters; -import org.opentripplanner.ext.vehicleparking.noi.NoiUpdaterParameters; import org.opentripplanner.ext.vehicleparking.parkapi.ParkAPIUpdaterParameters; import org.opentripplanner.ext.vehicleparking.sirifm.SiriFmUpdaterParameters; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; @@ -79,17 +78,6 @@ public static VehicleParkingUpdaterParameters create(String updaterRef, NodeAdap .asDuration(Duration.ofMinutes(1)), HttpHeadersConfig.headers(c, V2_3) ); - case NOI_OPEN_DATA_HUB -> new NoiUpdaterParameters( - updaterRef, - c.of("url").since(V2_6).summary("URL of the locations endpoint.").asUri(), - feedId, - c - .of("frequency") - .since(V2_6) - .summary("How often to update the source.") - .asDuration(Duration.ofMinutes(1)), - HttpHeadersConfig.headers(c, V2_6) - ); case BIKEEP -> new BikeepUpdaterParameters( updaterRef, c.of("url").since(V2_6).summary("URL of the locations endpoint.").asUri(), diff --git a/src/main/java/org/opentripplanner/street/model/edge/AreaEdgeList.java b/src/main/java/org/opentripplanner/street/model/edge/AreaEdgeList.java index 09eb347ac5d..1f8d286995b 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/AreaEdgeList.java +++ b/src/main/java/org/opentripplanner/street/model/edge/AreaEdgeList.java @@ -7,7 +7,6 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Polygon; import org.opentripplanner.street.model.vertex.IntersectionVertex; @@ -60,7 +59,6 @@ public Geometry getGeometry() { /** * Returns the list of visibility vertices. */ - @Nonnull public Set visibilityVertices() { return visibilityVertices; } @@ -68,7 +66,7 @@ public Set visibilityVertices() { /** * Add a visibility vertex to this edge. */ - public void addVisibilityVertex(@Nonnull IntersectionVertex toBeAdded) { + public void addVisibilityVertex(IntersectionVertex toBeAdded) { Objects.requireNonNull(toBeAdded); synchronized (this) { if (visibilityVertices == EMPTY_SET) { diff --git a/src/main/java/org/opentripplanner/street/model/edge/Edge.java b/src/main/java/org/opentripplanner/street/model/edge/Edge.java index 00130265cf4..31bff5b1e15 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/Edge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/Edge.java @@ -5,6 +5,8 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; +import java.util.function.Consumer; +import javax.annotation.Nullable; import org.locationtech.jts.geom.LineString; import org.opentripplanner.astar.spi.AStarEdge; import org.opentripplanner.framework.i18n.I18NString; @@ -99,8 +101,28 @@ public int hashCode() { return Objects.hash(fromv, tov); } + @Override public String toString() { - return String.format("%s (%s -> %s)", getClass().getName(), fromv, tov); + return buildToString(null, b -> {}); + } + + /** + * Use this to construct a {@code toString()} in derived classes. The result will be: + * {@code "%simple-class-name%(%name%, %fromv% -> %tov%" + body + ")"}. Note! There is no space + * between {@code tov} and {@code body}. Both name and body are optional. + * + * @param name Will add name to the string if not null. + * @param body Use this callback to add local content. + */ + protected String buildToString(@Nullable String name, Consumer body) { + var buf = new StringBuilder(); + buf.append(getClass().getSimpleName()).append("("); + if (name != null) { + buf.append(name).append(", "); + } + buf.append(fromv).append(" -> ").append(tov); + body.accept(buf); + return buf.append(')').toString(); } /** diff --git a/src/main/java/org/opentripplanner/street/model/edge/ElevatorAlightEdge.java b/src/main/java/org/opentripplanner/street/model/edge/ElevatorAlightEdge.java index 26b2fc622ba..ca3b0b4b0f7 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/ElevatorAlightEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/ElevatorAlightEdge.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.edge; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.LineString; import org.opentripplanner.framework.geometry.GeometryUtils; @@ -56,12 +55,7 @@ public static ElevatorAlightEdge createElevatorAlightEdge( return connectToGraph(new ElevatorAlightEdge(from, to, level)); } - public String toString() { - return "ElevatorAlightEdge(" + fromv + " -> " + tov + ")"; - } - @Override - @Nonnull public State[] traverse(State s0) { StateEditor s1 = createEditorForDrivingOrWalking(s0, this); s1.incrementWeight(1); diff --git a/src/main/java/org/opentripplanner/street/model/edge/ElevatorBoardEdge.java b/src/main/java/org/opentripplanner/street/model/edge/ElevatorBoardEdge.java index 624867fa4eb..81856476f2e 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/ElevatorBoardEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/ElevatorBoardEdge.java @@ -1,7 +1,6 @@ package org.opentripplanner.street.model.edge; import java.util.List; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.LineString; import org.opentripplanner.framework.geometry.GeometryUtils; @@ -42,12 +41,6 @@ public static ElevatorBoardEdge createElevatorBoardEdge( } @Override - public String toString() { - return ToStringBuilder.of(this.getClass()).addObj("from", fromv).addObj("to", tov).toString(); - } - - @Override - @Nonnull public State[] traverse(State s0) { StateEditor s1 = createEditorForDrivingOrWalking(s0, this); if (s1 == null) { diff --git a/src/main/java/org/opentripplanner/street/model/edge/ElevatorHopEdge.java b/src/main/java/org/opentripplanner/street/model/edge/ElevatorHopEdge.java index 0029b31f117..4acecd05a64 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/ElevatorHopEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/ElevatorHopEdge.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.edge; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.routing.api.request.preference.RoutingPreferences; @@ -101,12 +100,6 @@ public StreetTraversalPermission getPermission() { } @Override - public String toString() { - return ToStringBuilder.of(this.getClass()).addObj("from", fromv).addObj("to", tov).toString(); - } - - @Override - @Nonnull public State[] traverse(State s0) { RoutingPreferences preferences = s0.getPreferences(); diff --git a/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java b/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java index 11a31aeb86d..20fae657c78 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/EscalatorEdge.java @@ -1,6 +1,7 @@ package org.opentripplanner.street.model.edge; -import javax.annotation.Nonnull; +import org.locationtech.jts.geom.LineString; +import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.LocalizedString; import org.opentripplanner.street.model.vertex.Vertex; @@ -22,7 +23,6 @@ private EscalatorEdge(Vertex v1, Vertex v2, double length) { this.length = length; } - @Nonnull @Override public State[] traverse(State s0) { // Only allow traversal by walking @@ -36,6 +36,11 @@ public State[] traverse(State s0) { } else return State.empty(); } + @Override + public LineString getGeometry() { + return GeometryUtils.makeLineString(fromv.getCoordinate(), tov.getCoordinate()); + } + @Override public double getDistanceMeters() { return length; diff --git a/src/main/java/org/opentripplanner/street/model/edge/FreeEdge.java b/src/main/java/org/opentripplanner/street/model/edge/FreeEdge.java index 9fbc69af126..3b1631211ff 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/FreeEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/FreeEdge.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.edge; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.LineString; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.street.model.vertex.Vertex; @@ -23,12 +22,7 @@ public static FreeEdge createFreeEdge(Vertex from, Vertex to) { return connectToGraph(new FreeEdge(from, to)); } - public String toString() { - return "FreeEdge(" + fromv + " -> " + tov + ")"; - } - @Override - @Nonnull public State[] traverse(State s0) { StateEditor s1 = s0.edit(this); s1.incrementWeight(1); diff --git a/src/main/java/org/opentripplanner/street/model/edge/PathwayEdge.java b/src/main/java/org/opentripplanner/street/model/edge/PathwayEdge.java index 587b25a6704..703658fdda4 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/PathwayEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/PathwayEdge.java @@ -2,7 +2,6 @@ import java.util.Objects; import java.util.Optional; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.LineString; @@ -103,7 +102,6 @@ public static PathwayEdge createPathwayEdge( } @Override - @Nonnull public State[] traverse(State s0) { StateEditor s1 = createEditorForWalking(s0, this); if (s1 == null) { diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java index ca33396b0a3..92244fe2af2 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java @@ -9,7 +9,6 @@ import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.impl.PackedCoordinateSequence; import org.opentripplanner.framework.geometry.CompactLineStringUtils; @@ -308,20 +307,17 @@ public double getEffectiveWalkSafetyDistance() { } public String toString() { - return ( - "StreetEdge(" + - name + - ", " + - fromv + - " -> " + - tov + - " length=" + - this.getDistanceMeters() + - " carSpeed=" + - this.getCarSpeed() + - " permission=" + - this.getPermission() + - ")" + var nameString = name != null ? name.toString() : null; + return buildToString( + nameString, + b -> + b + .append(", length=") + .append(this.getDistanceMeters()) + .append(", carSpeed=") + .append(this.getCarSpeed()) + .append(", permission=") + .append(this.getPermission()) ); } @@ -330,7 +326,6 @@ public boolean isRoundabout() { } @Override - @Nonnull public State[] traverse(State s0) { final StateEditor editor; @@ -858,7 +853,6 @@ public void removeTurnRestrictionsTo(Edge other) { * This method is thread-safe, even if {@link StreetEdge#addTurnRestriction} or * {@link StreetEdge#removeTurnRestriction} is called concurrently. */ - @Nonnull public List getTurnRestrictions() { // this can be safely returned as it's unmodifiable return turnRestrictions; @@ -992,7 +986,6 @@ static int defaultMillimeterLength(LineString geometry) { * When we then leave the no drop off zone on foot we generate a state for each network that the * zone applies to where we pick up a vehicle with a specific network. */ - @Nonnull private State[] splitStatesAfterHavingExitedNoDropOffZoneWhenReverseSearching(State s0) { var networks = Stream.concat( // null is a special rental network that speculatively assumes that you can take any vehicle @@ -1194,7 +1187,6 @@ private StateEditor doTraverse(State s0, TraverseMode traverseMode, boolean walk return s1; } - @Nonnull private TraversalCosts otherTraversalCosts( RoutingPreferences preferences, TraverseMode traverseMode, @@ -1213,7 +1205,6 @@ private TraversalCosts otherTraversalCosts( return new TraversalCosts(time, weight); } - @Nonnull private TraversalCosts bicycleOrScooterTraversalCost( RoutingPreferences pref, TraverseMode mode, @@ -1258,7 +1249,6 @@ private TraversalCosts bicycleOrScooterTraversalCost( return new TraversalCosts(time, weight); } - @Nonnull private TraversalCosts walkingTraversalCosts( RoutingPreferences preferences, TraverseMode traverseMode, diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetStationCentroidLink.java b/src/main/java/org/opentripplanner/street/model/edge/StreetStationCentroidLink.java new file mode 100644 index 00000000000..6b336907db5 --- /dev/null +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetStationCentroidLink.java @@ -0,0 +1,32 @@ +package org.opentripplanner.street.model.edge; + +import org.opentripplanner.street.model.vertex.StationCentroidVertex; +import org.opentripplanner.street.model.vertex.StreetVertex; + +/** + * This represents the connection between a street vertex and a transit station centroid vertex + */ +public class StreetStationCentroidLink extends FreeEdge { + + private StreetStationCentroidLink(StreetVertex fromv, StationCentroidVertex tov) { + super(fromv, tov); + } + + private StreetStationCentroidLink(StationCentroidVertex fromv, StreetVertex tov) { + super(fromv, tov); + } + + public static StreetStationCentroidLink createStreetStationLink( + StreetVertex fromv, + StationCentroidVertex tov + ) { + return connectToGraph(new StreetStationCentroidLink(fromv, tov)); + } + + public static StreetStationCentroidLink createStreetStationLink( + StationCentroidVertex fromv, + StreetVertex tov + ) { + return connectToGraph(new StreetStationCentroidLink(fromv, tov)); + } +} diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetTransitEntityLink.java b/src/main/java/org/opentripplanner/street/model/edge/StreetTransitEntityLink.java index dabd70bf28d..92897232700 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetTransitEntityLink.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetTransitEntityLink.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.edge; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.LineString; import org.opentripplanner.framework.geometry.GeometryUtils; @@ -46,12 +45,7 @@ protected StreetTransitEntityLink( this.wheelchairAccessibility = wheelchairAccessibility; } - public String toString() { - return ToStringBuilder.of(this.getClass()).addObj("from", fromv).addObj("to", tov).toString(); - } - @Override - @Nonnull public State[] traverse(State s0) { // Forbid taking shortcuts composed of two street-transit links associated with the same stop in a row. Also // avoids spurious leg transitions. As noted in https://github.com/opentripplanner/OpenTripPlanner/issues/2815, @@ -135,7 +129,6 @@ else if ( }; } - @Nonnull private State[] buildState(State s0, StateEditor s1, RoutingPreferences pref) { if (s0.isRentingVehicleFromStation() && s0.mayKeepRentedVehicleAtDestination()) { var rentalPreferences = s0.getRequest().preferences().rental(s0.getRequest().mode()); diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetTransitEntranceLink.java b/src/main/java/org/opentripplanner/street/model/edge/StreetTransitEntranceLink.java index 07b918159a7..7145f6183e4 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetTransitEntranceLink.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetTransitEntranceLink.java @@ -43,10 +43,6 @@ public boolean isExit() { return !isEntrance; } - public String toString() { - return "StreetTransitEntranceLink(" + fromv + " -> " + tov + ")"; - } - protected int getStreetToStopTime() { return 0; } diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetTransitStopLink.java b/src/main/java/org/opentripplanner/street/model/edge/StreetTransitStopLink.java index 41605be4ccc..a7ce1c0e2df 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetTransitStopLink.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetTransitStopLink.java @@ -36,8 +36,4 @@ protected int getStreetToStopTime() { ? 0 : getTransitEntityVertex().getStreetToStopTime(); } - - public String toString() { - return "StreetTransitStopLink(" + fromv + " -> " + tov + ")"; - } } diff --git a/src/main/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLink.java b/src/main/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLink.java index 2ad9d0f39c4..e8fbae31b5f 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLink.java +++ b/src/main/java/org/opentripplanner/street/model/edge/StreetVehicleParkingLink.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.edge; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.LineString; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.i18n.I18NString; @@ -45,12 +44,6 @@ public static StreetVehicleParkingLink createStreetVehicleParkingLink( } @Override - public String toString() { - return ToStringBuilder.of(this.getClass()).addObj("fromv", fromv).addObj("tov", tov).toString(); - } - - @Override - @Nonnull public State[] traverse(State s0) { // Disallow traversing two StreetBikeParkLinks in a row. // Prevents router using bike rental stations as shortcuts to get around diff --git a/src/main/java/org/opentripplanner/street/model/edge/TemporaryFreeEdge.java b/src/main/java/org/opentripplanner/street/model/edge/TemporaryFreeEdge.java index e93c6523d89..7b114cfeade 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/TemporaryFreeEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/TemporaryFreeEdge.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.edge; -import javax.annotation.Nonnull; import org.opentripplanner.street.model.vertex.TemporaryVertex; import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.search.state.State; @@ -31,12 +30,6 @@ public static TemporaryFreeEdge createTemporaryFreeEdge(Vertex from, TemporaryVe } @Override - public String toString() { - return "Temporary" + super.toString(); - } - - @Override - @Nonnull public State[] traverse(State s0) { StateEditor s1 = s0.edit(this); s1.incrementWeight(1); diff --git a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java index 8c797f1f492..31792614cde 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/TemporaryPartialStreetEdge.java @@ -55,20 +55,10 @@ public StreetEdge getParentEdge() { @Override public String toString() { - return ( - "TemporaryPartialStreetEdge(" + - this.getDefaultName() + - ", " + - this.getFromVertex() + - " -> " + - this.getToVertex() + - " length=" + - this.getDistanceMeters() + - " carSpeed=" + - this.getCarSpeed() + - " parentEdge=" + - parentEdge + - ")" + return buildToString( + this.getDefaultName(), + b -> + b.append(", length=").append(this.getCarSpeed()).append(", parentEdge=").append(parentEdge) ); } diff --git a/src/main/java/org/opentripplanner/street/model/edge/VehicleParkingEdge.java b/src/main/java/org/opentripplanner/street/model/edge/VehicleParkingEdge.java index d32f13d6ea4..95d9ea5507d 100644 --- a/src/main/java/org/opentripplanner/street/model/edge/VehicleParkingEdge.java +++ b/src/main/java/org/opentripplanner/street/model/edge/VehicleParkingEdge.java @@ -1,7 +1,6 @@ package org.opentripplanner.street.model.edge; import java.time.Duration; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.model.Cost; import org.opentripplanner.routing.api.request.StreetMode; @@ -62,12 +61,7 @@ public boolean equals(Object o) { return false; } - public String toString() { - return "VehicleParkingEdge(" + fromv + " -> " + tov + ")"; - } - @Override - @Nonnull public State[] traverse(State s0) { if (!s0.getRequest().mode().includesParking()) { return State.empty(); diff --git a/src/main/java/org/opentripplanner/street/model/vertex/ElevatorOffboardVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/ElevatorOffboardVertex.java index 7fd66d8a759..89148bc6ace 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/ElevatorOffboardVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/ElevatorOffboardVertex.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.vertex; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; public class ElevatorOffboardVertex extends StreetVertex { @@ -20,7 +19,6 @@ public VertexLabel getLabel() { return VertexLabel.string(LABEL_TEMPLATE.formatted(label, level)); } - @Nonnull @Override public I18NString getName() { return I18NString.of(label); diff --git a/src/main/java/org/opentripplanner/street/model/vertex/ElevatorOnboardVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/ElevatorOnboardVertex.java index 5999b293ce7..5f6f19e2dab 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/ElevatorOnboardVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/ElevatorOnboardVertex.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.vertex; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.i18n.I18NString; @@ -21,7 +20,6 @@ public VertexLabel getLabel() { return VertexLabel.string(LABEL_TEMPLATE.formatted(label, level)); } - @Nonnull @Override public I18NString getName() { return I18NString.of(label); diff --git a/src/main/java/org/opentripplanner/street/model/vertex/LabelledIntersectionVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/LabelledIntersectionVertex.java index c32ac19fd89..ecb82b43008 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/LabelledIntersectionVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/LabelledIntersectionVertex.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.vertex; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; /** @@ -12,7 +11,7 @@ public class LabelledIntersectionVertex extends IntersectionVertex { private final String label; public LabelledIntersectionVertex( - @Nonnull String label, + String label, double x, double y, boolean hasHighwayTrafficLight, @@ -27,7 +26,6 @@ public VertexLabel getLabel() { return VertexLabel.string(label); } - @Nonnull @Override public I18NString getName() { return I18NString.of(label); diff --git a/src/main/java/org/opentripplanner/street/model/vertex/OsmBoardingLocationVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/OsmBoardingLocationVertex.java index 606c27ef4b2..8ba8b25c2b2 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/OsmBoardingLocationVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/OsmBoardingLocationVertex.java @@ -3,7 +3,6 @@ import java.util.Collection; import java.util.Objects; import java.util.Set; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.tostring.ToStringBuilder; @@ -44,7 +43,6 @@ public boolean isConnectedToStreetNetwork() { return (getOutgoing().size() + getIncoming().size()) > 0; } - @Nonnull @Override public I18NString getName() { return name; diff --git a/src/main/java/org/opentripplanner/street/model/vertex/OsmVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/OsmVertex.java index e4784e6edb9..39d195f695e 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/OsmVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/OsmVertex.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.vertex; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; /** @@ -29,7 +28,6 @@ public OsmVertex( this.nodeId = nodeId; } - @Nonnull @Override public I18NString getName() { return NO_NAME; diff --git a/src/main/java/org/opentripplanner/street/model/vertex/SplitterVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/SplitterVertex.java index 0362f6e1766..c1fc60983e4 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/SplitterVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/SplitterVertex.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.vertex; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; /** @@ -31,7 +30,6 @@ public boolean inferredFreeFlowing() { return true; } - @Nonnull @Override public I18NString getName() { return name; diff --git a/src/main/java/org/opentripplanner/street/model/vertex/StationCentroidVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/StationCentroidVertex.java new file mode 100644 index 00000000000..f6f3b00191d --- /dev/null +++ b/src/main/java/org/opentripplanner/street/model/vertex/StationCentroidVertex.java @@ -0,0 +1,33 @@ +package org.opentripplanner.street.model.vertex; + +import javax.annotation.Nonnull; +import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.transit.model.site.Station; + +/** + * A vertex representing a station centroid. This can be used as a source/destination for routing. + */ +public class StationCentroidVertex extends Vertex { + + private final Station station; + + public StationCentroidVertex(Station station) { + super(station.getLon(), station.getLat()); + this.station = station; + } + + public Station getStation() { + return this.station; + } + + @Nonnull + @Override + public I18NString getName() { + return station.getName(); + } + + @Override + public VertexLabel getLabel() { + return VertexLabel.feedScopedId(station.getId()); + } +} diff --git a/src/main/java/org/opentripplanner/street/model/vertex/StationElementVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/StationElementVertex.java index 0873ca64fc5..63595803300 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/StationElementVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/StationElementVertex.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.vertex; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.site.StationElement; @@ -22,12 +21,14 @@ public final VertexLabel getLabel() { } /** Get the corresponding StationElement */ - @Nonnull public abstract StationElement getStationElement(); - @Nonnull @Override public I18NString getName() { return name; } + + public FeedScopedId getId() { + return id; + } } diff --git a/src/main/java/org/opentripplanner/street/model/vertex/StreetLocation.java b/src/main/java/org/opentripplanner/street/model/vertex/StreetLocation.java index 31b25ec7b7d..44ca1f4db32 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/StreetLocation.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/StreetLocation.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.vertex; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.Coordinate; import org.opentripplanner.framework.i18n.I18NString; @@ -42,7 +41,6 @@ public I18NString getIntersectionName() { return getName(); } - @Nonnull @Override public I18NString getName() { return name; diff --git a/src/main/java/org/opentripplanner/street/model/vertex/StreetVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/StreetVertex.java index 64438667af8..8b58466a33c 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/StreetVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/StreetVertex.java @@ -8,7 +8,6 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.LocalizedString; import org.opentripplanner.street.model.edge.Edge; @@ -84,7 +83,6 @@ public boolean isEligibleForCarPickupDropoff() { /** * Returns the list of area stops that this vertex is inside of. */ - @Nonnull public Set areaStops() { return areaStops; } @@ -92,7 +90,7 @@ public Set areaStops() { /** * Add a collection of area stops to this vertex. */ - public void addAreaStops(@Nonnull Collection toBeAdded) { + public void addAreaStops(Collection toBeAdded) { Objects.requireNonNull(toBeAdded); synchronized (this) { if (areaStops == EMPTY_SET) { diff --git a/src/main/java/org/opentripplanner/street/model/vertex/TransitBoardingAreaVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/TransitBoardingAreaVertex.java index 3221934c2f6..5654185440a 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/TransitBoardingAreaVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/TransitBoardingAreaVertex.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.vertex; -import javax.annotation.Nonnull; import org.opentripplanner.transit.model.basic.Accessibility; import org.opentripplanner.transit.model.site.BoardingArea; import org.opentripplanner.transit.model.site.StationElement; @@ -30,7 +29,6 @@ public boolean isWheelchairAccessible() { return wheelchairAccessible; } - @Nonnull @Override public StationElement getStationElement() { return this.boardingArea; diff --git a/src/main/java/org/opentripplanner/street/model/vertex/TransitEntranceVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/TransitEntranceVertex.java index 54de400dedb..81b89ad3dfb 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/TransitEntranceVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/TransitEntranceVertex.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.vertex; -import javax.annotation.Nonnull; import org.opentripplanner.transit.model.basic.Accessibility; import org.opentripplanner.transit.model.site.Entrance; import org.opentripplanner.transit.model.site.StationElement; @@ -33,7 +32,6 @@ public Entrance getEntrance() { return this.entrance; } - @Nonnull @Override public StationElement getStationElement() { return this.entrance; diff --git a/src/main/java/org/opentripplanner/street/model/vertex/TransitPathwayNodeVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/TransitPathwayNodeVertex.java index b8cec0ec37c..645ec96b5ea 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/TransitPathwayNodeVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/TransitPathwayNodeVertex.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.vertex; -import javax.annotation.Nonnull; import org.opentripplanner.transit.model.basic.Accessibility; import org.opentripplanner.transit.model.site.PathwayNode; import org.opentripplanner.transit.model.site.StationElement; @@ -33,7 +32,6 @@ public PathwayNode getNode() { return this.node; } - @Nonnull @Override public StationElement getStationElement() { return this.node; diff --git a/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java index a9d7d221d44..82c977902b0 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/TransitStopVertex.java @@ -2,7 +2,6 @@ import java.util.HashSet; import java.util.Set; -import javax.annotation.Nonnull; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.edge.PathwayEdge; import org.opentripplanner.transit.model.basic.Accessibility; @@ -84,7 +83,6 @@ public RegularStop getStop() { return this.stop; } - @Nonnull @Override public StationElement getStationElement() { return this.stop; diff --git a/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java b/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java index ba7a1540715..591f71f1869 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/VehicleParkingEntranceVertex.java @@ -2,7 +2,6 @@ import java.util.Collection; import java.util.Objects; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingEntrance; @@ -25,7 +24,6 @@ public VehicleParkingEntranceVertex(VehicleParkingEntrance parkingEntrance) { this.parkingEntrance = parkingEntrance; } - @Nonnull @Override public I18NString getName() { return Objects.requireNonNullElse(parkingEntrance.getName(), NO_NAME); diff --git a/src/main/java/org/opentripplanner/street/model/vertex/Vertex.java b/src/main/java/org/opentripplanner/street/model/vertex/Vertex.java index 59407f93ea9..75fd5b7218e 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/Vertex.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/Vertex.java @@ -8,7 +8,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.Coordinate; import org.opentripplanner.astar.spi.AStarVertex; import org.opentripplanner.framework.geometry.WgsCoordinate; @@ -137,7 +136,6 @@ public final double getLat() { /** * Longer human-readable name for the client */ - @Nonnull public abstract I18NString getName(); /** @@ -163,6 +161,13 @@ public String getLabelString() { return getLabel().toString(); } + /** + * Return the position of the vertex as a WgsCoordinate. + */ + public WgsCoordinate toWgsCoordinate() { + return new WgsCoordinate(y, x); + } + public Coordinate getCoordinate() { return new Coordinate(getX(), getY()); } diff --git a/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java b/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java index c7e38ca0032..3e1a57a4581 100644 --- a/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java +++ b/src/main/java/org/opentripplanner/street/model/vertex/VertexFactory.java @@ -1,7 +1,6 @@ package org.opentripplanner.street.model.vertex; import java.util.Set; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.locationtech.jts.geom.Coordinate; import org.opentripplanner.framework.i18n.I18NString; @@ -15,6 +14,7 @@ import org.opentripplanner.transit.model.site.BoardingArea; import org.opentripplanner.transit.model.site.Entrance; import org.opentripplanner.transit.model.site.PathwayNode; +import org.opentripplanner.transit.model.site.Station; /** * This class is the central point where all vertices that are supposed to be permanently part @@ -32,12 +32,10 @@ public VertexFactory(Graph graph) { this.graph = graph; } - @Nonnull public TransitBoardingAreaVertex transitBoardingArea(BoardingArea boardingArea) { return addToGraph(new TransitBoardingAreaVertex(boardingArea)); } - @Nonnull public ElevatorOnboardVertex elevatorOnboard( Vertex sourceVertex, String label, @@ -46,7 +44,6 @@ public ElevatorOnboardVertex elevatorOnboard( return addToGraph(new ElevatorOnboardVertex(sourceVertex, label, levelName)); } - @Nonnull public ElevatorOffboardVertex elevatorOffboard( Vertex sourceVertex, String label, @@ -55,7 +52,6 @@ public ElevatorOffboardVertex elevatorOffboard( return addToGraph(new ElevatorOffboardVertex(sourceVertex, label, levelName)); } - @Nonnull public IntersectionVertex intersection(Coordinate edgeCoordinate) { return addToGraph( new LabelledIntersectionVertex( @@ -68,12 +64,10 @@ public IntersectionVertex intersection(Coordinate edgeCoordinate) { ); } - @Nonnull public IntersectionVertex intersection(String label, double longitude, double latitude) { return addToGraph(new LabelledIntersectionVertex(label, longitude, latitude, false, false)); } - @Nonnull public OsmBoardingLocationVertex osmBoardingLocation( Coordinate coordinate, String label, @@ -83,7 +77,6 @@ public OsmBoardingLocationVertex osmBoardingLocation( return addToGraph(new OsmBoardingLocationVertex(label, coordinate.x, coordinate.y, name, refs)); } - @Nonnull public SplitterVertex splitter( StreetEdge originalEdge, double x, @@ -93,17 +86,14 @@ public SplitterVertex splitter( return addToGraph(new SplitterVertex(uniqueSplitLabel, x, y, originalEdge.getName())); } - @Nonnull public BarrierVertex barrier(long nid, Coordinate coordinate) { return addToGraph(new BarrierVertex(coordinate.x, coordinate.y, nid)); } - @Nonnull public ExitVertex exit(long nid, Coordinate coordinate, String exitName) { return addToGraph(new ExitVertex(coordinate.x, coordinate.y, nid, exitName)); } - @Nonnull public OsmVertex osm( Coordinate coordinate, OSMNode node, @@ -121,12 +111,14 @@ public OsmVertex osm( ); } - @Nonnull public TransitStopVertex transitStop(TransitStopVertexBuilder transitStopVertexBuilder) { return addToGraph(transitStopVertexBuilder.build()); } - @Nonnull + public StationCentroidVertex stationCentroid(Station station) { + return addToGraph(new StationCentroidVertex(station)); + } + public VehicleParkingEntranceVertex vehicleParkingEntrance(VehicleParking vehicleParking) { return vehicleParkingEntrance(vehicleParking.getEntrances().get(0)); } @@ -135,17 +127,14 @@ public VehicleParkingEntranceVertex vehicleParkingEntrance(VehicleParkingEntranc return addToGraph(new VehicleParkingEntranceVertex(entrance)); } - @Nonnull public VehicleRentalPlaceVertex vehicleRentalPlace(VehicleRentalPlace station) { return addToGraph(new VehicleRentalPlaceVertex(station)); } - @Nonnull public TransitPathwayNodeVertex transitPathwayNode(PathwayNode node) { return addToGraph(new TransitPathwayNodeVertex(node)); } - @Nonnull public TransitEntranceVertex transitEntrance(Entrance entrance) { return addToGraph(new TransitEntranceVertex(entrance)); } diff --git a/src/main/java/org/opentripplanner/street/search/StreetSearchBuilder.java b/src/main/java/org/opentripplanner/street/search/StreetSearchBuilder.java index 079cd29706f..68a71aef0a7 100644 --- a/src/main/java/org/opentripplanner/street/search/StreetSearchBuilder.java +++ b/src/main/java/org/opentripplanner/street/search/StreetSearchBuilder.java @@ -3,7 +3,6 @@ import java.time.Duration; import java.util.Collection; import java.util.Set; -import javax.annotation.Nonnull; import org.opentripplanner.astar.AStarBuilder; import org.opentripplanner.astar.spi.DominanceFunction; import org.opentripplanner.astar.spi.RemainingWeightHeuristic; @@ -65,7 +64,6 @@ public StreetSearchBuilder setDataOverlayContext(DataOverlayContext dataOverlayC return this; } - @Nonnull @Override protected Duration streetRoutingTimeout() { return routeRequest.preferences().street().routingTimeout(); diff --git a/src/main/java/org/opentripplanner/street/search/TemporaryVerticesContainer.java b/src/main/java/org/opentripplanner/street/search/TemporaryVerticesContainer.java index 3e7ecd2c237..1c6394bfd2f 100644 --- a/src/main/java/org/opentripplanner/street/search/TemporaryVerticesContainer.java +++ b/src/main/java/org/opentripplanner/street/search/TemporaryVerticesContainer.java @@ -10,7 +10,6 @@ import org.locationtech.jts.geom.GeometryFactory; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.model.GenericLocation; -import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.api.response.InputField; import org.opentripplanner.routing.api.response.RoutingError; @@ -32,14 +31,16 @@ public class TemporaryVerticesContainer implements AutoCloseable { private final Graph graph; - private final RouteRequest opt; private final Set tempEdges; private final Set fromVertices; private final Set toVertices; + private final GenericLocation from; + private final GenericLocation to; public TemporaryVerticesContainer( Graph graph, - RouteRequest opt, + GenericLocation from, + GenericLocation to, StreetMode accessMode, StreetMode egressMode ) { @@ -47,9 +48,10 @@ public TemporaryVerticesContainer( this.graph = graph; StreetIndex index = this.graph.getStreetIndex(); - this.opt = opt; - fromVertices = index.getVerticesForLocation(opt.from(), accessMode, false, tempEdges); - toVertices = index.getVerticesForLocation(opt.to(), egressMode, true, tempEdges); + this.from = from; + this.to = to; + fromVertices = index.getStreetVerticesForLocation(from, accessMode, false, tempEdges); + toVertices = index.getStreetVerticesForLocation(to, egressMode, true, tempEdges); checkIfVerticesFound(); @@ -80,22 +82,48 @@ public Set getToVertices() { return toVertices; } + /** + * Get the stop vertices that corresponds to the from location. If the from location only contains + * coordinates, this will return an empty set. If the from location is a station id this will + * return the child stops of that station. + */ + public Set getFromStopVertices() { + StreetIndex index = this.graph.getStreetIndex(); + if (from.stopId == null) { + return Set.of(); + } + return index.getStopOrChildStopsVertices(from.stopId); + } + + /** + * Get the stop vertices that corresponds to the to location. If the to location only contains + * coordinates, this will return an empty set. If the to location is a station id this will + * return the child stops of that station. + */ + public Set getToStopVertices() { + StreetIndex index = this.graph.getStreetIndex(); + if (to.stopId == null) { + return Set.of(); + } + return index.getStopOrChildStopsVertices(to.stopId); + } + /* PRIVATE METHODS */ private void checkIfVerticesFound() { List routingErrors = new ArrayList<>(); // check that vertices where found if from-location was specified - if (opt.from().isSpecified() && isDisconnected(fromVertices, true)) { + if (from.isSpecified() && isDisconnected(fromVertices, true)) { routingErrors.add( - new RoutingError(getRoutingErrorCodeForDisconnected(opt.from()), InputField.FROM_PLACE) + new RoutingError(getRoutingErrorCodeForDisconnected(from), InputField.FROM_PLACE) ); } // check that vertices where found if to-location was specified - if (opt.to().isSpecified() && isDisconnected(toVertices, false)) { + if (to.isSpecified() && isDisconnected(toVertices, false)) { routingErrors.add( - new RoutingError(getRoutingErrorCodeForDisconnected(opt.to()), InputField.TO_PLACE) + new RoutingError(getRoutingErrorCodeForDisconnected(to), InputField.TO_PLACE) ); } diff --git a/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java b/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java index c33ed6a35e9..c93ea598256 100644 --- a/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java +++ b/src/main/java/org/opentripplanner/street/search/request/StreetSearchRequest.java @@ -1,7 +1,6 @@ package org.opentripplanner.street.search.request; import java.time.Instant; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; @@ -76,12 +75,10 @@ private StreetSearchRequest() { this.toEnvelope = createEnvelope(to); } - @Nonnull public static StreetSearchRequestBuilder of() { return new StreetSearchRequestBuilder(DEFAULT).withStartTime(Instant.now()); } - @Nonnull public static StreetSearchRequestBuilder copyOf(StreetSearchRequest original) { return new StreetSearchRequestBuilder(original); } diff --git a/src/main/java/org/opentripplanner/street/search/state/State.java b/src/main/java/org/opentripplanner/street/search/state/State.java index deef516a674..6c7f342cad6 100644 --- a/src/main/java/org/opentripplanner/street/search/state/State.java +++ b/src/main/java/org/opentripplanner/street/search/state/State.java @@ -8,7 +8,6 @@ import java.util.Objects; import java.util.Set; import java.util.stream.Stream; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.astar.spi.AStarState; import org.opentripplanner.ext.dataoverlay.routing.DataOverlayContext; @@ -57,7 +56,7 @@ public class State implements AStarState, Cloneable { /** * Create an initial state, forcing vertex to the specified value. Useful for tests, etc. */ - public State(@Nonnull Vertex vertex, @Nonnull StreetSearchRequest streetSearchRequest) { + public State(Vertex vertex, StreetSearchRequest streetSearchRequest) { this( vertex, streetSearchRequest.startTime(), @@ -66,12 +65,7 @@ public State(@Nonnull Vertex vertex, @Nonnull StreetSearchRequest streetSearchRe ); } - public State( - @Nonnull Vertex vertex, - @Nonnull Instant startTime, - @Nonnull StateData stateData, - @Nonnull StreetSearchRequest request - ) { + public State(Vertex vertex, Instant startTime, StateData stateData, StreetSearchRequest request) { this.request = request; this.weight = 0; this.vertex = vertex; diff --git a/src/main/java/org/opentripplanner/street/search/state/StateEditor.java b/src/main/java/org/opentripplanner/street/search/state/StateEditor.java index 40c44a16488..271b6e78030 100644 --- a/src/main/java/org/opentripplanner/street/search/state/StateEditor.java +++ b/src/main/java/org/opentripplanner/street/search/state/StateEditor.java @@ -1,7 +1,6 @@ package org.opentripplanner.street.search.state; import java.util.Set; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.street.model.RentalFormFactor; import org.opentripplanner.street.model.edge.Edge; @@ -135,7 +134,6 @@ public State makeState() { * Calls {@link StateEditor#makeState()} and wraps the result in an array of {@link State}. * If the state is null, then a zero-length array is returned. */ - @Nonnull public State[] makeStateArray() { return State.ofNullable(makeState()); } diff --git a/src/main/java/org/opentripplanner/transit/api/request/TripOnServiceDateRequest.java b/src/main/java/org/opentripplanner/transit/api/request/TripOnServiceDateRequest.java new file mode 100644 index 00000000000..6735dc1db29 --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/api/request/TripOnServiceDateRequest.java @@ -0,0 +1,77 @@ +package org.opentripplanner.transit.api.request; + +import java.time.LocalDate; +import java.util.List; +import org.opentripplanner.framework.collection.ListUtils; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.timetable.TripAlteration; + +/* + * A request for trips on a specific service date. + * + * This request is used to retrieve TripsOnServiceDates that match the provided criteria. + * At least one operatingDay must be provided. + */ +public class TripOnServiceDateRequest { + + private final List operatingDays; + private final List authorities; + private final List lines; + private final List serviceJourneys; + private final List replacementFor; + private final List privateCodes; + private final List alterations; + + protected TripOnServiceDateRequest( + List operatingDays, + List authorities, + List lines, + List serviceJourneys, + List replacementFor, + List privateCodes, + List alterations + ) { + if (operatingDays == null || operatingDays.isEmpty()) { + throw new IllegalArgumentException("operatingDays must have at least one date"); + } + this.operatingDays = ListUtils.nullSafeImmutableList(operatingDays); + this.authorities = ListUtils.nullSafeImmutableList(authorities); + this.lines = ListUtils.nullSafeImmutableList(lines); + this.serviceJourneys = ListUtils.nullSafeImmutableList(serviceJourneys); + this.replacementFor = ListUtils.nullSafeImmutableList(replacementFor); + this.privateCodes = ListUtils.nullSafeImmutableList(privateCodes); + this.alterations = ListUtils.nullSafeImmutableList(alterations); + } + + public static TripOnServiceDateRequestBuilder of() { + return new TripOnServiceDateRequestBuilder(); + } + + public List authorities() { + return authorities; + } + + public List lines() { + return lines; + } + + public List serviceJourneys() { + return serviceJourneys; + } + + public List replacementFor() { + return replacementFor; + } + + public List privateCodes() { + return privateCodes; + } + + public List alterations() { + return alterations; + } + + public List operatingDays() { + return operatingDays; + } +} diff --git a/src/main/java/org/opentripplanner/transit/api/request/TripOnServiceDateRequestBuilder.java b/src/main/java/org/opentripplanner/transit/api/request/TripOnServiceDateRequestBuilder.java new file mode 100644 index 00000000000..7aa2644fdc9 --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/api/request/TripOnServiceDateRequestBuilder.java @@ -0,0 +1,66 @@ +package org.opentripplanner.transit.api.request; + +import java.time.LocalDate; +import java.util.List; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.timetable.TripAlteration; + +public class TripOnServiceDateRequestBuilder { + + private List authorities; + private List lines; + private List serviceJourneys; + private List replacementFor; + private List privateCodes; + private List alterations; + private List operatingDays; + + protected TripOnServiceDateRequestBuilder() {} + + public TripOnServiceDateRequestBuilder withOperatingDays(List operatingDays) { + this.operatingDays = operatingDays; + return this; + } + + public TripOnServiceDateRequestBuilder withAuthorities(List authorities) { + this.authorities = authorities; + return this; + } + + public TripOnServiceDateRequestBuilder withLines(List lines) { + this.lines = lines; + return this; + } + + public TripOnServiceDateRequestBuilder withServiceJourneys(List serviceJourneys) { + this.serviceJourneys = serviceJourneys; + return this; + } + + public TripOnServiceDateRequestBuilder withReplacementFor(List replacementFor) { + this.replacementFor = replacementFor; + return this; + } + + public TripOnServiceDateRequestBuilder withPrivateCodes(List privateCodes) { + this.privateCodes = privateCodes; + return this; + } + + public TripOnServiceDateRequestBuilder withAlterations(List alterations) { + this.alterations = alterations; + return this; + } + + public TripOnServiceDateRequest build() { + return new TripOnServiceDateRequest( + operatingDays, + authorities, + lines, + serviceJourneys, + replacementFor, + privateCodes, + alterations + ); + } +} diff --git a/src/main/java/org/opentripplanner/transit/model/basic/MainAndSubMode.java b/src/main/java/org/opentripplanner/transit/model/basic/MainAndSubMode.java index 571584700e9..030e5b5ac63 100644 --- a/src/main/java/org/opentripplanner/transit/model/basic/MainAndSubMode.java +++ b/src/main/java/org/opentripplanner/transit/model/basic/MainAndSubMode.java @@ -4,13 +4,12 @@ import java.util.List; import java.util.function.Predicate; import java.util.stream.Stream; -import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * Tupple of main- and sub-mode. */ -public record MainAndSubMode(@Nonnull TransitMode mainMode, @Nullable SubMode subMode) { +public record MainAndSubMode(TransitMode mainMode, @Nullable SubMode subMode) { private static final List ALL = Stream .of(TransitMode.values()) .map(MainAndSubMode::new) diff --git a/src/main/java/org/opentripplanner/transit/model/basic/Money.java b/src/main/java/org/opentripplanner/transit/model/basic/Money.java index 35278d8fbd8..de1c6647b6b 100644 --- a/src/main/java/org/opentripplanner/transit/model/basic/Money.java +++ b/src/main/java/org/opentripplanner/transit/model/basic/Money.java @@ -8,7 +8,6 @@ import java.util.Locale; import java.util.Objects; import java.util.function.Function; -import javax.annotation.Nonnull; import org.opentripplanner.framework.lang.IntUtils; /** @@ -26,7 +25,7 @@ public class Money implements Comparable, Serializable { * @param currency The currency of the money amount * @param minorUnitAmount The amount in the smaller currency unit, so for 1.50 EUR pass 150. */ - private Money(@Nonnull Currency currency, int minorUnitAmount) { + private Money(Currency currency, int minorUnitAmount) { this.currency = Objects.requireNonNull(currency); this.amount = minorUnitAmount; } @@ -65,7 +64,7 @@ public static Money max(Money first, Money second) { * Take a fractional amount of money, ie 1.5 and convert it to amount using the number of default * fraction digits of the currency. */ - public static Money ofFractionalAmount(@Nonnull Currency currency, float fractionalAmount) { + public static Money ofFractionalAmount(Currency currency, float fractionalAmount) { Objects.requireNonNull(currency); var fractionDigits = currency.getDefaultFractionDigits(); int amount = IntUtils.round(fractionalAmount * Math.pow(10, fractionDigits)); @@ -181,7 +180,6 @@ private boolean booleanOp(Money other, boolean result) { return result; } - @Nonnull private Money op(Money other, Function op) { checkCurrencyOrThrow(other); return op.apply(other); diff --git a/src/main/java/org/opentripplanner/transit/model/basic/Notice.java b/src/main/java/org/opentripplanner/transit/model/basic/Notice.java index f9713417805..660a44c680f 100644 --- a/src/main/java/org/opentripplanner/transit/model/basic/Notice.java +++ b/src/main/java/org/opentripplanner/transit/model/basic/Notice.java @@ -1,7 +1,6 @@ package org.opentripplanner.transit.model.basic; import java.util.Objects; -import javax.annotation.Nonnull; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -34,7 +33,7 @@ public String publicCode() { } @Override - public boolean sameAs(@Nonnull Notice other) { + public boolean sameAs(Notice other) { return ( getId().equals(other.getId()) && Objects.equals(publicCode, other.publicCode) && @@ -43,7 +42,6 @@ public boolean sameAs(@Nonnull Notice other) { } @Override - @Nonnull public NoticeBuilder copy() { return new NoticeBuilder(this); } diff --git a/src/main/java/org/opentripplanner/transit/model/filter/expr/AndMatcher.java b/src/main/java/org/opentripplanner/transit/model/filter/expr/AndMatcher.java new file mode 100644 index 00000000000..74f38efa8b7 --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/filter/expr/AndMatcher.java @@ -0,0 +1,43 @@ +package org.opentripplanner.transit.model.filter.expr; + +import static org.opentripplanner.transit.model.filter.expr.BinaryOperator.AND; + +import java.util.List; + +/** + * Takes a list of matchers and provides a single interface. All matchers in the list must match for + * the composite matcher to return a match. + * + * @param The entity type the AndMatcher matches. + */ +public final class AndMatcher implements Matcher { + + private final Matcher[] matchers; + + private AndMatcher(List> matchers) { + this.matchers = matchers.toArray(Matcher[]::new); + } + + public static Matcher of(List> matchers) { + // simplify a list of one element + if (matchers.size() == 1) { + return matchers.get(0); + } + return new AndMatcher<>(matchers); + } + + @Override + public boolean match(T entity) { + for (var m : matchers) { + if (!m.match(entity)) { + return false; + } + } + return true; + } + + @Override + public String toString() { + return "(" + AND.arrayToString(matchers) + ')'; + } +} diff --git a/src/main/java/org/opentripplanner/transit/model/filter/expr/BinaryOperator.java b/src/main/java/org/opentripplanner/transit/model/filter/expr/BinaryOperator.java new file mode 100644 index 00000000000..62f3fa30f27 --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/filter/expr/BinaryOperator.java @@ -0,0 +1,37 @@ +package org.opentripplanner.transit.model.filter.expr; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Used to concatenate matches with either the logical "AND" or "OR" operator. + */ +enum BinaryOperator { + AND("&"), + OR("|"); + + private final String token; + + BinaryOperator(String token) { + this.token = token; + } + + @Override + public String toString() { + return token; + } + + String arrayToString(T[] values) { + return colToString(Arrays.asList(values)); + } + + String colToString(Collection values) { + return values.stream().map(Objects::toString).collect(Collectors.joining(" " + token + " ")); + } + + String toString(T a, T b) { + return a.toString() + " " + token + " " + b.toString(); + } +} diff --git a/src/main/java/org/opentripplanner/transit/model/filter/expr/ContainsMatcher.java b/src/main/java/org/opentripplanner/transit/model/filter/expr/ContainsMatcher.java new file mode 100644 index 00000000000..ed3731897ec --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/filter/expr/ContainsMatcher.java @@ -0,0 +1,55 @@ +package org.opentripplanner.transit.model.filter.expr; + +import java.util.function.Function; + +/** + * A matcher that applies a provided matcher to an iterable of child entities returned from the main + * entity that this matcher is for. + *

+ * If any of the iterable entities match the valueMatcher, then the match method returns true. In + * this way it is similar to an OR. + *

+ * @param The main entity type this matcher is applied to. + * @param The type of the child entities, for which there is a mapping from S to T. + */ +public class ContainsMatcher implements Matcher { + + private final String relationshipName; + private final Function> valuesProvider; + private final Matcher valueMatcher; + + /** + * @param relationshipName The name of the type of relationship between the main entity and the + * entity matched by the valueMatcher. + * @param valuesProvider The function that maps the entity being matched by this matcher (S) to + * the iterable of items being matched by valueMatcher. + * @param valueMatcher The matcher that is applied each of the iterable entities returned from the + * valuesProvider function. + */ + public ContainsMatcher( + String relationshipName, + Function> valuesProvider, + Matcher valueMatcher + ) { + this.relationshipName = relationshipName; + this.valuesProvider = valuesProvider; + this.valueMatcher = valueMatcher; + } + + public boolean match(S entity) { + if (valuesProvider.apply(entity) == null) { + return false; + } + for (T it : valuesProvider.apply(entity)) { + if (valueMatcher.match(it)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return "ContainsMatcher: " + relationshipName + ": " + valueMatcher.toString(); + } +} diff --git a/src/main/java/org/opentripplanner/transit/model/filter/expr/EqualityMatcher.java b/src/main/java/org/opentripplanner/transit/model/filter/expr/EqualityMatcher.java new file mode 100644 index 00000000000..1380131e07a --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/filter/expr/EqualityMatcher.java @@ -0,0 +1,40 @@ +package org.opentripplanner.transit.model.filter.expr; + +import java.util.function.Function; + +/** + * A matcher that checks if a value is equal to another value derived from the matched entities. + *

+ * The derived entity value is provided by a function that takes the entity being matched as an argument. + *

+ * @param The type of the entity being matched. + * @param The type of the value that the matcher will test equality for. + */ +public class EqualityMatcher implements Matcher { + + private final String typeName; + private final V value; + private final Function valueProvider; + + /** + * @param typeName The typeName appears in the toString for easier debugging. + * @param value The value that this matcher will check equality for. + * @param valueProvider The function that maps the entity being matched by this matcher (T) to + * the value being matched by this matcher. + */ + public EqualityMatcher(String typeName, V value, Function valueProvider) { + this.typeName = typeName; + this.value = value; + this.valueProvider = valueProvider; + } + + @Override + public boolean match(T entity) { + return value.equals(valueProvider.apply(entity)); + } + + @Override + public String toString() { + return typeName + "==" + value; + } +} diff --git a/src/main/java/org/opentripplanner/transit/model/filter/expr/ExpressionBuilder.java b/src/main/java/org/opentripplanner/transit/model/filter/expr/ExpressionBuilder.java new file mode 100644 index 00000000000..b1b4d5be322 --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/filter/expr/ExpressionBuilder.java @@ -0,0 +1,37 @@ +package org.opentripplanner.transit.model.filter.expr; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Function; + +/** + * A builder for creating complex matchers composed of other matchers. + *

+ * This builder contains convenience methods for creating complex matchers from simpler ones. The + * resulting matcher "ands" together all the matchers it has built up. This supports the common + * pattern of narrowing results with multiple filters. + * + * @param The type of entity to match in the expression. + */ +public class ExpressionBuilder { + + private final List> matchers = new ArrayList<>(); + + public static ExpressionBuilder of() { + return new ExpressionBuilder<>(); + } + + public ExpressionBuilder or(Collection values, Function> valueProvider) { + if (values.isEmpty()) { + return this; + } + + matchers.add(OrMatcher.of(values.stream().map(valueProvider).toList())); + return this; + } + + public Matcher build() { + return AndMatcher.of(matchers); + } +} diff --git a/src/main/java/org/opentripplanner/transit/model/filter/expr/Matcher.java b/src/main/java/org/opentripplanner/transit/model/filter/expr/Matcher.java new file mode 100644 index 00000000000..db3c02296b9 --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/filter/expr/Matcher.java @@ -0,0 +1,19 @@ +package org.opentripplanner.transit.model.filter.expr; + +/** + * Generic matcher interface - this is the root of the matcher type hierarchy. + *

+ * @param Domain type to match. + */ +@FunctionalInterface +public interface Matcher { + boolean match(T entity); + + static Matcher everything() { + return e -> true; + } + + static Matcher nothing() { + return e -> false; + } +} diff --git a/src/main/java/org/opentripplanner/transit/model/filter/expr/OrMatcher.java b/src/main/java/org/opentripplanner/transit/model/filter/expr/OrMatcher.java new file mode 100644 index 00000000000..62da7af63f4 --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/filter/expr/OrMatcher.java @@ -0,0 +1,58 @@ +package org.opentripplanner.transit.model.filter.expr; + +import static org.opentripplanner.transit.model.filter.expr.BinaryOperator.OR; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Takes a list of matchers and provides a single interface. At least one of the matchers in the + * list must match for the composite matcher to return a match. + *

+ * @param The entity type the OrMatcher matches. + */ +public final class OrMatcher implements Matcher { + + private final Matcher[] matchers; + + private OrMatcher(List> matchers) { + this.matchers = matchers.toArray(Matcher[]::new); + } + + public static Matcher of(Matcher a, Matcher b) { + return of(List.of(a, b)); + } + + public static Matcher of(List> matchers) { + // Simplify if there is just one matcher in the list + if (matchers.size() == 1) { + return matchers.get(0); + } + // Collapse nested or matchers + var expr = new ArrayList>(); + for (Matcher it : matchers) { + if (it instanceof OrMatcher orMatcher) { + expr.addAll(Arrays.asList(orMatcher.matchers)); + } else { + expr.add(it); + } + } + return new OrMatcher<>(expr); + } + + @Override + public boolean match(T entity) { + for (var m : matchers) { + if (m.match(entity)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return "(" + OR.arrayToString(matchers) + ')'; + } +} diff --git a/src/main/java/org/opentripplanner/transit/model/filter/transit/TripOnServiceDateMatcherFactory.java b/src/main/java/org/opentripplanner/transit/model/filter/transit/TripOnServiceDateMatcherFactory.java new file mode 100644 index 00000000000..f86e7a1ff77 --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/filter/transit/TripOnServiceDateMatcherFactory.java @@ -0,0 +1,70 @@ +package org.opentripplanner.transit.model.filter.transit; + +import java.time.LocalDate; +import org.opentripplanner.transit.api.request.TripOnServiceDateRequest; +import org.opentripplanner.transit.model.filter.expr.ContainsMatcher; +import org.opentripplanner.transit.model.filter.expr.EqualityMatcher; +import org.opentripplanner.transit.model.filter.expr.ExpressionBuilder; +import org.opentripplanner.transit.model.filter.expr.Matcher; +import org.opentripplanner.transit.model.framework.AbstractTransitEntity; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.timetable.TripAlteration; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; + +/** + * A factory for creating matchers for TripOnServiceDate objects. + *

+ * This factory is used to create matchers for TripOnServiceDate objects based on a request. The + * resulting matcher can be used to filter a list of TripOnServiceDate objects. + */ +public class TripOnServiceDateMatcherFactory { + + public static Matcher of(TripOnServiceDateRequest request) { + ExpressionBuilder expr = ExpressionBuilder.of(); + + expr.or(request.operatingDays(), TripOnServiceDateMatcherFactory::operatingDay); + expr.or(request.authorities(), TripOnServiceDateMatcherFactory::authorityId); + expr.or(request.lines(), TripOnServiceDateMatcherFactory::routeId); + expr.or(request.serviceJourneys(), TripOnServiceDateMatcherFactory::serviceJourneyId); + expr.or(request.replacementFor(), TripOnServiceDateMatcherFactory::replacementFor); + expr.or(request.privateCodes(), TripOnServiceDateMatcherFactory::privateCode); + expr.or(request.alterations(), TripOnServiceDateMatcherFactory::alteration); + return expr.build(); + } + + static Matcher authorityId(FeedScopedId id) { + return new EqualityMatcher<>("agency", id, t -> t.getTrip().getRoute().getAgency().getId()); + } + + static Matcher routeId(FeedScopedId id) { + return new EqualityMatcher<>("route", id, t -> t.getTrip().getRoute().getId()); + } + + static Matcher serviceJourneyId(FeedScopedId id) { + return new EqualityMatcher<>("serviceJourney", id, t -> t.getTrip().getId()); + } + + static Matcher replacementFor(FeedScopedId id) { + return new ContainsMatcher<>( + "replacementForContains", + t -> t.getReplacementFor().stream().map(AbstractTransitEntity::getId).toList(), + new EqualityMatcher<>("replacementForIdEquals", id, (idToMatch -> idToMatch)) + ); + } + + static Matcher privateCode(String code) { + return new EqualityMatcher<>( + "privateCode", + code, + t -> t.getTrip().getNetexInternalPlanningCode() + ); + } + + static Matcher operatingDay(LocalDate date) { + return new EqualityMatcher<>("operatingDay", date, TripOnServiceDate::getServiceDate); + } + + static Matcher alteration(TripAlteration alteration) { + return new EqualityMatcher<>("alteration", alteration, TripOnServiceDate::getTripAlteration); + } +} diff --git a/src/main/java/org/opentripplanner/transit/model/framework/AbstractBuilder.java b/src/main/java/org/opentripplanner/transit/model/framework/AbstractBuilder.java index de20445a7ba..60a970a0521 100644 --- a/src/main/java/org/opentripplanner/transit/model/framework/AbstractBuilder.java +++ b/src/main/java/org/opentripplanner/transit/model/framework/AbstractBuilder.java @@ -1,6 +1,5 @@ package org.opentripplanner.transit.model.framework; -import javax.annotation.Nonnull; import javax.annotation.Nullable; public abstract class AbstractBuilder< @@ -29,7 +28,7 @@ E original() { protected abstract E buildFromValues(); @Override - public final @Nonnull E build() { + public final E build() { var b = buildFromValues(); if (original == null) { diff --git a/src/main/java/org/opentripplanner/transit/model/framework/AbstractTransitEntity.java b/src/main/java/org/opentripplanner/transit/model/framework/AbstractTransitEntity.java index 4053a4860f2..258d505b53c 100644 --- a/src/main/java/org/opentripplanner/transit/model/framework/AbstractTransitEntity.java +++ b/src/main/java/org/opentripplanner/transit/model/framework/AbstractTransitEntity.java @@ -5,7 +5,6 @@ import java.util.List; import java.util.Objects; import java.util.Set; -import javax.annotation.Nonnull; /** * All OTP Transit entities should extend this class. The purpose of the class is to enforce a @@ -30,7 +29,7 @@ public abstract class AbstractTransitEntity< private final FeedScopedId id; - public AbstractTransitEntity(@Nonnull FeedScopedId id) { + public AbstractTransitEntity(FeedScopedId id) { this.id = Objects.requireNonNull(id); } diff --git a/src/main/java/org/opentripplanner/transit/model/framework/FeedScopedId.java b/src/main/java/org/opentripplanner/transit/model/framework/FeedScopedId.java index b48ff040c28..bdb09cb0db8 100644 --- a/src/main/java/org/opentripplanner/transit/model/framework/FeedScopedId.java +++ b/src/main/java/org/opentripplanner/transit/model/framework/FeedScopedId.java @@ -5,7 +5,6 @@ import java.io.Serializable; import java.util.Arrays; import java.util.List; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.lang.StringUtils; @@ -22,7 +21,7 @@ public final class FeedScopedId implements Serializable, Comparable { private Result() {} - public static Result failure(@Nonnull E failure) { + public static Result failure(E failure) { return new Failure<>(failure); } - public static Result success(@Nonnull T success) { + public static Result success(T success) { return new Success<>(success); } diff --git a/src/main/java/org/opentripplanner/transit/model/framework/TransitObject.java b/src/main/java/org/opentripplanner/transit/model/framework/TransitObject.java index ad09a40db07..60d261fee00 100644 --- a/src/main/java/org/opentripplanner/transit/model/framework/TransitObject.java +++ b/src/main/java/org/opentripplanner/transit/model/framework/TransitObject.java @@ -1,7 +1,6 @@ package org.opentripplanner.transit.model.framework; import java.io.Serializable; -import javax.annotation.Nonnull; public interface TransitObject, T extends TransitBuilder> extends Serializable { @@ -10,7 +9,7 @@ public interface TransitObject, T extends TransitB * the same value. This is used to avoid creating new objects during transit model construction * and during RealTime updates. */ - boolean sameAs(@Nonnull E other); + boolean sameAs(E other); /** * The copy method is used to mutate the existing object by creating a builder and setting @@ -25,6 +24,5 @@ public interface TransitObject, T extends TransitB *

* TODO RTM - Document design "rules" in a package readme, when the design is set. */ - @Nonnull TransitBuilder copy(); } diff --git a/src/main/java/org/opentripplanner/transit/model/network/GroupOfRoutes.java b/src/main/java/org/opentripplanner/transit/model/network/GroupOfRoutes.java index c52e7ffe3e6..66cf57788c6 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/GroupOfRoutes.java +++ b/src/main/java/org/opentripplanner/transit/model/network/GroupOfRoutes.java @@ -1,7 +1,6 @@ package org.opentripplanner.transit.model.network; import java.util.Objects; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -34,13 +33,11 @@ public static GroupOfRoutesBuilder of(FeedScopedId id) { return new GroupOfRoutesBuilder(id); } - @Nonnull public String getName() { return logName(); } @Override - @Nonnull public String logName() { return name; } @@ -66,7 +63,7 @@ public GroupOfRoutesBuilder copy() { } @Override - public boolean sameAs(@Nonnull GroupOfRoutes other) { + public boolean sameAs(GroupOfRoutes other) { return ( getId().equals(other.getId()) && Objects.equals(name, other.name) && diff --git a/src/main/java/org/opentripplanner/transit/model/network/GroupOfRoutesBuilder.java b/src/main/java/org/opentripplanner/transit/model/network/GroupOfRoutesBuilder.java index 1080209f33c..c4fb7967a55 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/GroupOfRoutesBuilder.java +++ b/src/main/java/org/opentripplanner/transit/model/network/GroupOfRoutesBuilder.java @@ -1,6 +1,5 @@ package org.opentripplanner.transit.model.network; -import javax.annotation.Nonnull; import org.opentripplanner.transit.model.framework.AbstractEntityBuilder; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -21,7 +20,7 @@ public class GroupOfRoutesBuilder super(id); } - GroupOfRoutesBuilder(@Nonnull GroupOfRoutes original) { + GroupOfRoutesBuilder(GroupOfRoutes original) { super(original); this.privateCode = original.getPrivateCode(); this.shortName = original.getShortName(); diff --git a/src/main/java/org/opentripplanner/transit/model/network/Route.java b/src/main/java/org/opentripplanner/transit/model/network/Route.java index 721c6098ac8..206e65f7af9 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/Route.java +++ b/src/main/java/org/opentripplanner/transit/model/network/Route.java @@ -7,7 +7,6 @@ import java.util.List; import java.util.Locale; import java.util.Objects; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.lang.IntUtils; @@ -65,12 +64,12 @@ public final class Route extends AbstractTransitEntity impl this.textColor = builder.getTextColor(); } - public static RouteBuilder of(@Nonnull FeedScopedId id) { + public static RouteBuilder of(FeedScopedId id) { return new RouteBuilder(id); } @Override - public boolean sameAs(@Nonnull Route other) { + public boolean sameAs(Route other) { return ( getId().equals(other.getId()) && Objects.equals(this.agency, other.agency) && @@ -101,7 +100,6 @@ public RouteBuilder copy() { * The 'agency' property represent a GTFS Agency and NeTEx the Authority. Note that Agency does * NOT map 1-1 to Authority, it is rather a mix between Authority and Operator. */ - @Nonnull public Agency getAgency() { return agency; } @@ -119,7 +117,6 @@ public Branding getBranding() { return branding; } - @Nonnull public List getGroupsOfRoutes() { return groupsOfRoutes; } @@ -134,7 +131,6 @@ public I18NString getLongName() { return longName; } - @Nonnull public TransitMode getMode() { return mode; } @@ -160,7 +156,6 @@ public Integer getGtfsSortOrder() { return gtfsSortOrder; } - @Nonnull public SubMode getNetexSubmode() { return netexSubmode; } @@ -180,7 +175,6 @@ public String getTextColor() { return textColor; } - @Nonnull public BikeAccess getBikesAllowed() { return bikesAllowed; } @@ -194,13 +188,11 @@ public String getFlexibleLineType() { } /** @return the route's short name, or the long name if the short name is null. */ - @Nonnull public String getName(Locale locale) { return shortName == null ? longName.toString(locale) : shortName; } /** @return the route's short name, or the long name if the short name is null. */ - @Nonnull public String getName() { return shortName == null ? longName.toString() : shortName; } diff --git a/src/main/java/org/opentripplanner/transit/model/network/StopPattern.java b/src/main/java/org/opentripplanner/transit/model/network/StopPattern.java index 3fc51cea0c6..7395d28419f 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/StopPattern.java +++ b/src/main/java/org/opentripplanner/transit/model/network/StopPattern.java @@ -8,7 +8,6 @@ import java.util.Objects; import java.util.Optional; import java.util.function.Predicate; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.framework.lang.MemEfficientArrayBuilder; import org.opentripplanner.model.PickDrop; @@ -272,7 +271,7 @@ private int findStopPosition( * @param index Given index for stop * @return true if stop and next stop are equal on both stop patterns, else false */ - boolean sameStops(@Nonnull StopPattern other, int index) { + boolean sameStops(StopPattern other, int index) { var otherOrigin = other.getStop(index); var otherDestination = other.getStop(index + 1); var origin = getStop(index); @@ -289,7 +288,7 @@ boolean sameStops(@Nonnull StopPattern other, int index) { * @return true if the stops have the same stations, else false. If any station is null then * false. */ - boolean sameStations(@Nonnull StopPattern other, int index) { + boolean sameStations(StopPattern other, int index) { var otherOrigin = other.getStop(index).getParentStation(); var otherDestination = other.getStop(index + 1).getParentStation(); var origin = getStop(index).getParentStation(); diff --git a/src/main/java/org/opentripplanner/transit/model/network/TripPattern.java b/src/main/java/org/opentripplanner/transit/model/network/TripPattern.java index 57c71d06113..becf1686733 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/TripPattern.java +++ b/src/main/java/org/opentripplanner/transit/model/network/TripPattern.java @@ -1,17 +1,14 @@ package org.opentripplanner.transit.model.network; import static java.util.Objects.requireNonNull; -import static java.util.Objects.requireNonNullElseGet; import static org.opentripplanner.framework.lang.ObjectUtils.requireNotInitialized; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; -import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.LineString; import org.opentripplanner.framework.geometry.CompactLineStringUtils; @@ -28,7 +25,6 @@ import org.opentripplanner.transit.model.site.Station; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.timetable.Direction; -import org.opentripplanner.transit.model.timetable.FrequencyEntry; import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripTimes; import org.slf4j.Logger; @@ -122,20 +118,27 @@ public final class TripPattern private final RoutingTripPattern routingTripPattern; - public TripPattern(TripPatternBuilder builder) { + TripPattern(TripPatternBuilder builder) { super(builder.getId()); this.name = builder.getName(); this.route = builder.getRoute(); this.stopPattern = requireNonNull(builder.getStopPattern()); this.createdByRealtimeUpdater = builder.isCreatedByRealtimeUpdate(); - this.mode = requireNonNullElseGet(builder.getMode(), route::getMode); - this.netexSubMode = requireNonNullElseGet(builder.getNetexSubmode(), route::getNetexSubmode); + this.mode = requireNonNull(builder.getMode()); + this.netexSubMode = requireNonNull(builder.getNetexSubmode()); this.containsMultipleModes = builder.getContainsMultipleModes(); - this.scheduledTimetable = - builder.getScheduledTimetable() != null - ? builder.getScheduledTimetable() - : new Timetable(this); + if (builder.getScheduledTimetable() != null) { + if (builder.getScheduledTimetableBuilder() != null) { + throw new IllegalArgumentException( + "Cannot provide both scheduled timetable and scheduled timetable builder" + ); + } + this.scheduledTimetable = builder.getScheduledTimetable(); + } else { + this.scheduledTimetable = + builder.getScheduledTimetableBuilder().withTripPattern(this).build(); + } this.originalTripPattern = builder.getOriginalTripPattern(); @@ -143,7 +146,7 @@ public TripPattern(TripPatternBuilder builder) { this.routingTripPattern = new RoutingTripPattern(this, builder); } - public static TripPatternBuilder of(@Nonnull FeedScopedId id) { + public static TripPatternBuilder of(FeedScopedId id) { return new TripPatternBuilder(id); } @@ -331,56 +334,6 @@ public boolean isBoardAndAlightAt(int stopIndex, PickDrop value) { /* METHODS THAT DELEGATE TO THE SCHEDULED TIMETABLE */ - // TODO OTP2 this method modifies the state, it will be refactored in a subsequent step - /** - * Add the given tripTimes to this pattern's scheduled timetable, recording the corresponding trip - * as one of the scheduled trips on this pattern. - */ - public void add(TripTimes tt) { - // Only scheduled trips (added at graph build time, rather than directly to the timetable - // via updates) are in this list. - scheduledTimetable.addTripTimes(tt); - - // Check that all trips added to this pattern are on the initially declared route. - // Identity equality is valid on GTFS entity objects. - if (this.route != tt.getTrip().getRoute()) { - LOG.warn( - "The trip {} is on route {} but its stop pattern is on route {}.", - tt.getTrip(), - tt.getTrip().getRoute(), - route - ); - } - } - - // TODO OTP2 this method modifies the state, it will be refactored in a subsequent step - /** - * Add the given FrequencyEntry to this pattern's scheduled timetable, recording the corresponding - * trip as one of the scheduled trips on this pattern. - * TODO possible improvements: combine freq entries and TripTimes. Do not keep trips list in TripPattern - * since it is redundant. - */ - public void add(FrequencyEntry freq) { - scheduledTimetable.addFrequencyEntry(freq); - if (this.getRoute() != freq.tripTimes.getTrip().getRoute()) { - LOG.warn( - "The trip {} is on a different route than its stop pattern, which is on {}.", - freq.tripTimes.getTrip(), - route - ); - } - } - - // TODO OTP2 this method modifies the state, it will be refactored in a subsequent step - /** - * Remove all trips matching the given predicate. - * - * @param removeTrip it the predicate returns true - */ - public void removeTrips(Predicate removeTrip) { - scheduledTimetable.getTripTimes().removeIf(tt -> removeTrip.test(tt.getTrip())); - } - /** * Checks that this is TripPattern is based of the provided TripPattern and contains same stops * (but not necessarily with same pickup and dropoff values). @@ -394,7 +347,11 @@ public boolean isModifiedFromTripPatternWithEqualStops(TripPattern other) { } /** - * The direction for all the trips in this pattern. + * Return the direction for all the trips in this pattern. + * By construction, all trips in a pattern have the same direction: + * - trips derived from NeTEx data belong to a ServiceJourney that belongs to a JourneyPattern + * that belongs to a NeTEx Route that specifies a single direction. + * - trips derived from GTFS data are grouped by direction in a trip pattern, during graph build. */ public Direction getDirection() { return scheduledTimetable.getDirection(); @@ -503,7 +460,7 @@ private static Coordinate coordinate(StopLocation s) { } @Override - public boolean sameAs(@Nonnull TripPattern other) { + public boolean sameAs(TripPattern other) { return ( getId().equals(other.getId()) && Objects.equals(this.route, other.route) && diff --git a/src/main/java/org/opentripplanner/transit/model/network/TripPatternBuilder.java b/src/main/java/org/opentripplanner/transit/model/network/TripPatternBuilder.java index f34d206922b..ec2451ea66d 100644 --- a/src/main/java/org/opentripplanner/transit/model/network/TripPatternBuilder.java +++ b/src/main/java/org/opentripplanner/transit/model/network/TripPatternBuilder.java @@ -1,18 +1,23 @@ package org.opentripplanner.transit.model.network; +import static java.util.Objects.requireNonNullElseGet; + import java.util.ArrayList; import java.util.List; +import java.util.function.UnaryOperator; import java.util.stream.IntStream; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.LineString; import org.opentripplanner.framework.geometry.CompactLineStringUtils; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.model.Timetable; +import org.opentripplanner.model.TimetableBuilder; import org.opentripplanner.routing.algorithm.raptoradapter.api.SlackProvider; import org.opentripplanner.transit.model.basic.SubMode; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.framework.AbstractEntityBuilder; import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.timetable.Direction; @SuppressWarnings("UnusedReturnValue") public final class TripPatternBuilder @@ -24,6 +29,7 @@ public final class TripPatternBuilder private boolean containsMultipleModes; private StopPattern stopPattern; private Timetable scheduledTimetable; + private TimetableBuilder scheduledTimetableBuilder; private String name; private boolean createdByRealtimeUpdate; @@ -33,6 +39,7 @@ public final class TripPatternBuilder TripPatternBuilder(FeedScopedId id) { super(id); + this.scheduledTimetableBuilder = Timetable.of(); } TripPatternBuilder(TripPattern original) { @@ -86,10 +93,28 @@ public TripPatternBuilder withStopPattern(StopPattern stopPattern) { } public TripPatternBuilder withScheduledTimeTable(Timetable scheduledTimetable) { + if (scheduledTimetableBuilder != null) { + throw new IllegalStateException( + "Cannot set scheduled Timetable after scheduled Timetable builder is created" + ); + } this.scheduledTimetable = scheduledTimetable; return this; } + public TripPatternBuilder withScheduledTimeTableBuilder( + UnaryOperator producer + ) { + // create a builder for the scheduled timetable only if it needs to be modified. + // otherwise reuse the existing timetable + if (scheduledTimetableBuilder == null) { + scheduledTimetableBuilder = scheduledTimetable.copyOf(); + scheduledTimetable = null; + } + producer.apply(scheduledTimetableBuilder); + return this; + } + public TripPatternBuilder withCreatedByRealtimeUpdater(boolean createdByRealtimeUpdate) { this.createdByRealtimeUpdate = createdByRealtimeUpdate; return this; @@ -115,6 +140,13 @@ public int transitReluctanceFactorIndex() { return route.getMode().ordinal(); } + public Direction getDirection() { + if (scheduledTimetable != null) { + return scheduledTimetable.getDirection(); + } + return scheduledTimetableBuilder.getDirection(); + } + @Override protected TripPattern buildFromValues() { return new TripPattern(this); @@ -125,11 +157,11 @@ public Route getRoute() { } public TransitMode getMode() { - return mode; + return mode != null ? mode : route.getMode(); } public SubMode getNetexSubmode() { - return netexSubMode; + return netexSubMode != null ? netexSubMode : route.getNetexSubmode(); } public boolean getContainsMultipleModes() { @@ -144,6 +176,10 @@ public Timetable getScheduledTimetable() { return scheduledTimetable; } + public TimetableBuilder getScheduledTimetableBuilder() { + return scheduledTimetableBuilder; + } + public String getName() { return name; } diff --git a/src/main/java/org/opentripplanner/transit/model/organization/Agency.java b/src/main/java/org/opentripplanner/transit/model/organization/Agency.java index d72aa6588f3..4d82ab05e57 100644 --- a/src/main/java/org/opentripplanner/transit/model/organization/Agency.java +++ b/src/main/java/org/opentripplanner/transit/model/organization/Agency.java @@ -5,7 +5,6 @@ import java.time.ZoneId; import java.util.Objects; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -47,16 +46,14 @@ public final class Agency extends AbstractTransitEntity i this.brandingUrl = builder.getBrandingUrl(); } - public static AgencyBuilder of(@Nonnull FeedScopedId id) { + public static AgencyBuilder of(FeedScopedId id) { return new AgencyBuilder(id); } - @Nonnull public String getName() { return logName(); } - @Nonnull public ZoneId getTimezone() { return timezone; } @@ -87,19 +84,17 @@ public String getBrandingUrl() { } @Override - @Nonnull public AgencyBuilder copy() { return new AgencyBuilder(this); } @Override - @Nonnull public String logName() { return name; } @Override - public boolean sameAs(@Nonnull Agency other) { + public boolean sameAs(Agency other) { return ( getId().equals(other.getId()) && Objects.equals(name, other.name) && diff --git a/src/main/java/org/opentripplanner/transit/model/organization/AgencyBuilder.java b/src/main/java/org/opentripplanner/transit/model/organization/AgencyBuilder.java index 8411797f888..3cf24523c5d 100644 --- a/src/main/java/org/opentripplanner/transit/model/organization/AgencyBuilder.java +++ b/src/main/java/org/opentripplanner/transit/model/organization/AgencyBuilder.java @@ -1,6 +1,5 @@ package org.opentripplanner.transit.model.organization; -import javax.annotation.Nonnull; import org.opentripplanner.transit.model.framework.AbstractEntityBuilder; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -18,7 +17,7 @@ public class AgencyBuilder extends AbstractEntityBuilder super(id); } - AgencyBuilder(@Nonnull Agency original) { + AgencyBuilder(Agency original) { super(original); this.name = original.getName(); this.timezone = original.getTimezone().getId(); diff --git a/src/main/java/org/opentripplanner/transit/model/organization/Branding.java b/src/main/java/org/opentripplanner/transit/model/organization/Branding.java index b0599d31341..502c4a27cc7 100644 --- a/src/main/java/org/opentripplanner/transit/model/organization/Branding.java +++ b/src/main/java/org/opentripplanner/transit/model/organization/Branding.java @@ -1,7 +1,6 @@ package org.opentripplanner.transit.model.organization; import java.util.Objects; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -30,7 +29,7 @@ public class Branding extends AbstractTransitEntity i this.image = builder.getImage(); } - public static BrandingBuilder of(@Nonnull FeedScopedId id) { + public static BrandingBuilder of(FeedScopedId id) { return new BrandingBuilder(id); } @@ -66,13 +65,12 @@ public String getDescription() { } @Override - @Nonnull public BrandingBuilder copy() { return new BrandingBuilder(this); } @Override - public boolean sameAs(@Nonnull Branding other) { + public boolean sameAs(Branding other) { return ( getId().equals(other.getId()) && Objects.equals(name, other.name) && diff --git a/src/main/java/org/opentripplanner/transit/model/organization/BrandingBuilder.java b/src/main/java/org/opentripplanner/transit/model/organization/BrandingBuilder.java index 1367d683058..77f35d86fa1 100644 --- a/src/main/java/org/opentripplanner/transit/model/organization/BrandingBuilder.java +++ b/src/main/java/org/opentripplanner/transit/model/organization/BrandingBuilder.java @@ -1,6 +1,5 @@ package org.opentripplanner.transit.model.organization; -import javax.annotation.Nonnull; import org.opentripplanner.transit.model.framework.AbstractEntityBuilder; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -16,7 +15,7 @@ public class BrandingBuilder extends AbstractEntityBuilder i this.phone = builder.getPhone(); } - public static OperatorBuilder of(@Nonnull FeedScopedId id) { + public static OperatorBuilder of(FeedScopedId id) { return new OperatorBuilder(id); } - @Nonnull public String getName() { return logName(); } @Override - @Nonnull public String logName() { return name; } @@ -60,13 +57,12 @@ public String getPhone() { } @Override - @Nonnull public OperatorBuilder copy() { return new OperatorBuilder(this); } @Override - public boolean sameAs(@Nonnull Operator other) { + public boolean sameAs(Operator other) { return ( getId().equals(other.getId()) && Objects.equals(name, other.name) && 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 35576e0c42f..03844c80b77 100644 --- a/src/main/java/org/opentripplanner/transit/model/site/AreaStop.java +++ b/src/main/java/org/opentripplanner/transit/model/site/AreaStop.java @@ -3,7 +3,6 @@ import java.util.Objects; import java.util.Optional; import java.util.function.IntSupplier; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.locationtech.jts.geom.Geometry; import org.opentripplanner.framework.geometry.WgsCoordinate; @@ -69,7 +68,6 @@ public int getIndex() { * communication, eg. the name of the village where the service stops. */ @Override - @Nonnull public I18NString getName() { return name; } @@ -85,7 +83,6 @@ public I18NString getUrl() { return url; } - @Nonnull @Override public StopType getStopType() { return StopType.FLEXIBLE_AREA; @@ -97,7 +94,6 @@ public String getFirstZoneAsString() { } @Override - @Nonnull public WgsCoordinate getCoordinate() { return centroid; } @@ -137,7 +133,7 @@ public boolean hasFallbackName() { } @Override - public boolean sameAs(@Nonnull AreaStop other) { + public boolean sameAs(AreaStop other) { return ( getId().equals(other.getId()) && Objects.equals(name, other.getName()) && @@ -149,7 +145,6 @@ public boolean sameAs(@Nonnull AreaStop other) { } @Override - @Nonnull public AreaStopBuilder copy() { return new AreaStopBuilder(this); } diff --git a/src/main/java/org/opentripplanner/transit/model/site/AreaStopBuilder.java b/src/main/java/org/opentripplanner/transit/model/site/AreaStopBuilder.java index 8e29ad4acbf..51bbc06f427 100644 --- a/src/main/java/org/opentripplanner/transit/model/site/AreaStopBuilder.java +++ b/src/main/java/org/opentripplanner/transit/model/site/AreaStopBuilder.java @@ -2,7 +2,6 @@ import java.util.Objects; import java.util.function.IntSupplier; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.Geometry; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; @@ -26,7 +25,7 @@ public class AreaStopBuilder extends AbstractEntityBuilder getChildStops() { return this.childStations.stream().flatMap(s -> s.getChildStops().stream()).toList(); } - @Nonnull public Collection getChildStations() { return this.childStations; } @@ -70,7 +66,7 @@ public GroupOfStationsPurpose getPurposeOfGrouping() { } @Override - public boolean sameAs(@Nonnull GroupOfStations other) { + public boolean sameAs(GroupOfStations other) { return ( getId().equals(other.getId()) && Objects.equals(name, other.getName()) && @@ -80,7 +76,6 @@ public boolean sameAs(@Nonnull GroupOfStations other) { ); } - @Nonnull @Override public GroupOfStationsBuilder copy() { return new GroupOfStationsBuilder(this); diff --git a/src/main/java/org/opentripplanner/transit/model/site/GroupOfStationsBuilder.java b/src/main/java/org/opentripplanner/transit/model/site/GroupOfStationsBuilder.java index 72da8921446..a9e87c4c022 100644 --- a/src/main/java/org/opentripplanner/transit/model/site/GroupOfStationsBuilder.java +++ b/src/main/java/org/opentripplanner/transit/model/site/GroupOfStationsBuilder.java @@ -2,7 +2,6 @@ import java.util.HashSet; import java.util.Set; -import javax.annotation.Nonnull; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.transit.model.framework.AbstractEntityBuilder; @@ -20,7 +19,7 @@ public class GroupOfStationsBuilder super(id); } - GroupOfStationsBuilder(@Nonnull GroupOfStations original) { + GroupOfStationsBuilder(GroupOfStations original) { super(original); // Required fields this.name = I18NString.assertHasValue(original.getName()); 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 c674d588238..40618e60ace 100644 --- a/src/main/java/org/opentripplanner/transit/model/site/GroupStop.java +++ b/src/main/java/org/opentripplanner/transit/model/site/GroupStop.java @@ -4,7 +4,6 @@ import java.util.Objects; import java.util.Optional; import java.util.function.IntSupplier; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryCollection; @@ -65,7 +64,6 @@ public I18NString getUrl() { } @Override - @Nonnull public StopType getStopType() { return StopType.FLEXIBLE_GROUP; } @@ -79,7 +77,6 @@ public String getFirstZoneAsString() { * Returns the centroid of all stops and areas belonging to this location group. */ @Override - @Nonnull public WgsCoordinate getCoordinate() { return centroid; } @@ -116,13 +113,12 @@ public boolean isPartOfSameStationAs(StopLocation alternativeStop) { * Returns all the locations belonging to this location group. */ @Override - @Nonnull public List getChildLocations() { return stopLocations; } @Override - public boolean sameAs(@Nonnull GroupStop other) { + public boolean sameAs(GroupStop other) { return ( getId().equals(other.getId()) && Objects.equals(name, other.getName()) && @@ -131,7 +127,6 @@ public boolean sameAs(@Nonnull GroupStop other) { } @Override - @Nonnull public GroupStopBuilder copy() { return new GroupStopBuilder(this); } 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 64cad9325d1..c51b704aa33 100644 --- a/src/main/java/org/opentripplanner/transit/model/site/GroupStopBuilder.java +++ b/src/main/java/org/opentripplanner/transit/model/site/GroupStopBuilder.java @@ -3,7 +3,6 @@ import java.util.ArrayList; import java.util.List; import java.util.function.IntSupplier; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryCollection; import org.opentripplanner.framework.geometry.GeometryUtils; @@ -34,7 +33,7 @@ public class GroupStopBuilder extends AbstractEntityBuilder getFareZones() { return fareZones; } - @Nonnull public Collection getBoardingAreas() { return boardingAreas; } @Override - @Nonnull public RegularStopBuilder copy() { return new RegularStopBuilder(this); } @Override - public boolean sameAs(@Nonnull RegularStop other) { + public boolean sameAs(RegularStop other) { return ( super.sameAs(other) && Objects.equals(platformCode, other.platformCode) && diff --git a/src/main/java/org/opentripplanner/transit/model/site/Station.java b/src/main/java/org/opentripplanner/transit/model/site/Station.java index 68ce2d6d5a2..c8d2558a5cd 100644 --- a/src/main/java/org/opentripplanner/transit/model/site/Station.java +++ b/src/main/java/org/opentripplanner/transit/model/site/Station.java @@ -9,7 +9,6 @@ import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.locationtech.jts.algorithm.ConvexHull; import org.locationtech.jts.geom.Geometry; @@ -34,6 +33,7 @@ public class Station private final String code; private final I18NString description; private final WgsCoordinate coordinate; + private final boolean shouldRouteToCentroid; private final StopTransferPriority priority; private final I18NString url; private final ZoneId timezone; @@ -50,6 +50,7 @@ public class Station // Required fields this.name = Objects.requireNonNull(builder.getName()); this.coordinate = Objects.requireNonNull(builder.getCoordinate()); + this.shouldRouteToCentroid = builder.shouldRouteToCentroid(); this.priority = Objects.requireNonNullElse(builder.getPriority(), StopTransferPriority.defaultValue()); this.transfersNotAllowed = builder.isTransfersNotAllowed(); @@ -77,12 +78,10 @@ public boolean includes(StopLocation stop) { return childStops.contains(stop); } - @Nonnull public I18NString getName() { return name; } - @Nonnull public Collection getChildStops() { return childStops; } @@ -97,11 +96,18 @@ public double getLon() { return coordinate.longitude(); } - @Nonnull public WgsCoordinate getCoordinate() { return coordinate; } + /** + * When doing a street search from/to the station, we can either route to the centroid of the station + * or from/to any child stop. This feature is inactive unless configured. + */ + public boolean shouldRouteToCentroid() { + return shouldRouteToCentroid; + } + /** Public facing station code (short text or number) */ @Nullable public String getCode() { @@ -132,7 +138,6 @@ public I18NString getUrl() { * that the {@link StopTransferPriority#ALLOWED} (which is default) should a nett-effect of adding * 0 - zero cost. */ - @Nonnull public StopTransferPriority getPriority() { return priority; } @@ -153,7 +158,6 @@ public boolean isTransfersNotAllowed() { * A geometry collection that contains the center point and the convex hull of all the child * stops. */ - @Nonnull public GeometryCollection getGeometry() { return geometry; } @@ -165,19 +169,19 @@ public String logName() { } @Override - @Nonnull public StationBuilder copy() { return new StationBuilder(this); } @Override - public boolean sameAs(@Nonnull Station other) { + public boolean sameAs(Station other) { return ( getId().equals(other.getId()) && Objects.equals(name, other.name) && Objects.equals(code, other.code) && Objects.equals(description, other.description) && Objects.equals(coordinate, other.coordinate) && + Objects.equals(shouldRouteToCentroid, other.shouldRouteToCentroid) && Objects.equals(priority, other.priority) && Objects.equals(url, other.url) && Objects.equals(timezone, other.timezone) diff --git a/src/main/java/org/opentripplanner/transit/model/site/StationBuilder.java b/src/main/java/org/opentripplanner/transit/model/site/StationBuilder.java index 8d74f36fb73..79b3b529146 100644 --- a/src/main/java/org/opentripplanner/transit/model/site/StationBuilder.java +++ b/src/main/java/org/opentripplanner/transit/model/site/StationBuilder.java @@ -16,6 +16,7 @@ public class StationBuilder extends AbstractEntityBuilder getFareZones() { return List.of(); } - @Nonnull default Accessibility getWheelchairAccessibility() { return Accessibility.NO_INFORMATION; } @@ -112,7 +107,6 @@ default String getFirstZoneAsString() { * Representative location for the StopLocation. Can either be the actual location of the stop, or * the centroid of an area or line. */ - @Nonnull WgsCoordinate getCoordinate(); /** @@ -140,7 +134,6 @@ default ZoneId getTimeZone() { boolean isPartOfStation(); - @Nonnull default StopTransferPriority getPriority() { return StopTransferPriority.defaultValue(); } diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/StopTimeKey.java b/src/main/java/org/opentripplanner/transit/model/timetable/StopTimeKey.java index d110778a267..d3267804f70 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/StopTimeKey.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/StopTimeKey.java @@ -1,6 +1,5 @@ package org.opentripplanner.transit.model.timetable; -import javax.annotation.Nonnull; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -16,18 +15,17 @@ public class StopTimeKey extends AbstractTransitEntity impleme this.netexInternalPlanningCode = builder.getNetexInternalPlanningCode(); } - public static TripBuilder of(@Nonnull FeedScopedId id) { + public static TripBuilder of(FeedScopedId id) { return new TripBuilder(id); } @@ -93,7 +92,6 @@ public Operator getOperator() { return operator; } - @Nonnull public Route getRoute() { return route; } @@ -117,12 +115,10 @@ public String getShortName() { return shortName; } - @Nonnull public TransitMode getMode() { return mode; } - @Nonnull public SubMode getNetexSubMode() { return netexSubmode; } @@ -140,17 +136,14 @@ public FeedScopedId getShapeId() { /** * The direction for this Trip (and all other Trips in this TripPattern). */ - @Nonnull public Direction getDirection() { return direction; } - @Nonnull public BikeAccess getBikesAllowed() { return bikesAllowed; } - @Nonnull public Accessibility getWheelchairBoarding() { return wheelchairBoarding; } @@ -175,7 +168,6 @@ public String getNetexInternalPlanningCode() { *

* This is planned, by default (e.g. GTFS and if not set explicit). */ - @Nonnull public TripAlteration getNetexAlteration() { return netexAlteration; } @@ -198,7 +190,7 @@ public String logName() { } @Override - public boolean sameAs(@Nonnull Trip other) { + public boolean sameAs(Trip other) { return ( getId().equals(other.getId()) && Objects.equals(this.operator, other.operator) && diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/TripOnServiceDate.java b/src/main/java/org/opentripplanner/transit/model/timetable/TripOnServiceDate.java index 1b4ecc964cf..ee520f9e0f0 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/TripOnServiceDate.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/TripOnServiceDate.java @@ -3,7 +3,6 @@ import java.time.LocalDate; import java.util.List; import java.util.Objects; -import javax.annotation.Nonnull; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -26,7 +25,7 @@ public class TripOnServiceDate this.replacementFor = builder.getReplacementFor(); } - public static TripOnServiceDateBuilder of(@Nonnull FeedScopedId id) { + public static TripOnServiceDateBuilder of(FeedScopedId id) { return new TripOnServiceDateBuilder(id); } @@ -51,7 +50,7 @@ public TripIdAndServiceDate getTripIdAndServiceDate() { } @Override - public boolean sameAs(@Nonnull TripOnServiceDate other) { + public boolean sameAs(TripOnServiceDate other) { return ( getId().equals(other.getId()) && Objects.equals(this.trip, other.trip) && @@ -61,7 +60,6 @@ public boolean sameAs(@Nonnull TripOnServiceDate other) { ); } - @Nonnull @Override public TripOnServiceDateBuilder copy() { return new TripOnServiceDateBuilder(this); diff --git a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index 8fa18443bab..1738c66bab0 100644 --- a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -35,8 +35,11 @@ import org.opentripplanner.routing.services.TransitAlertService; import org.opentripplanner.routing.stoptimes.ArrivalDeparture; import org.opentripplanner.routing.stoptimes.StopTimesHelper; +import org.opentripplanner.transit.api.request.TripOnServiceDateRequest; import org.opentripplanner.transit.model.basic.Notice; import org.opentripplanner.transit.model.basic.TransitMode; +import org.opentripplanner.transit.model.filter.expr.Matcher; +import org.opentripplanner.transit.model.filter.transit.TripOnServiceDateMatcherFactory; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -591,6 +594,23 @@ public TripOnServiceDate getTripOnServiceDateForTripAndDay( return transitModelIndex.getTripOnServiceDateForTripAndDay().get(tripIdAndServiceDate); } + /** + * Returns a list of TripOnServiceDates that match the filtering defined in the request. + * + * @param request - A TripOnServiceDateRequest object with filtering defined. + * @return - A list of TripOnServiceDates + */ + @Override + public List getTripOnServiceDates(TripOnServiceDateRequest request) { + Matcher matcher = TripOnServiceDateMatcherFactory.of(request); + return transitModelIndex + .getTripOnServiceDateForTripAndDay() + .values() + .stream() + .filter(matcher::match) + .collect(Collectors.toList()); + } + /** * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix * this when doing the issue #3030. diff --git a/src/main/java/org/opentripplanner/transit/service/TransitModel.java b/src/main/java/org/opentripplanner/transit/service/TransitModel.java index 84c7597d562..87380b66b1b 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitModel.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitModel.java @@ -19,7 +19,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.ext.flex.trip.FlexTrip; import org.opentripplanner.framework.lang.ObjectUtils; @@ -261,7 +260,7 @@ public void updateCalendarServiceData( * service period {@code null} is returned. */ @Nullable - public FeedScopedId getOrCreateServiceIdForDate(@Nonnull LocalDate serviceDate) { + public FeedScopedId getOrCreateServiceIdForDate(LocalDate serviceDate) { // Start of day ZonedDateTime time = ServiceDateUtils.asStartOfService(serviceDate, getTimeZone()); diff --git a/src/main/java/org/opentripplanner/transit/service/TransitService.java b/src/main/java/org/opentripplanner/transit/service/TransitService.java index 8ac6e6ba03a..cfc6bb64a53 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -24,6 +24,7 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitLayer; import org.opentripplanner.routing.services.TransitAlertService; import org.opentripplanner.routing.stoptimes.ArrivalDeparture; +import org.opentripplanner.transit.api.request.TripOnServiceDateRequest; import org.opentripplanner.transit.model.basic.Notice; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.framework.AbstractTransitEntity; @@ -313,4 +314,12 @@ List stopTimesForPatternAtStop( Set getAllServiceCodes(); Map getServiceCodesRunningForDate(); + + /** + * Returns a list of TripOnServiceDates that match the filtering defined in the request. + * + * @param request - A TripOnServiceDateRequest object with filtering defined. + * @return - A list of TripOnServiceDates + */ + List getTripOnServiceDates(TripOnServiceDateRequest request); } diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java index 03ce9052331..b8ff20f9c02 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java @@ -35,7 +35,6 @@ import java.util.Objects; import java.util.Set; import java.util.function.Supplier; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.framework.lang.StringUtils; @@ -513,7 +512,6 @@ private Result validateAndHandleAddedTrip( /** * Remove any stop that is not know in the static transit data. */ - @Nonnull private List removeUnknownStops(TripUpdate tripUpdate, FeedScopedId tripId) { return tripUpdate .getStopTimeUpdateList() diff --git a/src/main/java/org/opentripplanner/updater/trip/TripPatternCache.java b/src/main/java/org/opentripplanner/updater/trip/TripPatternCache.java index bf8e75418ae..f94d023c560 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TripPatternCache.java +++ b/src/main/java/org/opentripplanner/updater/trip/TripPatternCache.java @@ -2,7 +2,6 @@ import java.util.HashMap; import java.util.Map; -import javax.annotation.Nonnull; import org.opentripplanner.gtfs.GenerateTripPatternsOperation; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.Route; @@ -47,8 +46,8 @@ public class TripPatternCache { * @return cached or newly created trip pattern */ public synchronized TripPattern getOrCreateTripPattern( - @Nonnull final StopPattern stopPattern, - @Nonnull final Trip trip, + final StopPattern stopPattern, + final Trip trip, final TripPattern originalTripPattern ) { Route route = trip.getRoute(); diff --git a/src/main/java/org/opentripplanner/updater/vehicle_parking/AvailabilityDatasourceFactory.java b/src/main/java/org/opentripplanner/updater/vehicle_parking/AvailabilityDatasourceFactory.java index 876cd303c78..66c01b4b33b 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_parking/AvailabilityDatasourceFactory.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_parking/AvailabilityDatasourceFactory.java @@ -16,7 +16,6 @@ public static DataSource create(VehicleParkingUpdaterParamete BICYCLE_PARK_API, HSL_PARK, BIKEEP, - NOI_OPEN_DATA_HUB, BIKELY -> throw new IllegalArgumentException("Cannot instantiate SIRI-FM data source"); }; } diff --git a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingDataSourceFactory.java b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingDataSourceFactory.java index 09ecb67a54d..ef587cd2b68 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingDataSourceFactory.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingDataSourceFactory.java @@ -6,8 +6,6 @@ import org.opentripplanner.ext.vehicleparking.bikely.BikelyUpdaterParameters; import org.opentripplanner.ext.vehicleparking.hslpark.HslParkUpdater; import org.opentripplanner.ext.vehicleparking.hslpark.HslParkUpdaterParameters; -import org.opentripplanner.ext.vehicleparking.noi.NoiUpdater; -import org.opentripplanner.ext.vehicleparking.noi.NoiUpdaterParameters; import org.opentripplanner.ext.vehicleparking.parkapi.BicycleParkAPIUpdater; import org.opentripplanner.ext.vehicleparking.parkapi.CarParkAPIUpdater; import org.opentripplanner.ext.vehicleparking.parkapi.ParkAPIUpdaterParameters; @@ -40,7 +38,6 @@ public static DataSource create( openingHoursCalendarService ); case BIKELY -> new BikelyUpdater((BikelyUpdaterParameters) parameters); - case NOI_OPEN_DATA_HUB -> new NoiUpdater((NoiUpdaterParameters) parameters); case BIKEEP -> new BikeepUpdater((BikeepUpdaterParameters) parameters); case SIRI_FM -> throw new IllegalArgumentException("Cannot instantiate SIRI-FM data source"); }; diff --git a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingSourceType.java b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingSourceType.java index 1601245b16c..f146bb7dcf2 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingSourceType.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingSourceType.java @@ -5,7 +5,6 @@ public enum VehicleParkingSourceType { BICYCLE_PARK_API, HSL_PARK, BIKELY, - NOI_OPEN_DATA_HUB, BIKEEP, SIRI_FM, } diff --git a/src/main/java/org/opentripplanner/updater/vehicle_position/RealtimeVehiclePatternMatcher.java b/src/main/java/org/opentripplanner/updater/vehicle_position/RealtimeVehiclePatternMatcher.java index fe29ef35b97..0723adc91b8 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_position/RealtimeVehiclePatternMatcher.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_position/RealtimeVehiclePatternMatcher.java @@ -29,7 +29,6 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.lang.StringUtils; import org.opentripplanner.framework.time.ServiceDateUtils; @@ -206,8 +205,8 @@ protected static LocalDate inferServiceDate( private RealtimeVehicle mapRealtimeVehicle( VehiclePosition vehiclePosition, List stopsOnVehicleTrip, - @Nonnull Trip trip, - @Nonnull Function stopIndexOfGtfsSequence + Trip trip, + Function stopIndexOfGtfsSequence ) { var newVehicle = RealtimeVehicle.builder(); diff --git a/src/main/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdater.java b/src/main/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdater.java index a024bac9629..60eaae3a8ed 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdater.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdater.java @@ -7,7 +7,6 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; @@ -142,7 +141,6 @@ private Map applyExtension( * When finding the edges near the business area border in Oslo this speeds up the computation * from ~25 seconds to ~3 seconds (on 2021 hardware). */ - @Nonnull private Set getEdgesAlongLineStrings(Collection lineStrings) { return lineStrings .stream() diff --git a/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFreeVehicleStatusMapper.java b/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFreeVehicleStatusMapper.java index 7fd884b7ab8..959521e017e 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFreeVehicleStatusMapper.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/GbfsFreeVehicleStatusMapper.java @@ -5,7 +5,6 @@ import java.time.Instant; import java.util.HashMap; import java.util.Map; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.mobilitydata.gbfs.v2_3.free_bike_status.GBFSBike; import org.mobilitydata.gbfs.v2_3.free_bike_status.GBFSRentalUris; @@ -20,7 +19,6 @@ public class GbfsFreeVehicleStatusMapper { private final VehicleRentalSystem system; - @Nonnull private final Map vehicleTypes; public GbfsFreeVehicleStatusMapper( diff --git a/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/GbfsVehicleRentalDataSourceParameters.java b/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/GbfsVehicleRentalDataSourceParameters.java index 99a5b48772c..7d7a6c75a5d 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/GbfsVehicleRentalDataSourceParameters.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/GbfsVehicleRentalDataSourceParameters.java @@ -1,6 +1,5 @@ package org.opentripplanner.updater.vehicle_rental.datasources.params; -import javax.annotation.Nonnull; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.vehicle_rental.VehicleRentalSourceType; @@ -14,7 +13,6 @@ public record GbfsVehicleRentalDataSourceParameters( boolean overloadingAllowed ) implements VehicleRentalDataSourceParameters { - @Nonnull @Override public VehicleRentalSourceType sourceType() { return VehicleRentalSourceType.GBFS; diff --git a/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/VehicleRentalDataSourceParameters.java b/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/VehicleRentalDataSourceParameters.java index abf7aa805cd..cdff994ab18 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/VehicleRentalDataSourceParameters.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_rental/datasources/params/VehicleRentalDataSourceParameters.java @@ -1,20 +1,16 @@ package org.opentripplanner.updater.vehicle_rental.datasources.params; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.vehicle_rental.VehicleRentalSourceType; public interface VehicleRentalDataSourceParameters { - @Nonnull String url(); @Nullable String network(); - @Nonnull VehicleRentalSourceType sourceType(); - @Nonnull HttpHeaders httpHeaders(); } diff --git a/src/main/java/org/opentripplanner/visualizer/GraphVisualizer.java b/src/main/java/org/opentripplanner/visualizer/GraphVisualizer.java index 6ab7d86917c..5ca8e55fc14 100644 --- a/src/main/java/org/opentripplanner/visualizer/GraphVisualizer.java +++ b/src/main/java/org/opentripplanner/visualizer/GraphVisualizer.java @@ -514,7 +514,8 @@ protected void route(String from, String to) { try ( var temporaryVertices = new TemporaryVerticesContainer( graph, - options, + options.from(), + options.to(), options.journey().direct().mode(), options.journey().direct().mode() ) diff --git a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls index 78f18f4e654..839fd3c6d61 100644 --- a/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls +++ b/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls @@ -670,6 +670,11 @@ type Leg { """ headsign: String """ + An identifier for the leg, which can be used to re-fetch transit leg information. + Re-fetching fails when the underlying transit data no longer exists. + """ + id: String + """ Interlines with previous leg. This is true when the same vehicle is used for the previous leg as for this leg and passenger can stay inside the vehicle. @@ -1166,6 +1171,11 @@ type QueryType { time: Int! ): Trip """ + Try refetching the current state of a transit leg using its id. + This fails when the underlying transit data (mostly IDs) has changed or are no longer available. + """ + leg(id: String!): Leg + """ Get all places (stops, stations, etc. with coordinates) within the specified radius from a location. The returned type is a Relay connection (see https://facebook.github.io/relay/graphql/connections.htm). The placeAtDistance @@ -1512,7 +1522,7 @@ type QueryType { walkSpeed: Float, "Whether the itinerary must be wheelchair accessible. Default value: false" wheelchair: Boolean - ): Plan @async + ): Plan @async @deprecated(reason : "Use `planConnection` instead.") """ Plan (itinerary) search that follows [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm). @@ -1597,7 +1607,7 @@ type QueryType { number of itineraries in each search. """ searchWindow: Duration - ): PlanConnection @async @deprecated(reason : "Experimental and can include breaking changes, use plan instead") + ): PlanConnection @async "Get a single rental vehicle based on its ID, i.e. value of field `vehicleId`" rentalVehicle(id: String!): RentalVehicle "Get all rental vehicles" diff --git a/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql b/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql index dafd4d6fa1d..4ef44e1a432 100644 --- a/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql +++ b/src/main/resources/org/opentripplanner/apis/transmodel/schema.graphql @@ -325,7 +325,7 @@ type Leg { fromPlace: Place! "Generalized cost or weight of the leg. Used for debugging." generalizedCost: Int - "An identifier for the leg, which can be used to re-fetch the information." + "An identifier for the leg, which can be used to re-fetch transit leg information." id: ID interchangeFrom: Interchange interchangeTo: Interchange @@ -647,7 +647,7 @@ type QueryType { groupOfLines(id: String!): GroupOfLines "Get all groups of lines" groupsOfLines: [GroupOfLines!]! - "Refetch a single leg based on its id" + "Refetch a single transit leg based on its id" leg(id: ID!): Leg @timingData "Get a single line based on its id" line(id: ID!): Line @timingData @@ -730,7 +730,7 @@ type QueryType { ): quayAtDistanceConnection @timingData "Get default routing parameters." routingParameters: RoutingParameters @timingData - "Get OTP server information" + "Get OTP deployment information. This is only useful for developers of OTP itself not regular API users." serverInfo: ServerInfo! @timingData "Get a single service journey based on its id" serviceJourney(id: String!): ServiceJourney @timingData @@ -1072,6 +1072,11 @@ type RoutingParameters { wheelChairAccessible: Boolean } +""" +Information about the deployment. This is only useful to developers of OTP itself. +It is not recommended for regular API consumers to use this type as it has no +stability guarantees. +""" type ServerInfo { "The 'configVersion' of the build-config.json file." buildConfigVersion: String @@ -1080,6 +1085,12 @@ type ServerInfo { gitBranch: String gitCommit: String gitCommitTime: String + """ + The internal time zone of the transit data. + + Note: The input data can be in several time zones, but OTP internally operates on a single one. + """ + internalTransitModelTimeZone: String "The 'configVersion' of the otp-config.json file." otpConfigVersion: String "The otp-serialization-version-id used to check graphs for compatibility with current version of OTP." diff --git a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java index 0e0eb159c1b..416ca6c364e 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -30,7 +30,6 @@ import java.util.Locale; import java.util.Set; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.glassfish.jersey.message.internal.OutboundJaxrsResponse; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; @@ -163,11 +162,13 @@ static void setup() { var model = stopModel.build(); var transitModel = new TransitModel(model, DEDUPLICATOR); - final TripPattern pattern = TEST_MODEL.pattern(BUS).build(); var trip = TransitModelForTest.trip("123").withHeadsign(I18NString.of("Trip Headsign")).build(); var stopTimes = TEST_MODEL.stopTimesEvery5Minutes(3, trip, T11_00); var tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, DEDUPLICATOR); - pattern.add(tripTimes); + final TripPattern pattern = TEST_MODEL + .pattern(BUS) + .withScheduledTimeTableBuilder(builder -> builder.addTripTimes(tripTimes)) + .build(); transitModel.addTripPattern(id("pattern-1"), pattern); @@ -383,7 +384,6 @@ void graphQL(Path path) throws IOException { assertEqualJson(expectedJson, actualJson); } - @Nonnull private static List getTransitAlert(EntitySelector.Stop entitySelector) { var alertWithoutDescription = TransitAlert .of(id("no-description")) @@ -404,7 +404,6 @@ private static List getTransitAlert(EntitySelector.Stop entitySele .toList(); } - @Nonnull private static WalkStepBuilder walkStep(String name) { return WalkStep .builder() @@ -413,7 +412,6 @@ private static WalkStepBuilder walkStep(String name) { .withAngle(10); } - @Nonnull private static FareProduct fareProduct(String name) { return new FareProduct( id(name), @@ -429,7 +427,6 @@ private static FareProduct fareProduct(String name) { * Locate 'expectations' relative to the given query input file. The 'expectations' and 'queries' * subdirectories are expected to be in the same directory. */ - @Nonnull private static Path getExpectation(Path path) { return path .getParent() diff --git a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java index 6a60490a07f..db056050394 100644 --- a/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java +++ b/src/test/java/org/opentripplanner/apis/vectortiles/DebugStyleSpecTest.java @@ -16,6 +16,9 @@ class DebugStyleSpecTest { private final ResourceLoader RESOURCES = ResourceLoader.of(this); + /** + * Remove the style.json file and re-run this test in order to regenerate the file. + */ @Test void spec() throws IOException { var vectorSource = new VectorSource("vectorSource", "https://example.com"); diff --git a/src/test/java/org/opentripplanner/astar/strategy/MaxCountSkipEdgeStrategyTest.java b/src/test/java/org/opentripplanner/astar/strategy/MaxCountSkipEdgeStrategyTest.java index 88976591ea1..c190ff1abac 100644 --- a/src/test/java/org/opentripplanner/astar/strategy/MaxCountSkipEdgeStrategyTest.java +++ b/src/test/java/org/opentripplanner/astar/strategy/MaxCountSkipEdgeStrategyTest.java @@ -9,10 +9,12 @@ class MaxCountSkipEdgeStrategyTest { + private final StreetNearbyStopFinder finder = new StreetNearbyStopFinder(null, 0, null); + @Test void countStops() { var state = TestStateBuilder.ofWalking().stop().build(); - var strategy = new MaxCountSkipEdgeStrategy<>(1, StreetNearbyStopFinder::hasReachedStop); + var strategy = new MaxCountSkipEdgeStrategy<>(1, finder::hasReachedStop); assertFalse(strategy.shouldSkipEdge(state, null)); assertTrue(strategy.shouldSkipEdge(state, null)); } @@ -20,7 +22,7 @@ void countStops() { @Test void doNotCountStop() { var state = TestStateBuilder.ofWalking().build(); - var strategy = new MaxCountSkipEdgeStrategy<>(1, StreetNearbyStopFinder::hasReachedStop); + var strategy = new MaxCountSkipEdgeStrategy<>(1, finder::hasReachedStop); assertFalse(strategy.shouldSkipEdge(state, null)); assertFalse(strategy.shouldSkipEdge(state, null)); assertFalse(strategy.shouldSkipEdge(state, null)); @@ -30,7 +32,7 @@ void doNotCountStop() { void nonFinalState() { var state = TestStateBuilder.ofScooterRentalArriveBy().stop().build(); assertFalse(state.isFinal()); - var strategy = new MaxCountSkipEdgeStrategy<>(1, StreetNearbyStopFinder::hasReachedStop); + var strategy = new MaxCountSkipEdgeStrategy<>(1, finder::hasReachedStop); assertFalse(strategy.shouldSkipEdge(state, null)); } } diff --git a/src/test/java/org/opentripplanner/framework/collection/CollectionUtilsTest.java b/src/test/java/org/opentripplanner/framework/collection/CollectionUtilsTest.java index 51e827fad09..6686ac8e0d9 100644 --- a/src/test/java/org/opentripplanner/framework/collection/CollectionUtilsTest.java +++ b/src/test/java/org/opentripplanner/framework/collection/CollectionUtilsTest.java @@ -19,6 +19,13 @@ class CollectionUtilsTest { public static final String NULL_STRING = ""; + @Test + void testIsEmpty() { + assertTrue(CollectionUtils.isEmpty((List) null)); + assertTrue(CollectionUtils.isEmpty(List.of())); + assertFalse(CollectionUtils.isEmpty(List.of(1))); + } + @Test void testToString() { assertEquals("", CollectionUtils.toString(null, NULL_STRING)); diff --git a/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java b/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java index 7eb6a685cca..3353d901f98 100644 --- a/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java @@ -12,7 +12,6 @@ import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; import org.opentripplanner.generate.doc.framework.GeneratesDocumentation; import org.opentripplanner.generate.doc.framework.TemplateUtil; @@ -38,7 +37,7 @@ public void updateTutorialDoc() throws IOException { String original = readFile(OUT_FILE); var routeQuery = getGraphQlQuery("routes-tutorial.graphql"); - var planQuery = getGraphQlQuery("plan-tutorial.graphql"); + var planQuery = getGraphQlQuery("planConnection-tutorial.graphql"); doc = replaceSection(doc, "route-query", routeQuery); doc = replaceSection(doc, "plan-query", planQuery); @@ -47,7 +46,6 @@ public void updateTutorialDoc() throws IOException { assertFileEquals(original, OUT_FILE); } - @Nonnull private static String getGraphQlQuery(String resourceName) throws IOException { var url = Resources.getResource("org/opentripplanner/apis/gtfs/queries/" + resourceName); var query = TemplateUtil.graphQlExample(Resources.toString(url, StandardCharsets.UTF_8)); diff --git a/src/test/java/org/opentripplanner/generate/doc/NetexTutorialDocTest.java b/src/test/java/org/opentripplanner/generate/doc/NetexTutorialDocTest.java new file mode 100644 index 00000000000..5a953c1d21c --- /dev/null +++ b/src/test/java/org/opentripplanner/generate/doc/NetexTutorialDocTest.java @@ -0,0 +1,55 @@ +package org.opentripplanner.generate.doc; + +import static org.opentripplanner.framework.application.OtpFileNames.BUILD_CONFIG_FILENAME; +import static org.opentripplanner.framework.application.OtpFileNames.ROUTER_CONFIG_FILENAME; +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.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; +import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceSection; +import static org.opentripplanner.standalone.config.framework.json.JsonSupport.jsonNodeFromResource; + +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.standalone.config.RouterConfig; +import org.opentripplanner.standalone.config.framework.json.NodeAdapter; + +@GeneratesDocumentation +class NetexTutorialDocTest { + + private static final String NETEX_TUTORIAL_MD = "Netex-Tutorial.md"; + private static final File TEMPLATE = new File(TEMPLATE_PATH, NETEX_TUTORIAL_MD); + private static final File OUT_FILE = new File(USER_DOC_PATH, NETEX_TUTORIAL_MD); + + private static final String TUTORIAL_PATH = "standalone/config/netex-tutorial/"; + + @Test + void updateTutorialDoc() { + // Read and close input file (same as output file) + String template = readFile(TEMPLATE); + String original = readFile(OUT_FILE); + + template = replaceSection(template, "build-config", toJson(BUILD_CONFIG_FILENAME)); + template = replaceSection(template, "router-config", toJson(ROUTER_CONFIG_FILENAME)); + + writeFile(OUT_FILE, template); + assertFileEquals(original, OUT_FILE); + } + + private static String toJson(String fileName) { + var path = TUTORIAL_PATH + fileName; + var nodeAdapter = loadBuildConfig(path); + var buf = new DocBuilder(); + buf.addExample(fileName, nodeAdapter.rawNode()); + return buf.toString(); + } + + private static NodeAdapter loadBuildConfig(String buildConfigPath) { + var json = jsonNodeFromResource(buildConfigPath); + var conf = new RouterConfig(json, buildConfigPath, false); + return conf.asNodeAdapter(); + } +} diff --git a/src/test/java/org/opentripplanner/generate/doc/framework/DocBuilder.java b/src/test/java/org/opentripplanner/generate/doc/framework/DocBuilder.java index a105adb62d6..16579075077 100644 --- a/src/test/java/org/opentripplanner/generate/doc/framework/DocBuilder.java +++ b/src/test/java/org/opentripplanner/generate/doc/framework/DocBuilder.java @@ -99,6 +99,9 @@ public DocBuilder addSection(String text) { return endParagraph(); } + /** + * Adds a JSON snippet which is formatted as a fenced code block. + */ public void addExample(String comment, JsonNode body) { buffer.append(TemplateUtil.jsonExample(body, comment)); } diff --git a/src/test/java/org/opentripplanner/generate/doc/framework/DocsTestConstants.java b/src/test/java/org/opentripplanner/generate/doc/framework/DocsTestConstants.java index a7f7afa806a..cc306c133ac 100644 --- a/src/test/java/org/opentripplanner/generate/doc/framework/DocsTestConstants.java +++ b/src/test/java/org/opentripplanner/generate/doc/framework/DocsTestConstants.java @@ -14,19 +14,20 @@ public interface DocsTestConstants { File USER_DOC_PATH = new File(DOC_ROOT, "user"); /** - * This method return {@code true} if the /docs directory is available. If not, a warning is + * This method return {@code true} if both the /doc/user and /doc/templates directories are available. If not, a warning is * logged and the method returns {@code false}. This is used by the {@link GeneratesDocumentation} * annotation. */ static boolean docsExistOrWarn() { - if (USER_DOC_PATH.exists()) { + if (USER_DOC_PATH.exists() && TEMPLATE_PATH.exists()) { return true; } + LOG.warn( """ - SKIP TEST - '/docs' NOT FOUND + SKIP TEST - '/doc/user' or '/doc/templates' NOT FOUND - The doc/templates directory might not be available if you run the tests outside the + The /doc/user and /doc/templates directories might not be available if you run the tests outside the root of the projects. This may happen if the project root is not the working directory, if you run tests using jar files or in a Maven multi-module project. diff --git a/src/test/java/org/opentripplanner/generate/doc/framework/GeneratesDocumentation.java b/src/test/java/org/opentripplanner/generate/doc/framework/GeneratesDocumentation.java index d8ce7a529d2..2dc099b2a0b 100644 --- a/src/test/java/org/opentripplanner/generate/doc/framework/GeneratesDocumentation.java +++ b/src/test/java/org/opentripplanner/generate/doc/framework/GeneratesDocumentation.java @@ -8,7 +8,7 @@ import org.junit.jupiter.api.condition.EnabledIf; /** - * Use this annotation on tests that generate(access) the /docs directory outside the + * Use this annotation on tests that generate(access) the /doc directory outside the * source/resource. *

* All tests annotated with this annotation is tagged with "docs". You may include or exclude diff --git a/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java b/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java index ebde5cbdfe8..002c386d151 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java @@ -290,13 +290,18 @@ private TestOtpModel model( new Builder() { @Override public void build() { - S0 = stop("S0", 47.495, 19.001, withNoTransfersOnStations); - S11 = stop("S11", 47.500, 19.001, withNoTransfersOnStations); - S12 = stop("S12", 47.520, 19.001, withNoTransfersOnStations); - S13 = stop("S13", 47.540, 19.001, withNoTransfersOnStations); - S21 = stop("S21", 47.500, 19.011, withNoTransfersOnStations); - S22 = stop("S22", 47.520, 19.011, withNoTransfersOnStations); - S23 = stop("S23", 47.540, 19.011, withNoTransfersOnStations); + var station = stationEntity( + "1", + builder -> builder.withTransfersNotAllowed(withNoTransfersOnStations) + ); + + S0 = stop("S0", 47.495, 19.001, station); + S11 = stop("S11", 47.500, 19.001, station); + S12 = stop("S12", 47.520, 19.001, station); + S13 = stop("S13", 47.540, 19.001, station); + S21 = stop("S21", 47.500, 19.011, station); + S22 = stop("S22", 47.520, 19.011, station); + S23 = stop("S23", 47.540, 19.011, station); V0 = intersection("V0", 47.495, 19.000); V11 = intersection("V11", 47.500, 19.000); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/GtfsFeedIdTest.java b/src/test/java/org/opentripplanner/graph_builder/module/GtfsFeedIdTest.java index 133ec71070a..f1d6f2e8e77 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/GtfsFeedIdTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/GtfsFeedIdTest.java @@ -5,7 +5,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -36,7 +35,6 @@ void keepUnderscore() { assertEquals("feed_id_", feedId("feed_id_")); } - @Nonnull private static String feedId(String input) { var id = new GtfsFeedId.Builder().id(input).build().getId(); assertNotNull(id); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/geometry/CalculateWorldEnvelopeModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/geometry/CalculateWorldEnvelopeModuleTest.java index fb7ae2b8a10..9157f8433b8 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/geometry/CalculateWorldEnvelopeModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/geometry/CalculateWorldEnvelopeModuleTest.java @@ -3,7 +3,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.List; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.street.model.vertex.Vertex; @@ -63,7 +62,6 @@ public VertexLabel getLabel() { return VertexLabel.string("%s/%s".formatted(getX(), getY())); } - @Nonnull @Override public I18NString getName() { return I18NString.of(getLabel().toString()); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderTest.java b/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderTest.java new file mode 100644 index 00000000000..261a40454f0 --- /dev/null +++ b/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderTest.java @@ -0,0 +1,197 @@ +package org.opentripplanner.graph_builder.module.nearbystops; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.time.Duration; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.opentripplanner.framework.geometry.WgsCoordinate; +import org.opentripplanner.routing.algorithm.GraphRoutingTest; +import org.opentripplanner.routing.api.request.RouteRequest; +import org.opentripplanner.routing.api.request.request.StreetRequest; +import org.opentripplanner.routing.graphfinder.NearbyStop; +import org.opentripplanner.street.model.vertex.TransitStopVertex; +import org.opentripplanner.street.model.vertex.Vertex; + +class StreetNearbyStopFinderTest extends GraphRoutingTest { + + private static final WgsCoordinate origin = new WgsCoordinate(0.0, 0.0); + private TransitStopVertex isolatedStop; + private TransitStopVertex stopA; + private TransitStopVertex stopB; + private TransitStopVertex stopC; + private TransitStopVertex stopD; + + @BeforeEach + protected void setUp() throws Exception { + modelOf( + new GraphRoutingTest.Builder() { + @Override + public void build() { + var isolated = intersection("isolated", origin.moveNorthMeters(1000)); + + var A = intersection("A", origin); + var B = intersection("B", origin.moveEastMeters(100)); + var C = intersection("C", origin.moveEastMeters(200)); + var D = intersection("D", origin.moveEastMeters(300)); + + biStreet(A, B, 100); + biStreet(B, C, 100); + biStreet(C, D, 100); + + isolatedStop = stop("IsolatedStop", isolated.toWgsCoordinate()); + stopA = stop("StopA", A.toWgsCoordinate()); + stopB = stop("StopB", B.toWgsCoordinate()); + stopC = stop("StopC", C.toWgsCoordinate()); + stopD = stop("StopD", D.toWgsCoordinate()); + + biLink(A, stopA); + biLink(B, stopB); + biLink(C, stopC); + biLink(D, stopD); + } + } + ); + } + + @Test + void testIsolatedStop() { + var durationLimit = Duration.ofMinutes(10); + var maxStopCount = 0; + var finder = new StreetNearbyStopFinder(durationLimit, maxStopCount, null); + + var nearbyStops = finder.findNearbyStops( + isolatedStop, + new RouteRequest(), + new StreetRequest(), + false + ); + + assertThat(nearbyStops).hasSize(1); + var nearbyStop = nearbyStops.stream().findFirst().get(); + assertZeroDistanceStop(isolatedStop, nearbyStop); + } + + @Test + void testMultipleStops() { + var durationLimit = Duration.ofMinutes(10); + var maxStopCount = 0; + var finder = new StreetNearbyStopFinder(durationLimit, maxStopCount, null); + + var sortedNearbyStops = sort( + finder.findNearbyStops(stopA, new RouteRequest(), new StreetRequest(), false) + ); + + assertThat(sortedNearbyStops).hasSize(4); + assertZeroDistanceStop(stopA, sortedNearbyStops.get(0)); + assertStopAtDistance(stopB, 100, sortedNearbyStops.get(1)); + assertStopAtDistance(stopC, 200, sortedNearbyStops.get(2)); + assertStopAtDistance(stopD, 300, sortedNearbyStops.get(3)); + } + + @Test + @Disabled("Currently disabled because of a bug in stop counting") + void testMaxStopCount() { + var durationLimit = Duration.ofMinutes(10); + var maxStopCount = 2; + var finder = new StreetNearbyStopFinder(durationLimit, maxStopCount, null); + + var sortedNearbyStops = sort( + finder.findNearbyStops(stopA, new RouteRequest(), new StreetRequest(), false) + ); + + assertThat(sortedNearbyStops).hasSize(2); + assertZeroDistanceStop(stopA, sortedNearbyStops.get(0)); + assertStopAtDistance(stopB, 100, sortedNearbyStops.get(1)); + } + + @Test + void testDurationLimit() { + // If we only allow walk for 101 seconds and speed is 1 m/s we should only be able to reach + // one extra stop. + var durationLimit = Duration.ofSeconds(101); + var maxStopCount = 0; + var routeRequest = new RouteRequest() + .withPreferences(b -> b.withWalk(walkPreferences -> walkPreferences.withSpeed(1.0))); + + var finder = new StreetNearbyStopFinder(durationLimit, maxStopCount, null); + var sortedNearbyStops = sort( + finder.findNearbyStops(stopA, routeRequest, new StreetRequest(), false) + ); + + assertThat(sortedNearbyStops).hasSize(2); + assertZeroDistanceStop(stopA, sortedNearbyStops.get(0)); + assertStopAtDistance(stopB, 100, sortedNearbyStops.get(1)); + } + + @Test + void testIgnoreStops() { + var durationLimit = Duration.ofMinutes(10); + var maxStopCount = 0; + Set ignore = Set.of(stopA, stopB); + var finder = new StreetNearbyStopFinder(durationLimit, maxStopCount, null, ignore); + + var sortedNearbyStops = sort( + finder.findNearbyStops(Set.of(stopA), new RouteRequest(), new StreetRequest(), false) + ); + + assertThat(sortedNearbyStops).hasSize(2); + assertStopAtDistance(stopC, 200, sortedNearbyStops.get(0)); + assertStopAtDistance(stopD, 300, sortedNearbyStops.get(1)); + } + + @Test + @Disabled("Currently disabled because of a bug in stop counting") + void testIgnoreStopsWithMaxStops() { + var durationLimit = Duration.ofMinutes(10); + var maxStopCount = 1; + Set ignore = Set.of(stopA, stopB); + var finder = new StreetNearbyStopFinder(durationLimit, maxStopCount, null, ignore); + + var sortedNearbyStops = sort( + finder.findNearbyStops(Set.of(stopA), new RouteRequest(), new StreetRequest(), false) + ); + + assertThat(sortedNearbyStops).hasSize(1); + assertStopAtDistance(stopC, 200, sortedNearbyStops.get(0)); + } + + private List sort(Collection stops) { + return stops.stream().sorted(Comparator.comparing(x -> x.distance)).toList(); + } + + /** + * Verify that the nearby stop is zero distance and corresponds to the expected vertex + */ + private void assertZeroDistanceStop(TransitStopVertex expected, NearbyStop nearbyStop) { + assertEquals(expected.getStop(), nearbyStop.stop); + assertEquals(0, nearbyStop.distance); + assertEquals(0, nearbyStop.edges.size()); + assertEquals(expected, nearbyStop.state.getVertex()); + assertNull(nearbyStop.state.getBackState()); + } + + /** + * Verify that the nearby stop is at a specific distance and corresponds to the expected vertex + */ + private void assertStopAtDistance( + TransitStopVertex expected, + double expectedDistance, + NearbyStop nearbyStop + ) { + assertEquals(expected.getStop(), nearbyStop.stop); + assertEquals(expectedDistance, nearbyStop.distance); + assertEquals(expected, nearbyStop.state.getVertex()); + assertFalse(nearbyStop.edges.isEmpty()); + assertNotNull(nearbyStop.state.getBackState()); + } +} diff --git a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java index 091652a2dbf..043dba98ba0 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/osm/OsmModuleTest.java @@ -15,7 +15,6 @@ import java.util.Locale; import java.util.Set; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; import org.opentripplanner.astar.model.GraphPath; import org.opentripplanner.framework.i18n.LocalizedString; @@ -323,7 +322,6 @@ void testBarrierAtEnd() { assertEquals(barrier.getBarrierPermissions(), ALL); } - @Nonnull private Graph buildParkingLots() { var graph = new Graph(); var providers = Stream diff --git a/src/test/java/org/opentripplanner/gtfs/GenerateTripPatternsOperationTest.java b/src/test/java/org/opentripplanner/gtfs/GenerateTripPatternsOperationTest.java new file mode 100644 index 00000000000..74819deabc2 --- /dev/null +++ b/src/test/java/org/opentripplanner/gtfs/GenerateTripPatternsOperationTest.java @@ -0,0 +1,305 @@ +package org.opentripplanner.gtfs; + +import static org.opentripplanner.transit.model._data.TransitModelForTest.trip; + +import java.util.Collection; +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +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.graph_builder.issue.api.DataImportIssueStore; +import org.opentripplanner.graph_builder.issue.service.DefaultDataImportIssueStore; +import org.opentripplanner.graph_builder.issues.TripDegenerate; +import org.opentripplanner.graph_builder.issues.TripUndefinedService; +import org.opentripplanner.graph_builder.module.geometry.GeometryProcessor; +import org.opentripplanner.model.StopTime; +import org.opentripplanner.model.impl.OtpTransitServiceBuilder; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.basic.TransitMode; +import org.opentripplanner.transit.model.framework.Deduplicator; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.site.RegularStop; +import org.opentripplanner.transit.model.timetable.Direction; +import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.service.StopModel; + +class GenerateTripPatternsOperationTest { + + private static StopModel stopModel; + private static RegularStop stopA; + private static RegularStop stopB; + private static RegularStop stopC; + private static Trip trip1; + private static Trip trip2; + private static Trip trip3; + private static Trip trip4; + private static Trip trip5; + private static StopTime stopTimeA; + private static StopTime stopTimeB; + private static StopTime stopTimeC; + + private Deduplicator deduplicator; + private DataImportIssueStore issueStore; + private OtpTransitServiceBuilder transitServiceBuilder; + private GeometryProcessor geometryProcessor; + + @BeforeAll + static void setupClass() { + TransitModelForTest transitModelForTest = TransitModelForTest.of(); + stopA = transitModelForTest.stop("stopA").build(); + stopB = transitModelForTest.stop("stopB").build(); + stopC = transitModelForTest.stop("stopC").build(); + stopModel = + transitModelForTest + .stopModelBuilder() + .withRegularStop(stopA) + .withRegularStop(stopB) + .withRegularStop(stopC) + .build(); + + stopTimeA = new StopTime(); + stopTimeA.setStop(stopA); + stopTimeB = new StopTime(); + stopTimeB.setStop(stopB); + stopTimeC = new StopTime(); + stopTimeC.setStop(stopC); + + FeedScopedId serviceId1 = TransitModelForTest.id("SERVICE_ID_1"); + trip1 = + trip("TRIP_ID_1") + .withServiceId(serviceId1) + .withMode(TransitMode.RAIL) + .withNetexSubmode("SUBMODE_1") + .withDirection(Direction.INBOUND) + .build(); + + // same route, mode, submode and direction as trip1 + FeedScopedId serviceId2 = TransitModelForTest.id("SERVICE_ID_2"); + trip2 = + trip("TRIP_ID_2") + .withServiceId(serviceId2) + .withRoute(trip1.getRoute()) + .withMode(trip1.getMode()) + .withNetexSubmode(trip1.getNetexSubMode().name()) + .withDirection(trip1.getDirection()) + .build(); + + // same route, direction as trip1, different mode + FeedScopedId serviceId3 = TransitModelForTest.id("SERVICE_ID_3"); + trip3 = + trip("TRIP_ID_3") + .withServiceId(serviceId3) + .withRoute(trip1.getRoute()) + .withMode(TransitMode.BUS) + .withDirection(trip1.getDirection()) + .build(); + + // same route, mode, direction as trip1, different submode + FeedScopedId serviceId4 = TransitModelForTest.id("SERVICE_ID_4"); + trip4 = + trip("TRIP_ID_4") + .withServiceId(serviceId4) + .withRoute(trip1.getRoute()) + .withMode(trip1.getMode()) + .withNetexSubmode("SUMODE_2") + .withDirection(trip1.getDirection()) + .build(); + + // same route, mode as trip1, different direction + FeedScopedId serviceId5 = TransitModelForTest.id("SERVICE_ID_5"); + trip5 = + trip("TRIP_ID_5") + .withServiceId(serviceId5) + .withRoute(trip1.getRoute()) + .withMode(trip1.getMode()) + .withNetexSubmode(trip1.getNetexSubMode().name()) + .withDirection(Direction.OUTBOUND) + .build(); + } + + @BeforeEach + void setup() { + deduplicator = new Deduplicator(); + issueStore = new DefaultDataImportIssueStore(); + transitServiceBuilder = new OtpTransitServiceBuilder(stopModel, issueStore); + double maxStopToShapeSnapDistance = 100; + geometryProcessor = + new GeometryProcessor(transitServiceBuilder, maxStopToShapeSnapDistance, issueStore); + } + + @Test + void testGenerateTripPatternsNoTrip() { + Set calendarServiceIds = Set.of(); + GenerateTripPatternsOperation generateTripPatternsOperation = new GenerateTripPatternsOperation( + transitServiceBuilder, + issueStore, + deduplicator, + calendarServiceIds, + geometryProcessor + ); + generateTripPatternsOperation.run(); + + Assertions.assertTrue(transitServiceBuilder.getTripPatterns().isEmpty()); + Assertions.assertTrue(issueStore.listIssues().isEmpty()); + } + + @Test + void testGenerateTripPatternsTripWithUndefinedService() { + transitServiceBuilder.getTripsById().computeIfAbsent(trip1.getId(), feedScopedId -> trip1); + Set calendarServiceIds = Set.of(); + + GenerateTripPatternsOperation generateTripPatternsOperation = new GenerateTripPatternsOperation( + transitServiceBuilder, + issueStore, + deduplicator, + calendarServiceIds, + geometryProcessor + ); + generateTripPatternsOperation.run(); + + Assertions.assertTrue(transitServiceBuilder.getTripPatterns().isEmpty()); + Assertions.assertFalse(issueStore.listIssues().isEmpty()); + Assertions.assertInstanceOf(TripUndefinedService.class, issueStore.listIssues().getFirst()); + } + + @Test + void testGenerateTripPatternsDegeneratedTrip() { + transitServiceBuilder.getTripsById().computeIfAbsent(trip1.getId(), feedScopedId -> trip1); + Set calendarServiceIds = Set.of(trip1.getServiceId()); + + GenerateTripPatternsOperation generateTripPatternsOperation = new GenerateTripPatternsOperation( + transitServiceBuilder, + issueStore, + deduplicator, + calendarServiceIds, + geometryProcessor + ); + generateTripPatternsOperation.run(); + + Assertions.assertTrue(transitServiceBuilder.getTripPatterns().isEmpty()); + Assertions.assertFalse(issueStore.listIssues().isEmpty()); + Assertions.assertInstanceOf(TripDegenerate.class, issueStore.listIssues().getFirst()); + } + + @Test + void testGenerateTripPatterns() { + transitServiceBuilder.getTripsById().computeIfAbsent(trip1.getId(), feedScopedId -> trip1); + Collection stopTimes = List.of(stopTimeA, stopTimeB); + transitServiceBuilder.getStopTimesSortedByTrip().put(trip1, stopTimes); + Set calendarServiceIds = Set.of(trip1.getServiceId()); + + GenerateTripPatternsOperation generateTripPatternsOperation = new GenerateTripPatternsOperation( + transitServiceBuilder, + issueStore, + deduplicator, + calendarServiceIds, + geometryProcessor + ); + generateTripPatternsOperation.run(); + + Assertions.assertEquals(1, transitServiceBuilder.getTripPatterns().size()); + Assertions.assertTrue(issueStore.listIssues().isEmpty()); + } + + @Test + void testGenerateTripPatterns2TripsSameStops() { + transitServiceBuilder.getTripsById().computeIfAbsent(trip1.getId(), feedScopedId -> trip1); + transitServiceBuilder.getTripsById().computeIfAbsent(trip2.getId(), feedScopedId -> trip2); + Collection stopTimes = List.of(stopTimeA, stopTimeB); + + transitServiceBuilder.getStopTimesSortedByTrip().put(trip1, stopTimes); + transitServiceBuilder.getStopTimesSortedByTrip().put(trip2, stopTimes); + + Set calendarServiceIds = Set.of(trip1.getServiceId(), trip2.getServiceId()); + + GenerateTripPatternsOperation generateTripPatternsOperation = new GenerateTripPatternsOperation( + transitServiceBuilder, + issueStore, + deduplicator, + calendarServiceIds, + geometryProcessor + ); + generateTripPatternsOperation.run(); + + Assertions.assertEquals(1, transitServiceBuilder.getTripPatterns().size()); + Assertions.assertEquals( + 2, + transitServiceBuilder + .getTripPatterns() + .values() + .stream() + .findFirst() + .orElseThrow() + .getScheduledTimetable() + .getTripTimes() + .size() + ); + Assertions.assertTrue(issueStore.listIssues().isEmpty()); + } + + @Test + void testGenerateTripPatterns2TripsDifferentStops() { + transitServiceBuilder.getTripsById().computeIfAbsent(trip1.getId(), feedScopedId -> trip1); + transitServiceBuilder.getTripsById().computeIfAbsent(trip2.getId(), feedScopedId -> trip2); + Collection stopTimesTrip1 = List.of(stopTimeA, stopTimeB); + Collection stopTimesTrip2 = List.of(stopTimeA, stopTimeC); + + transitServiceBuilder.getStopTimesSortedByTrip().put(trip1, stopTimesTrip1); + transitServiceBuilder.getStopTimesSortedByTrip().put(trip2, stopTimesTrip2); + + Set calendarServiceIds = Set.of(trip1.getServiceId(), trip2.getServiceId()); + + GenerateTripPatternsOperation generateTripPatternsOperation = new GenerateTripPatternsOperation( + transitServiceBuilder, + issueStore, + deduplicator, + calendarServiceIds, + geometryProcessor + ); + generateTripPatternsOperation.run(); + + Assertions.assertEquals(2, transitServiceBuilder.getTripPatterns().size()); + Assertions.assertTrue(issueStore.listIssues().isEmpty()); + } + + static List testCases() { + return List.of( + // trips with different modes + Arguments.of(trip1, trip3), + // trips with different sub-modes + Arguments.of(trip1, trip4), + // trips with different directions + Arguments.of(trip1, trip5) + ); + } + + @ParameterizedTest + @MethodSource("testCases") + void testGenerateDifferentTripPatterns(Trip t1, Trip t2) { + transitServiceBuilder.getTripsById().computeIfAbsent(t1.getId(), feedScopedId -> t1); + transitServiceBuilder.getTripsById().computeIfAbsent(t2.getId(), feedScopedId -> t2); + Collection stopTimes = List.of(stopTimeA, stopTimeB); + + transitServiceBuilder.getStopTimesSortedByTrip().put(t1, stopTimes); + transitServiceBuilder.getStopTimesSortedByTrip().put(t2, stopTimes); + + Set calendarServiceIds = Set.of(t1.getServiceId(), t2.getServiceId()); + + GenerateTripPatternsOperation generateTripPatternsOperation = new GenerateTripPatternsOperation( + transitServiceBuilder, + issueStore, + deduplicator, + calendarServiceIds, + geometryProcessor + ); + generateTripPatternsOperation.run(); + + Assertions.assertEquals(2, transitServiceBuilder.getTripPatterns().size()); + Assertions.assertTrue(issueStore.listIssues().isEmpty()); + } +} diff --git a/src/test/java/org/opentripplanner/gtfs/interlining/InterlineProcessorTest.java b/src/test/java/org/opentripplanner/gtfs/interlining/InterlineProcessorTest.java index a1aa4f9d753..e14a33cd276 100644 --- a/src/test/java/org/opentripplanner/gtfs/interlining/InterlineProcessorTest.java +++ b/src/test/java/org/opentripplanner/gtfs/interlining/InterlineProcessorTest.java @@ -163,13 +163,12 @@ private static TripPattern tripPattern(String tripId, String blockId, String ser ); var stopPattern = new StopPattern(stopTimes); - var tp = TripPattern + var tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator()); + return TripPattern .of(TransitModelForTest.id(tripId)) .withRoute(trip.getRoute()) .withStopPattern(stopPattern) + .withScheduledTimeTableBuilder(builder -> builder.addTripTimes(tripTimes)) .build(); - var tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator()); - tp.add(tripTimes); - return tp; } } diff --git a/src/test/java/org/opentripplanner/gtfs/mapping/FareTransferRuleMapperTest.java b/src/test/java/org/opentripplanner/gtfs/mapping/FareTransferRuleMapperTest.java index dc924f6f0aa..eda714257cd 100644 --- a/src/test/java/org/opentripplanner/gtfs/mapping/FareTransferRuleMapperTest.java +++ b/src/test/java/org/opentripplanner/gtfs/mapping/FareTransferRuleMapperTest.java @@ -6,7 +6,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.FareProduct; @@ -65,7 +64,6 @@ void transferRuleWithLegGroup() { assertEquals(groupId2.getId(), transferRule.toLegGroup().getId()); } - @Nonnull private FareProduct fareProduct() { var fareProduct = new FareProduct(); fareProduct.setId(id); diff --git a/src/test/java/org/opentripplanner/gtfs/mapping/LocationGroupMapperTest.java b/src/test/java/org/opentripplanner/gtfs/mapping/LocationGroupMapperTest.java index 3a3c3f28649..67b18ead0c5 100644 --- a/src/test/java/org/opentripplanner/gtfs/mapping/LocationGroupMapperTest.java +++ b/src/test/java/org/opentripplanner/gtfs/mapping/LocationGroupMapperTest.java @@ -4,7 +4,6 @@ import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.LocationGroup; @@ -45,7 +44,6 @@ void map() { ); } - @Nonnull private static AgencyAndId id(String id) { return new AgencyAndId("1", id); } diff --git a/src/test/java/org/opentripplanner/gtfs/mapping/StopAreaMapperTest.java b/src/test/java/org/opentripplanner/gtfs/mapping/StopAreaMapperTest.java deleted file mode 100644 index d58bcfcdd31..00000000000 --- a/src/test/java/org/opentripplanner/gtfs/mapping/StopAreaMapperTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.opentripplanner.gtfs.mapping; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.opentripplanner.graph_builder.issue.api.DataImportIssueStore.NOOP; - -import java.util.Set; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.Test; -import org.onebusaway.gtfs.model.AgencyAndId; -import org.onebusaway.gtfs.model.Area; -import org.onebusaway.gtfs.model.Location; -import org.onebusaway.gtfs.model.Stop; -import org.onebusaway.gtfs.model.StopArea; -import org.opentripplanner._support.geometry.Polygons; -import org.opentripplanner.transit.service.StopModel; - -class StopAreaMapperTest { - - private static final String NAME = "Floxjam"; - private static final AgencyAndId AREA_ID = agencyAndId("flox"); - - @Test - void map() { - var stopModel = StopModel.of(); - var stopMapper = new StopMapper(new TranslationHelper(), ignored -> null, stopModel); - var locationMapper = new LocationMapper(stopModel, NOOP); - var mapper = new StopAreaMapper(stopMapper, locationMapper, stopModel); - - var area = new Area(); - area.setId(AREA_ID); - area.setName(NAME); - - var stop1 = stop("stop1"); - var stop2 = stop("stop2"); - var location = location("location"); - - var stopArea = new StopArea(); - stopArea.setArea(area); - stopArea.addLocation(stop1); - stopArea.addLocation(stop2); - stopArea.addLocation(location); - var areaStop = mapper.map(stopArea); - - assertEquals(NAME, areaStop.getName().toString()); - var stopIds = areaStop - .getChildLocations() - .stream() - .map(l -> l.getId().toString()) - .collect(Collectors.toSet()); - assertEquals(Set.of("1:location", "1:stop1", "1:stop2"), stopIds); - } - - private static Stop stop(String id) { - var stop = new Stop(); - stop.setId(agencyAndId(id)); - stop.setLat(1); - stop.setLon(2); - stop.setName("A stop"); - return stop; - } - - private static Location location(String id) { - var stop = new Location(); - stop.setId(agencyAndId(id)); - stop.setName("A stop"); - stop.setGeometry(Polygons.toGeoJson(Polygons.BERLIN)); - return stop; - } - - @Nonnull - private static AgencyAndId agencyAndId(String id) { - return new AgencyAndId("1", id); - } -} diff --git a/src/test/java/org/opentripplanner/gtfs/mapping/StopTimeMapperTest.java b/src/test/java/org/opentripplanner/gtfs/mapping/StopTimeMapperTest.java index e45f7a8ff0d..d17c22c4abe 100644 --- a/src/test/java/org/opentripplanner/gtfs/mapping/StopTimeMapperTest.java +++ b/src/test/java/org/opentripplanner/gtfs/mapping/StopTimeMapperTest.java @@ -85,17 +85,11 @@ public class StopTimeMapperTest { locationMapper, stopModelBuilder ); - private final StopAreaMapper stopAreaMapper = new StopAreaMapper( - stopMapper, - locationMapper, - stopModelBuilder - ); private final TranslationHelper translationHelper = new TranslationHelper(); private final StopTimeMapper subject = new StopTimeMapper( stopMapper, locationMapper, locationGroupMapper, - stopAreaMapper, new TripMapper( new RouteMapper(new AgencyMapper(FEED_ID), ISSUE_STORE, translationHelper), new DirectionMapper(ISSUE_STORE), diff --git a/src/test/java/org/opentripplanner/gtfs/mapping/TransferMapperTest.java b/src/test/java/org/opentripplanner/gtfs/mapping/TransferMapperTest.java index 3581cca9ac8..3c5a1eff42a 100644 --- a/src/test/java/org/opentripplanner/gtfs/mapping/TransferMapperTest.java +++ b/src/test/java/org/opentripplanner/gtfs/mapping/TransferMapperTest.java @@ -56,11 +56,6 @@ public class TransferMapperTest { LOCATION_MAPPER, STOP_MODEL_BUILDER ); - private static final StopAreaMapper STOP_AREA_MAPPER = new StopAreaMapper( - STOP_MAPPER, - LOCATION_MAPPER, - STOP_MODEL_BUILDER - ); private static StopTimeMapper STOP_TIME_MAPPER; private static final Integer ID = 45; @@ -99,7 +94,6 @@ void prepare() { STOP_MAPPER, LOCATION_MAPPER, LOCATION_GROUP_MAPPER, - STOP_AREA_MAPPER, new TripMapper( new RouteMapper(new AgencyMapper(FEED_ID), ISSUE_STORE, TRANSLATION_HELPER), new DirectionMapper(ISSUE_STORE), diff --git a/src/test/java/org/opentripplanner/model/TimetableSnapshotTest.java b/src/test/java/org/opentripplanner/model/TimetableSnapshotTest.java index 0a737630a5b..ffdfc027b76 100644 --- a/src/test/java/org/opentripplanner/model/TimetableSnapshotTest.java +++ b/src/test/java/org/opentripplanner/model/TimetableSnapshotTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; @@ -52,9 +53,9 @@ public static void setUp() throws Exception { @Test public void testCompare() { - Timetable orig = new Timetable(null); - Timetable a = new Timetable(orig, LocalDate.now(timeZone).minusDays(1)); - Timetable b = new Timetable(orig, LocalDate.now(timeZone)); + Timetable orig = Timetable.of().build(); + Timetable a = orig.copyOf().withServiceDate(LocalDate.now(timeZone).minusDays(1)).build(); + Timetable b = orig.copyOf().withServiceDate(LocalDate.now(timeZone)).build(); assertTrue(new TimetableSnapshot.SortedTimetableComparator().compare(a, b) < 0); } @@ -106,52 +107,50 @@ public void testResolve() { @Test public void testUpdate() { - assertThrows( - ConcurrentModificationException.class, - () -> { - LocalDate today = LocalDate.now(timeZone); - LocalDate yesterday = today.minusDays(1); - TripPattern pattern = patternIndex.get(new FeedScopedId(feedId, "1.1")); + LocalDate today = LocalDate.now(timeZone); + LocalDate yesterday = today.minusDays(1); + TripPattern pattern = patternIndex.get(new FeedScopedId(feedId, "1.1")); - TimetableSnapshot resolver = new TimetableSnapshot(); - Timetable origNow = resolver.resolve(pattern, today); + TimetableSnapshot resolver = new TimetableSnapshot(); + Timetable origNow = resolver.resolve(pattern, today); - TripDescriptor.Builder tripDescriptorBuilder = TripDescriptor.newBuilder(); + TripDescriptor.Builder tripDescriptorBuilder = TripDescriptor.newBuilder(); - tripDescriptorBuilder.setTripId("1.1"); - tripDescriptorBuilder.setScheduleRelationship(ScheduleRelationship.SCHEDULED); + tripDescriptorBuilder.setTripId("1.1"); + tripDescriptorBuilder.setScheduleRelationship(ScheduleRelationship.SCHEDULED); - TripUpdate.Builder tripUpdateBuilder = TripUpdate.newBuilder(); + TripUpdate.Builder tripUpdateBuilder = TripUpdate.newBuilder(); - tripUpdateBuilder.setTrip(tripDescriptorBuilder); + tripUpdateBuilder.setTrip(tripDescriptorBuilder); - var stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0); - stopTimeUpdateBuilder.setStopSequence(2); - stopTimeUpdateBuilder.setScheduleRelationship( - TripUpdate.StopTimeUpdate.ScheduleRelationship.SCHEDULED - ); - stopTimeUpdateBuilder.setDeparture( - TripUpdate.StopTimeEvent.newBuilder().setDelay(5).build() - ); + var stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0); + stopTimeUpdateBuilder.setStopSequence(2); + stopTimeUpdateBuilder.setScheduleRelationship( + TripUpdate.StopTimeUpdate.ScheduleRelationship.SCHEDULED + ); + stopTimeUpdateBuilder.setDeparture(TripUpdate.StopTimeEvent.newBuilder().setDelay(5).build()); - TripUpdate tripUpdate = tripUpdateBuilder.build(); + TripUpdate tripUpdate = tripUpdateBuilder.build(); - // new timetable for today - updateResolver(resolver, pattern, tripUpdate, today); - Timetable updatedNow = resolver.resolve(pattern, today); - assertNotSame(origNow, updatedNow); + // new timetable for today + updateResolver(resolver, pattern, tripUpdate, today); + Timetable updatedNow = resolver.resolve(pattern, today); + assertNotSame(origNow, updatedNow); - // reuse timetable for today - updateResolver(resolver, pattern, tripUpdate, today); - assertEquals(updatedNow, resolver.resolve(pattern, today)); + // a new timetable instance is created for today + updateResolver(resolver, pattern, tripUpdate, today); + assertNotEquals(updatedNow, resolver.resolve(pattern, today)); - // create new timetable for tomorrow - updateResolver(resolver, pattern, tripUpdate, yesterday); - assertNotSame(origNow, resolver.resolve(pattern, yesterday)); - assertNotSame(updatedNow, resolver.resolve(pattern, yesterday)); + // create new timetable for tomorrow + updateResolver(resolver, pattern, tripUpdate, yesterday); + assertNotSame(origNow, resolver.resolve(pattern, yesterday)); + assertNotSame(updatedNow, resolver.resolve(pattern, yesterday)); - // exception if we try to modify a snapshot - TimetableSnapshot snapshot = resolver.commit(); + // exception if we try to modify a snapshot + TimetableSnapshot snapshot = resolver.commit(); + assertThrows( + ConcurrentModificationException.class, + () -> { updateResolver(snapshot, pattern, tripUpdate, yesterday); } ); diff --git a/src/test/java/org/opentripplanner/model/TimetableTest.java b/src/test/java/org/opentripplanner/model/TimetableTest.java index 9e6a7467dc5..dca4d3e828d 100644 --- a/src/test/java/org/opentripplanner/model/TimetableTest.java +++ b/src/test/java/org/opentripplanner/model/TimetableTest.java @@ -22,7 +22,6 @@ import java.util.HashMap; import java.util.Map; import java.util.function.BiConsumer; -import javax.annotation.Nonnull; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -190,7 +189,7 @@ public void update() { result.ifSuccess(p -> { var updatedTripTimes = p.getTripTimes(); assertNotNull(updatedTripTimes); - timetable.setTripTimes(trip_1_1_index, updatedTripTimes); + timetable = timetable.copyOf().addOrUpdateTripTimes(updatedTripTimes).build(); assertEquals(20 * 60 + 120, timetable.getTripTimes(trip_1_1_index).getArrivalTime(2)); }); @@ -217,7 +216,7 @@ public void update() { result.ifSuccess(p -> { var updatedTripTimes = p.getTripTimes(); assertNotNull(updatedTripTimes); - timetable.setTripTimes(trip_1_1_index, updatedTripTimes); + timetable = timetable.copyOf().addOrUpdateTripTimes(updatedTripTimes).build(); }); // update trip arrival time only @@ -246,7 +245,7 @@ public void update() { result.ifSuccess(p -> { var updatedTripTimes = p.getTripTimes(); assertNotNull(updatedTripTimes); - timetable.setTripTimes(trip_1_1_index, updatedTripTimes); + timetable = timetable.copyOf().addOrUpdateTripTimes(updatedTripTimes).build(); }); // update trip departure time only @@ -273,7 +272,7 @@ public void update() { result.ifSuccess(p -> { var updatedTripTimes = p.getTripTimes(); assertNotNull(updatedTripTimes); - timetable.setTripTimes(trip_1_1_index, updatedTripTimes); + timetable = timetable.copyOf().addOrUpdateTripTimes(updatedTripTimes).build(); }); // update trip using stop id @@ -299,7 +298,7 @@ public void update() { result.ifSuccess(p -> { var updatedTripTimes = p.getTripTimes(); assertNotNull(updatedTripTimes); - timetable.setTripTimes(trip_1_1_index, updatedTripTimes); + timetable = timetable.copyOf().addOrUpdateTripTimes(updatedTripTimes).build(); }); } @@ -975,7 +974,6 @@ private static void testInvalidStopTime( }); } - @Nonnull private static StopTimeUpdate emptyStopTime( int sequence, BiConsumer setEmptyEvent diff --git a/src/test/java/org/opentripplanner/model/impl/OtpTransitServiceBuilderLimitPeriodTest.java b/src/test/java/org/opentripplanner/model/impl/OtpTransitServiceBuilderLimitPeriodTest.java index 5d1fef21271..27e6e56c645 100644 --- a/src/test/java/org/opentripplanner/model/impl/OtpTransitServiceBuilderLimitPeriodTest.java +++ b/src/test/java/org/opentripplanner/model/impl/OtpTransitServiceBuilderLimitPeriodTest.java @@ -1,6 +1,7 @@ package org.opentripplanner.model.impl; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.LocalDate; @@ -22,6 +23,7 @@ import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.network.StopPattern; import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.network.TripPatternBuilder; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.timetable.Direction; import org.opentripplanner.transit.model.timetable.Trip; @@ -141,16 +143,27 @@ public void testLimitPeriod() { assertTrue(patterns.contains(patternInT1), patterns.toString()); assertTrue(patterns.contains(patternInT2), patterns.toString()); - // Verify trips in pattern (one trip is removed from patternInT1) - assertEquals(1, patternInT1.scheduledTripsAsStream().count()); - assertEquals(tripCSIn, patternInT1.scheduledTripsAsStream().findFirst().orElseThrow()); - - // Verify trips in pattern is unchanged (one trip) + // Verify patternInT1 is replaced by a copy that contains one less trip + TripPattern copyOfTripPattern1 = subject + .getTripPatterns() + .values() + .stream() + .filter(p -> p.getId().equals(patternInT1.getId())) + .findFirst() + .orElseThrow(); + assertNotSame(patternInT1, copyOfTripPattern1); + assertEquals(1, copyOfTripPattern1.scheduledTripsAsStream().count()); + assertEquals(tripCSIn, copyOfTripPattern1.scheduledTripsAsStream().findFirst().orElseThrow()); + + // Verify trips in patternInT2 is unchanged (one trip) assertEquals(1, patternInT2.scheduledTripsAsStream().count()); - // Verify scheduledTimetable trips (one trip is removed from patternInT1) - assertEquals(1, patternInT1.getScheduledTimetable().getTripTimes().size()); - assertEquals(tripCSIn, patternInT1.getScheduledTimetable().getTripTimes().get(0).getTrip()); + // Verify scheduledTimetable trips (one trip is removed from the copy of patternInT1) + assertEquals(1, copyOfTripPattern1.getScheduledTimetable().getTripTimes().size()); + assertEquals( + tripCSIn, + copyOfTripPattern1.getScheduledTimetable().getTripTimes().get(0).getTrip() + ); // Verify scheduledTimetable trips in pattern is unchanged (one trip) assertEquals(1, patternInT2.getScheduledTimetable().getTripTimes().size()); @@ -186,16 +199,17 @@ private TripPattern createTripPattern(Collection trips) { FeedScopedId patternId = TransitModelForTest.id( trips.stream().map(t -> t.getId().getId()).collect(Collectors.joining(":")) ); - TripPattern p = TripPattern + TripPatternBuilder tpb = TripPattern .of(patternId) .withRoute(route) - .withStopPattern(STOP_PATTERN) - .build(); + .withStopPattern(STOP_PATTERN); for (Trip trip : trips) { - p.add(TripTimesFactory.tripTimes(trip, STOP_TIMES, DEDUPLICATOR)); + tpb.withScheduledTimeTableBuilder(builder -> + builder.addTripTimes(TripTimesFactory.tripTimes(trip, STOP_TIMES, DEDUPLICATOR)) + ); } - return p; + return tpb.build(); } private Trip createTrip(String id, FeedScopedId serviceId) { diff --git a/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java b/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java index edaafabd753..ec04d9f6d96 100644 --- a/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java +++ b/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java @@ -482,13 +482,13 @@ public TestItineraryBuilder transit( stopTimes.add(toStopTime); StopPattern stopPattern = new StopPattern(stopTimes); + final TripTimes tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator()); TripPattern tripPattern = TripPattern .of(route.getId()) .withRoute(route) .withStopPattern(stopPattern) + .withScheduledTimeTableBuilder(builder -> builder.addTripTimes(tripTimes)) .build(); - final TripTimes tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator()); - tripPattern.add(tripTimes); ScheduledTransitLeg leg; diff --git a/src/test/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReferenceTest.java b/src/test/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReferenceTest.java index 480639e5bda..315b12dfefc 100644 --- a/src/test/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReferenceTest.java +++ b/src/test/java/org/opentripplanner/model/plan/legreference/ScheduledTransitLegReferenceTest.java @@ -11,7 +11,6 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; -import org.opentripplanner.model.Timetable; import org.opentripplanner.model.calendar.CalendarServiceData; import org.opentripplanner.model.plan.PlanTestConstants; import org.opentripplanner.model.plan.ScheduledTransitLeg; @@ -54,23 +53,23 @@ static void buildTransitService() { stop4 = TEST_MODEL.stop("STOP4", 0, 0).withParentStation(parentStation).build(); // build transit data + Trip trip = TransitModelForTest.trip("1").build(); + var tripTimes = TripTimesFactory.tripTimes( + trip, + TEST_MODEL.stopTimesEvery5Minutes(5, trip, PlanTestConstants.T11_00), + new Deduplicator() + ); + tripTimes.setServiceCode(SERVICE_CODE); TripPattern tripPattern = TransitModelForTest .tripPattern("1", TransitModelForTest.route(id("1")).build()) .withStopPattern(TransitModelForTest.stopPattern(stop1, stop2, stop3)) + .withScheduledTimeTableBuilder(builder -> builder.addTripTimes(tripTimes)) .build(); - Timetable timetable = tripPattern.getScheduledTimetable(); - Trip trip = TransitModelForTest.trip("1").build(); + tripId = trip.getId(); stopIdAtPosition0 = tripPattern.getStop(0).getId(); stopIdAtPosition1 = tripPattern.getStop(1).getId(); stopIdAtPosition2 = tripPattern.getStop(2).getId(); - var tripTimes = TripTimesFactory.tripTimes( - trip, - TEST_MODEL.stopTimesEvery5Minutes(5, trip, PlanTestConstants.T11_00), - new Deduplicator() - ); - tripTimes.setServiceCode(SERVICE_CODE); - timetable.addTripTimes(tripTimes); // build transit model StopModel stopModel = TEST_MODEL diff --git a/src/test/java/org/opentripplanner/netex/NetexEpipBundleSmokeTest.java b/src/test/java/org/opentripplanner/netex/NetexEpipBundleSmokeTest.java index b89e6883951..72e750c055f 100644 --- a/src/test/java/org/opentripplanner/netex/NetexEpipBundleSmokeTest.java +++ b/src/test/java/org/opentripplanner/netex/NetexEpipBundleSmokeTest.java @@ -84,7 +84,7 @@ private static FeedScopedId fId(String id) { private void assertAgencies(Collection agencies) { assertEquals(3, agencies.size()); - Agency a = list(agencies).get(0); + Agency a = list(agencies).getFirst(); assertEquals("DE::Authority:41::", a.getId().getId()); assertEquals("HOCHBAHN, Bus", a.getName()); assertNull(a.getUrl()); @@ -146,7 +146,7 @@ private void assertTripPatterns(Collection patterns) { p.getStops().toString() ); List trips = p.scheduledTripsAsStream().toList(); - assertEquals("Trip{HH:DE::ServiceJourney:36439031_0:: X86}", trips.get(0).toString()); + assertEquals("Trip{HH:DE::ServiceJourney:36439062_0:: X86}", trips.getFirst().toString()); assertEquals(55, trips.size()); assertEquals(4, patterns.size()); } @@ -176,12 +176,12 @@ private void assertServiceIds(Collection trips, Collection s private void assetServiceCalendar(CalendarServiceData cal) { ArrayList sIds = new ArrayList<>(cal.getServiceIds()); assertEquals(2, sIds.size()); - FeedScopedId serviceId1 = sIds.get(0); + FeedScopedId serviceId1 = sIds.getFirst(); List dates = cal.getServiceDatesForServiceId(serviceId1); - assertEquals("2023-02-02", dates.get(0).toString()); - assertEquals("2023-12-08", dates.get(dates.size() - 1).toString()); + assertEquals("2023-02-02", dates.getFirst().toString()); + assertEquals("2023-12-08", dates.getLast().toString()); assertEquals(214, dates.size()); } } diff --git a/src/test/java/org/opentripplanner/netex/mapping/MappingSupport.java b/src/test/java/org/opentripplanner/netex/mapping/MappingSupport.java index 49f6d88c26b..cbabe19ad2d 100644 --- a/src/test/java/org/opentripplanner/netex/mapping/MappingSupport.java +++ b/src/test/java/org/opentripplanner/netex/mapping/MappingSupport.java @@ -1,7 +1,6 @@ package org.opentripplanner.netex.mapping; import jakarta.xml.bind.JAXBElement; -import javax.annotation.Nonnull; import javax.xml.namespace.QName; import org.opentripplanner.netex.mapping.support.FeedScopedIdFactory; import org.rutebanken.netex.model.VersionOfObjectRefStructure; @@ -54,7 +53,7 @@ public static T createRef(String id, Cla * @return the value wrapped in a JAXBElement */ @SuppressWarnings("unchecked") - public static JAXBElement createJaxbElement(@Nonnull T value) { + public static JAXBElement createJaxbElement(T value) { return new JAXBElement<>(new QName("x"), (Class) value.getClass(), value); } } diff --git a/src/test/java/org/opentripplanner/netex/mapping/StationMapperTest.java b/src/test/java/org/opentripplanner/netex/mapping/StationMapperTest.java index b100a38782a..8b09fe41b2a 100644 --- a/src/test/java/org/opentripplanner/netex/mapping/StationMapperTest.java +++ b/src/test/java/org/opentripplanner/netex/mapping/StationMapperTest.java @@ -5,6 +5,7 @@ import static org.opentripplanner.netex.NetexTestDataSupport.createStopPlace; import java.time.ZoneId; +import java.util.Set; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; @@ -27,6 +28,7 @@ void setUp() { new FeedScopedIdFactory("FEED_ID"), ZoneId.of("UTC"), false, + Set.of(), StopModel.of() ); } diff --git a/src/test/java/org/opentripplanner/netex/mapping/StopAndStationMapperTest.java b/src/test/java/org/opentripplanner/netex/mapping/StopAndStationMapperTest.java index 30ca5cebfa8..f61bef86a19 100644 --- a/src/test/java/org/opentripplanner/netex/mapping/StopAndStationMapperTest.java +++ b/src/test/java/org/opentripplanner/netex/mapping/StopAndStationMapperTest.java @@ -1,16 +1,19 @@ package org.opentripplanner.netex.mapping; +import static com.google.common.truth.Truth.assertThat; import static graphql.Assert.assertFalse; import static graphql.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.opentripplanner.netex.NetexTestDataSupport.createQuay; import static org.opentripplanner.netex.NetexTestDataSupport.createStopPlace; +import static org.rutebanken.netex.model.AllVehicleModesOfTransportEnumeration.TRAM; import java.time.ZoneId; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Set; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -159,7 +162,8 @@ void mapStopPlaceAndQuays() { StopModel.of(), DEFAULT_TIME_ZONE, DataImportIssueStore.NOOP, - false + false, + Set.of() ); stopMapper.mapParentAndChildStops(stopPlaces); @@ -229,7 +233,8 @@ void testMapIsolatedStopPlace(boolean isolated) { StopModel.of(), DEFAULT_TIME_ZONE, DataImportIssueStore.NOOP, - isolated + isolated, + Set.of() ); stopMapper.mapParentAndChildStops(stopPlaces); @@ -279,6 +284,33 @@ void testDuplicateStopIndices() { ); } + @Test + void testRouteToCentroid() { + var routeToCentroidStopPlaceIds = Set.of(MappingSupport.ID_FACTORY.createId("NSR:StopPlace:1")); + StopAndStationMapper stopMapper = new StopAndStationMapper( + MappingSupport.ID_FACTORY, + new HierarchicalVersionMapById<>(), + null, + StopModel.of(), + DEFAULT_TIME_ZONE, + DataImportIssueStore.NOOP, + false, + routeToCentroidStopPlaceIds + ); + + stopMapper.mapParentAndChildStops( + List.of(createStopPlace("NSR:StopPlace:1", "A", "1", 59.1, 10.0, TRAM)) + ); + stopMapper.mapParentAndChildStops( + List.of(createStopPlace("NSR:StopPlace:2", "B", "1", 59.2, 10.0, TRAM)) + ); + + var stations = stopMapper.resultStations; + assertThat(stations).hasSize(2); + assertTrue(stations.get(0).shouldRouteToCentroid()); + assertFalse(stations.get(1).shouldRouteToCentroid()); + } + private static StopAndStationMapper createStopAndStationMapper( StopModelBuilder stopModelBuilder ) { @@ -289,7 +321,8 @@ private static StopAndStationMapper createStopAndStationMapper( stopModelBuilder, DEFAULT_TIME_ZONE, DataImportIssueStore.NOOP, - false + false, + Set.of() ); } diff --git a/src/test/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapperTest.java b/src/test/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapperTest.java index 2a8988dda61..745cb5644dc 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapperTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapperTest.java @@ -1,6 +1,7 @@ package org.opentripplanner.openstreetmap.tagmapping; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.street.model.StreetTraversalPermission.ALL; import static org.opentripplanner.street.model.StreetTraversalPermission.PEDESTRIAN; @@ -189,6 +190,15 @@ void bicycleUseSidepath() { assertEquals(4.9, useSidepathBackwardProps.bicycleSafety().back(), epsilon); } + @Test + void slopeOverrides() { + var regular = WayTestData.southeastLaBonitaWay(); + assertFalse(wps.getSlopeOverride(regular)); + + var indoor = WayTestData.southeastLaBonitaWay().addTag("indoor", "yes"); + assertTrue(wps.getSlopeOverride(indoor)); + } + /** * Test that two values are within epsilon of each other. */ diff --git a/src/test/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySetTest.java b/src/test/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySetTest.java index 24360539b6f..c9ec745d914 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySetTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySetTest.java @@ -8,7 +8,6 @@ import static org.opentripplanner.street.model.StreetTraversalPermission.CAR; import static org.opentripplanner.street.model.StreetTraversalPermission.NONE; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.opentripplanner.graph_builder.module.osm.StreetTraversalPermissionPair; @@ -84,7 +83,6 @@ void mixinLeftSide() { assertEquals(expected, wps.getDataForWay(cycleway).bicycleSafety()); } - @Nonnull private static WayPropertySet wps() { var wps = new WayPropertySet(); var source = new OsmTagMapper() { diff --git a/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java b/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java index 5f1617075c4..4f225106ba2 100644 --- a/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java +++ b/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java @@ -10,7 +10,6 @@ import java.util.Iterator; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opentripplanner.model.transfer.ConstrainedTransfer; import org.opentripplanner.model.transfer.TransferConstraint; @@ -151,7 +150,6 @@ public RaptorConstrainedTransfer findConstrainedTransfer( }; } - @Nonnull @Override public RaptorStopNameResolver stopNameResolver() { // Index is translated: 1->'A', 2->'B', 3->'C' ... diff --git a/src/test/java/org/opentripplanner/routing/TestHalfEdges.java b/src/test/java/org/opentripplanner/routing/TestHalfEdges.java index ba7e024e57a..7b6db0a5acf 100644 --- a/src/test/java/org/opentripplanner/routing/TestHalfEdges.java +++ b/src/test/java/org/opentripplanner/routing/TestHalfEdges.java @@ -601,8 +601,8 @@ public void testStreetLocationFinder() { assertTrue(graphFinder.findClosestStops(new Coordinate(-74.005000001, 40.01), 100).size() > 0); // test that the closest vertex finder returns the closest vertex - TemporaryStreetLocation some = (TemporaryStreetLocation) finder.getVertexForLocationForTest( - new GenericLocation(40.00, -74.00), + TemporaryStreetLocation some = (TemporaryStreetLocation) finder.createVertexForCoordinateForTest( + new Coordinate(-74.00, 40.00), StreetMode.WALK, true, tempEdges @@ -610,8 +610,8 @@ public void testStreetLocationFinder() { assertNotNull(some); // test that the closest vertex finder correctly splits streets - TemporaryStreetLocation start = (TemporaryStreetLocation) finder.getVertexForLocationForTest( - new GenericLocation(40.004, -74.01), + TemporaryStreetLocation start = (TemporaryStreetLocation) finder.createVertexForCoordinateForTest( + new Coordinate(-74.01, 40.004), StreetMode.WALK, false, tempEdges @@ -625,8 +625,8 @@ public void testStreetLocationFinder() { Collection edges = start.getOutgoing(); assertEquals(2, edges.size()); - TemporaryStreetLocation end = (TemporaryStreetLocation) finder.getVertexForLocationForTest( - new GenericLocation(40.008, -74.0), + TemporaryStreetLocation end = (TemporaryStreetLocation) finder.createVertexForCoordinateForTest( + new Coordinate(-74.0, 40.008), StreetMode.BIKE, true, tempEdges @@ -648,7 +648,8 @@ public void testTemporaryVerticesContainer() { try ( var container = new TemporaryVerticesContainer( graph, - walking, + walking.from(), + walking.to(), StreetMode.WALK, StreetMode.WALK ) diff --git a/src/test/java/org/opentripplanner/routing/algorithm/GraphRoutingTest.java b/src/test/java/org/opentripplanner/routing/algorithm/GraphRoutingTest.java index 69e2ddaa944..4a09d140371 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/GraphRoutingTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/GraphRoutingTest.java @@ -3,6 +3,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.function.Consumer; +import javax.annotation.Nullable; import org.locationtech.jts.geom.Coordinate; import org.opentripplanner.TestOtpModel; import org.opentripplanner.framework.geometry.GeometryUtils; @@ -29,12 +31,14 @@ import org.opentripplanner.street.model.edge.PathwayEdge; import org.opentripplanner.street.model.edge.StreetEdge; import org.opentripplanner.street.model.edge.StreetEdgeBuilder; +import org.opentripplanner.street.model.edge.StreetStationCentroidLink; import org.opentripplanner.street.model.edge.StreetTransitEntranceLink; import org.opentripplanner.street.model.edge.StreetTransitStopLink; import org.opentripplanner.street.model.edge.StreetVehicleParkingLink; import org.opentripplanner.street.model.edge.TemporaryFreeEdge; import org.opentripplanner.street.model.vertex.ElevatorOnboardVertex; import org.opentripplanner.street.model.vertex.IntersectionVertex; +import org.opentripplanner.street.model.vertex.StationCentroidVertex; import org.opentripplanner.street.model.vertex.StreetVertex; import org.opentripplanner.street.model.vertex.TemporaryStreetLocation; import org.opentripplanner.street.model.vertex.TemporaryVertex; @@ -56,6 +60,7 @@ import org.opentripplanner.transit.model.site.PathwayMode; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.Station; +import org.opentripplanner.transit.model.site.StationBuilder; import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; @@ -108,6 +113,10 @@ public IntersectionVertex intersection(String label, double latitude, double lon return vertexFactory.intersection(label, longitude, latitude); } + public IntersectionVertex intersection(String label, WgsCoordinate coordinate) { + return intersection(label, coordinate.latitude(), coordinate.longitude()); + } + public StreetEdgeBuilder streetBuilder( StreetVertex from, StreetVertex to, @@ -126,6 +135,13 @@ public StreetEdgeBuilder streetBuilder( .withBack(false); } + /** + * Create a street with all permissions in both directions + */ + public List biStreet(StreetVertex from, StreetVertex to, int length) { + return street(from, to, length, StreetTraversalPermission.ALL, StreetTraversalPermission.ALL); + } + public StreetEdge street( StreetVertex from, StreetVertex to, @@ -220,20 +236,18 @@ public Entrance entranceEntity(String id, double latitude, double longitude) { .build(); } - RegularStop stopEntity(String id, double latitude, double longitude, boolean noTransfers) { + RegularStop stopEntity( + String id, + double latitude, + double longitude, + @Nullable Station parentStation + ) { var stopModelBuilder = transitModel.getStopModel().withContext(); var testModel = new TransitModelForTest(stopModelBuilder); var stopBuilder = testModel.stop(id).withCoordinate(latitude, longitude); - if (noTransfers) { - stopBuilder.withParentStation( - Station - .of(TransitModelForTest.id("1")) - .withName(new NonLocalizedString("Malmö C")) - .withCoordinate(latitude, longitude) - .withTransfersNotAllowed(true) - .build() - ); + if (parentStation != null) { + stopBuilder.withParentStation(parentStation); } var stop = stopBuilder.build(); @@ -241,18 +255,38 @@ RegularStop stopEntity(String id, double latitude, double longitude, boolean noT return stop; } + public Station stationEntity(String id, Consumer stationBuilder) { + var stopModelBuilder = transitModel.getStopModel().withContext(); + var testModel = new TransitModelForTest(stopModelBuilder); + + var builder = testModel.station(id); + stationBuilder.accept(builder); + var station = builder.build(); + + transitModel.mergeStopModels(stopModelBuilder.withStation(station).build()); + return station; + } + + public TransitStopVertex stop(String id, WgsCoordinate coordinate, Station parentStation) { + return stop(id, coordinate.latitude(), coordinate.longitude(), parentStation); + } + + public TransitStopVertex stop(String id, WgsCoordinate coordinate) { + return stop(id, coordinate, null); + } + public TransitStopVertex stop(String id, double latitude, double longitude) { - return stop(id, latitude, longitude, false); + return stop(id, latitude, longitude, null); } public TransitStopVertex stop( String id, double latitude, double longitude, - boolean noTransfers + @Nullable Station parentStation ) { return vertexFactory.transitStop( - TransitStopVertex.of().withStop(stopEntity(id, latitude, longitude, noTransfers)) + TransitStopVertex.of().withStop(stopEntity(id, latitude, longitude, parentStation)) ); } @@ -260,6 +294,10 @@ public TransitEntranceVertex entrance(String id, double latitude, double longitu return new TransitEntranceVertex(entranceEntity(id, latitude, longitude)); } + public StationCentroidVertex stationCentroid(Station station) { + return vertexFactory.stationCentroid(station); + } + public StreetTransitEntranceLink link(StreetVertex from, TransitEntranceVertex to) { return StreetTransitEntranceLink.createStreetTransitEntranceLink(from, to); } @@ -284,6 +322,18 @@ public List biLink(StreetVertex from, TransitStopVertex t return List.of(link(from, to), link(to, from)); } + public StreetStationCentroidLink link(StreetVertex from, StationCentroidVertex to) { + return StreetStationCentroidLink.createStreetStationLink(from, to); + } + + public StreetStationCentroidLink link(StationCentroidVertex from, StreetVertex to) { + return StreetStationCentroidLink.createStreetStationLink(from, to); + } + + public List biLink(StreetVertex from, StationCentroidVertex to) { + return List.of(link(from, to), link(to, from)); + } + public PathwayEdge pathway(Vertex from, Vertex to, int time, int length) { return PathwayEdge.createPathwayEdge( from, diff --git a/src/test/java/org/opentripplanner/routing/algorithm/StreetModeLinkingTest.java b/src/test/java/org/opentripplanner/routing/algorithm/StreetModeLinkingTest.java index 4d1b14832a9..1a098591288 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/StreetModeLinkingTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/StreetModeLinkingTest.java @@ -206,7 +206,8 @@ private void assertLinking( try ( var temporaryVertices = new TemporaryVerticesContainer( graph, - routingRequest, + routingRequest.from(), + routingRequest.to(), streetMode, streetMode ) @@ -230,7 +231,8 @@ private void assertLinking( try ( var temporaryVertices = new TemporaryVerticesContainer( graph, - routingRequest, + routingRequest.from(), + routingRequest.to(), streetMode, streetMode ) diff --git a/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java b/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java index 7b877abcf3a..67253f71438 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java @@ -45,6 +45,7 @@ import org.opentripplanner.raptor.api.path.TransferPathLeg; import org.opentripplanner.raptor.path.Path; import org.opentripplanner.raptor.spi.RaptorCostCalculator; +import org.opentripplanner.routing.algorithm.raptoradapter.router.street.AccessEgressType; import org.opentripplanner.routing.algorithm.raptoradapter.transit.DefaultAccessEgress; import org.opentripplanner.routing.algorithm.raptoradapter.transit.DefaultRaptorTransfer; import org.opentripplanner.routing.algorithm.raptoradapter.transit.FlexAccessEgressAdapter; @@ -193,7 +194,10 @@ void createItineraryWithOnBoardFlexAccess() { true, RoutingBookingInfo.NOT_SET ); - RaptorAccessEgress access = new FlexAccessEgressAdapter(flexAccessEgress, false); + RaptorAccessEgress access = new FlexAccessEgressAdapter( + flexAccessEgress, + AccessEgressType.ACCESS + ); Transfer transfer = new Transfer(S2.getIndex(), 0); RaptorTransfer raptorTransfer = new DefaultRaptorTransfer(S1.getIndex(), 0, 0, transfer); RaptorAccessEgress egress = new DefaultAccessEgress(S2.getIndex(), state); diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/AccessEgressRouterTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/AccessEgressRouterTest.java new file mode 100644 index 00000000000..9cd2b6b913e --- /dev/null +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/router/street/AccessEgressRouterTest.java @@ -0,0 +1,273 @@ +package org.opentripplanner.routing.algorithm.raptoradapter.router.street; + +import static com.google.common.truth.Truth.assertThat; + +import java.time.Duration; +import java.util.Collection; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opentripplanner.framework.geometry.WgsCoordinate; +import org.opentripplanner.model.GenericLocation; +import org.opentripplanner.routing.algorithm.GraphRoutingTest; +import org.opentripplanner.routing.api.request.RouteRequest; +import org.opentripplanner.routing.api.request.StreetMode; +import org.opentripplanner.routing.api.request.request.StreetRequest; +import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.routing.graphfinder.NearbyStop; +import org.opentripplanner.street.model.vertex.TransitStopVertex; +import org.opentripplanner.street.search.TemporaryVerticesContainer; +import org.opentripplanner.street.search.state.State; +import org.opentripplanner.transit.model.framework.FeedScopedId; + +class AccessEgressRouterTest extends GraphRoutingTest { + + private Graph graph; + + private TransitStopVertex stopForCentroidRoutingStation; + private TransitStopVertex stopForNoCentroidRoutingStation; + + private static final WgsCoordinate origin = new WgsCoordinate(0.0, 0.0); + private static final WgsCoordinate farAwayCoordinate = origin.moveEastMeters(100000); + + @BeforeEach + protected void setUp() throws Exception { + var otpModel = modelOf( + new GraphRoutingTest.Builder() { + @Override + public void build() { + var A = intersection("A", origin); + var B = intersection("B", origin.moveEastMeters(100)); + var C = intersection("C", origin.moveEastMeters(200)); + var D = intersection("D", origin.moveEastMeters(300)); + var farAway = intersection("FarAway", farAwayCoordinate); + + biStreet(A, B, 100); + biStreet(B, C, 100); + biStreet(C, D, 100); + biStreet(farAway, A, 1000000); + + var centroidRoutingStation = stationEntity( + "CentroidRoutingStation", + b -> b.withCoordinate(A.toWgsCoordinate()).withShouldRouteToCentroid(true) + ); + var centroidRoutingStationVertex = stationCentroid(centroidRoutingStation); + + var noCentroidRoutingStation = stationEntity( + "NoCentroidRoutingStation", + b -> b.withCoordinate(D.toWgsCoordinate()) + ); + var noCentroidRoutingStationVertex = stationCentroid(noCentroidRoutingStation); + + // StopForCentroidRoutingStation is a child of centroidRoutingStation + stopForCentroidRoutingStation = + stop("StopForCentroidRoutingStation", B.toWgsCoordinate(), centroidRoutingStation); + + // StopForNoCentroidRoutingStation is a child of noCentroidRoutingStation + stopForNoCentroidRoutingStation = + stop("StopForNoCentroidRoutingStation", C.toWgsCoordinate(), noCentroidRoutingStation); + + biLink(A, centroidRoutingStationVertex); + biLink(B, stopForCentroidRoutingStation); + biLink(C, stopForNoCentroidRoutingStation); + biLink(D, noCentroidRoutingStationVertex); + } + } + ); + graph = otpModel.graph(); + } + + @Test + void findAccessEgressFromStop() { + var accesses = findAccessEgressFromTo( + location("StopForCentroidRoutingStation"), + location(farAwayCoordinate), + AccessEgressType.ACCESS + ); + assertAcessEgresses( + Set.of( + "direct[StopForCentroidRoutingStation]", + "street[StopForCentroidRoutingStation -> StopForNoCentroidRoutingStation]" + ), + accesses + ); + + var egresses = findAccessEgressFromTo( + location(farAwayCoordinate), + location("StopForCentroidRoutingStation"), + AccessEgressType.EGRESS + ); + assertAcessEgresses( + Set.of( + "direct[StopForCentroidRoutingStation]", + "street[StopForCentroidRoutingStation -> StopForNoCentroidRoutingStation]" + ), + egresses + ); + } + + @Test + void findAccessEgressStation() { + // For stations with centroid routing we should use the station centroid as source for the street search + var accesses = findAccessEgressFromTo( + location("CentroidRoutingStation"), + location(farAwayCoordinate), + AccessEgressType.ACCESS + ); + assertAcessEgresses( + Set.of( + "direct[StopForCentroidRoutingStation]", + "street[CentroidRoutingStation -> StopForNoCentroidRoutingStation]" + ), + accesses + ); + + var egresses = findAccessEgressFromTo( + location(farAwayCoordinate), + location("CentroidRoutingStation"), + AccessEgressType.EGRESS + ); + assertAcessEgresses( + Set.of( + "direct[StopForCentroidRoutingStation]", + "street[CentroidRoutingStation -> StopForNoCentroidRoutingStation]" + ), + egresses + ); + } + + @Test + void findAccessEgressStationNoCentroidRouting() { + // For stations without centroid routing we should use the quay as source for the street search + var accesses = findAccessEgressFromTo( + location("NoCentroidRoutingStation"), + location(farAwayCoordinate), + AccessEgressType.ACCESS + ); + assertAcessEgresses( + Set.of( + "direct[StopForNoCentroidRoutingStation]", + "street[StopForNoCentroidRoutingStation -> StopForCentroidRoutingStation]" + ), + accesses + ); + + var egresses = findAccessEgressFromTo( + location(farAwayCoordinate), + location("NoCentroidRoutingStation"), + AccessEgressType.EGRESS + ); + assertAcessEgresses( + Set.of( + "direct[StopForNoCentroidRoutingStation]", + "street[StopForNoCentroidRoutingStation -> StopForCentroidRoutingStation]" + ), + egresses + ); + } + + @Test + void findAccessEgressFromCoordinate() { + var coordinate = origin.moveEastMeters(5); + + // We should get street access from coordinate to quay1 and quay2 + var accesses = findAccessEgressFromTo( + location(coordinate), + location(farAwayCoordinate), + AccessEgressType.ACCESS + ); + assertAcessEgresses( + Set.of( + "street[Origin -> StopForCentroidRoutingStation]", + "street[Origin -> StopForNoCentroidRoutingStation]" + ), + accesses + ); + + // We should get street access from coordinate to quay1 and quay2 + var egresses = findAccessEgressFromTo( + location(farAwayCoordinate), + location(coordinate), + AccessEgressType.EGRESS + ); + assertAcessEgresses( + Set.of( + "street[Destination -> StopForCentroidRoutingStation]", + "street[Destination -> StopForNoCentroidRoutingStation]" + ), + egresses + ); + } + + /* Helper methods */ + + private GenericLocation location(WgsCoordinate coordinate) { + return new GenericLocation(coordinate.latitude(), coordinate.longitude()); + } + + private GenericLocation location(FeedScopedId id) { + return new GenericLocation(null, id, null, null); + } + + private GenericLocation location(String id) { + return location(new FeedScopedId("F", id)); + } + + private RouteRequest requestFromTo(GenericLocation from, GenericLocation to) { + var routeRequest = new RouteRequest(); + routeRequest.setFrom(from); + routeRequest.setTo(to); + return routeRequest; + } + + private String nearbyStopDescription(NearbyStop nearbyStop) { + if (nearbyStop.edges.isEmpty()) { + return "direct[" + nearbyStop.stop.getName() + "]"; + } else { + return "street[" + stateDescription(nearbyStop.state) + "]"; + } + } + + private String stateDescription(State state) { + var last = state; + while (last.getBackState() != null) { + last = last.getBackState(); + } + return last.getVertex().getName() + " -> " + state.getVertex().getName(); + } + + private void assertAcessEgresses(Set expected, Collection actual) { + assertThat(actual.stream().map(this::nearbyStopDescription)) + .containsExactlyElementsIn(expected); + } + + private Collection findAccessEgressFromTo( + GenericLocation from, + GenericLocation to, + AccessEgressType accessEgress + ) { + var maxStopCount = 10; + var durationLimit = Duration.ofMinutes(10); + var request = requestFromTo(from, to); + + try ( + var verticesContainer = new TemporaryVerticesContainer( + graph, + from, + to, + StreetMode.WALK, + StreetMode.WALK + ) + ) { + return AccessEgressRouter.findAccessEgresses( + request, + verticesContainer, + new StreetRequest(), + null, + accessEgress, + durationLimit, + maxStopCount + ); + } + } +} diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TripPatternForDateMapperTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TripPatternForDateMapperTest.java index 020dc6305a4..3f41c1d29c7 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TripPatternForDateMapperTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TripPatternForDateMapperTest.java @@ -10,7 +10,6 @@ import java.time.LocalDate; import java.util.HashMap; import java.util.Map; -import javax.annotation.Nonnull; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.opentripplanner.model.Timetable; @@ -36,7 +35,6 @@ public class TripPatternForDateMapperTest { @BeforeAll public static void setUp() throws Exception { var pattern = TEST_MODEL.pattern(BUS).build(); - timetable = new Timetable(pattern); var trip = TransitModelForTest.trip("1").build(); var tripTimes = TripTimesFactory.tripTimes( trip, @@ -44,7 +42,7 @@ public static void setUp() throws Exception { new Deduplicator() ); tripTimes.setServiceCode(SERVICE_CODE); - timetable.addTripTimes(tripTimes); + timetable = Timetable.of().withTripPattern(pattern).addTripTimes(tripTimes).build(); } /** @@ -102,7 +100,6 @@ void testTimeTableWithServiceCodesRunningNotMatchingShouldReturnNull() { assertNull(mapper.map(timetable, SERVICE_DATE)); } - @Nonnull private static TIntHashSet tintHashSet(int... numbers) { var set = new TIntHashSet(); set.addAll(numbers); diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TestRouteData.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TestRouteData.java index f57f03705fd..8648ea20324 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TestRouteData.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TestRouteData.java @@ -62,8 +62,8 @@ public TestRouteData(Route route, List stops, List times) { .of(TransitModelForTest.id("TP:" + route)) .withRoute(this.route) .withStopPattern(new StopPattern(stopTimesFistTrip)) + .withScheduledTimeTableBuilder(builder -> builder.addAllTripTimes(tripTimes)) .build(); - tripTimes.forEach(tripPattern::add); RoutingTripPattern routingTripPattern = tripPattern.getRoutingTripPattern(); diff --git a/src/test/java/org/opentripplanner/routing/core/ItineraryFaresTest.java b/src/test/java/org/opentripplanner/routing/core/ItineraryFaresTest.java index 3ad3b918ce1..2e64952d351 100644 --- a/src/test/java/org/opentripplanner/routing/core/ItineraryFaresTest.java +++ b/src/test/java/org/opentripplanner/routing/core/ItineraryFaresTest.java @@ -15,7 +15,6 @@ import static org.opentripplanner.transit.model._data.TransitModelForTest.id; import java.util.List; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; import org.opentripplanner.model.fare.FareProduct; import org.opentripplanner.model.fare.FareProductUse; @@ -65,7 +64,6 @@ void empty() { assertTrue(ItineraryFares.empty().isEmpty()); } - @Nonnull private static FareProduct fareProduct(String id) { return new FareProduct(id(id), id, Money.euros(10), null, null, null); } diff --git a/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java b/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java index c01558338a4..1af45763b60 100644 --- a/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java +++ b/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java @@ -60,13 +60,8 @@ public void setup() { @Test public void temporaryChangesRemovedOnClose() { - // Given - A request - RouteRequest request = new RouteRequest(); - request.setFrom(from); - request.setTo(to); - // When - the container is created - subject = new TemporaryVerticesContainer(g, request, StreetMode.WALK, StreetMode.WALK); + subject = new TemporaryVerticesContainer(g, from, to, StreetMode.WALK, StreetMode.WALK); // Then: originAndDestinationInsertedCorrect(); diff --git a/src/test/java/org/opentripplanner/routing/graph/SimpleConcreteEdge.java b/src/test/java/org/opentripplanner/routing/graph/SimpleConcreteEdge.java index 0a3fccccda7..f36300de9ef 100644 --- a/src/test/java/org/opentripplanner/routing/graph/SimpleConcreteEdge.java +++ b/src/test/java/org/opentripplanner/routing/graph/SimpleConcreteEdge.java @@ -1,6 +1,5 @@ package org.opentripplanner.routing.graph; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.LineString; import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; import org.opentripplanner.framework.i18n.I18NString; @@ -24,7 +23,6 @@ public static SimpleConcreteEdge createSimpleConcreteEdge(Vertex v1, Vertex v2) } @Override - @Nonnull public State[] traverse(State s0) { double d = getDistanceMeters(); TraverseMode mode = s0.currentMode(); diff --git a/src/test/java/org/opentripplanner/routing/graph/TemporaryConcreteEdge.java b/src/test/java/org/opentripplanner/routing/graph/TemporaryConcreteEdge.java index 44f6b6f7298..19020fc3f4f 100644 --- a/src/test/java/org/opentripplanner/routing/graph/TemporaryConcreteEdge.java +++ b/src/test/java/org/opentripplanner/routing/graph/TemporaryConcreteEdge.java @@ -1,6 +1,5 @@ package org.opentripplanner.routing.graph; -import javax.annotation.Nonnull; import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.street.model.edge.Edge; @@ -36,7 +35,6 @@ public static TemporaryConcreteEdge createTemporaryConcreteEdge(Vertex v1, Tempo } @Override - @Nonnull public State[] traverse(State s0) { double d = getDistanceMeters(); TraverseMode mode = s0.currentMode(); diff --git a/src/test/java/org/opentripplanner/routing/stoptimes/StopTimesHelperTest.java b/src/test/java/org/opentripplanner/routing/stoptimes/StopTimesHelperTest.java index 1432c68fd49..61fffd7a905 100644 --- a/src/test/java/org/opentripplanner/routing/stoptimes/StopTimesHelperTest.java +++ b/src/test/java/org/opentripplanner/routing/stoptimes/StopTimesHelperTest.java @@ -33,14 +33,21 @@ public static void setUp() throws Exception { transitService = new DefaultTransitService(transitModel); feedId = transitModel.getFeedIds().iterator().next(); stopId = new FeedScopedId(feedId, "J"); - pattern = - transitService.getPatternForTrip( - transitService.getTripForId(new FeedScopedId(feedId, "5.1")) - ); - var tt = transitService.getTimetableForTripPattern(pattern, LocalDate.now()); + var originalPattern = transitService.getPatternForTrip( + transitService.getTripForId(new FeedScopedId(feedId, "5.1")) + ); + var tt = originalPattern.getScheduledTimetable(); var newTripTimes = tt.getTripTimes(0).copyScheduledTimes(); newTripTimes.cancelTrip(); - tt.setTripTimes(0, newTripTimes); + pattern = + originalPattern + .copy() + .withScheduledTimeTableBuilder(builder -> builder.addOrUpdateTripTimes(newTripTimes)) + .build(); + // replace the original pattern by the updated pattern in the transit model + transitModel.addTripPattern(pattern.getId(), pattern); + transitModel.index(); + transitService = new DefaultTransitService(transitModel); } /** diff --git a/src/test/java/org/opentripplanner/service/vehiclerental/model/TestFreeFloatingRentalVehicleBuilder.java b/src/test/java/org/opentripplanner/service/vehiclerental/model/TestFreeFloatingRentalVehicleBuilder.java index c3837942426..a9b2398f686 100644 --- a/src/test/java/org/opentripplanner/service/vehiclerental/model/TestFreeFloatingRentalVehicleBuilder.java +++ b/src/test/java/org/opentripplanner/service/vehiclerental/model/TestFreeFloatingRentalVehicleBuilder.java @@ -1,6 +1,5 @@ package org.opentripplanner.service.vehiclerental.model; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.street.model.RentalFormFactor; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -65,7 +64,6 @@ public TestFreeFloatingRentalVehicleBuilder withVehicleCar() { return buildVehicleType(RentalFormFactor.CAR); } - @Nonnull private TestFreeFloatingRentalVehicleBuilder buildVehicleType(RentalFormFactor rentalFormFactor) { this.vehicleType = new RentalVehicleType( diff --git a/src/test/java/org/opentripplanner/service/vehiclerental/model/TestVehicleRentalStationBuilder.java b/src/test/java/org/opentripplanner/service/vehiclerental/model/TestVehicleRentalStationBuilder.java index 0fb9f8b620f..ea8af5ade6f 100644 --- a/src/test/java/org/opentripplanner/service/vehiclerental/model/TestVehicleRentalStationBuilder.java +++ b/src/test/java/org/opentripplanner/service/vehiclerental/model/TestVehicleRentalStationBuilder.java @@ -2,7 +2,6 @@ import java.util.HashMap; import java.util.Map; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.street.model.RentalFormFactor; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -105,7 +104,6 @@ public TestVehicleRentalStationBuilder withVehicleTypeCar(int numAvailable, int ); } - @Nonnull private TestVehicleRentalStationBuilder buildVehicleType( RentalFormFactor rentalFormFactor, RentalVehicleType.PropulsionType propulsionType, diff --git a/src/test/java/org/opentripplanner/standalone/config/ExampleConfigTest.java b/src/test/java/org/opentripplanner/standalone/config/ExampleConfigTest.java index 12b8749c1fe..744deddd0dd 100644 --- a/src/test/java/org/opentripplanner/standalone/config/ExampleConfigTest.java +++ b/src/test/java/org/opentripplanner/standalone/config/ExampleConfigTest.java @@ -22,7 +22,12 @@ @GeneratesDocumentation public class ExampleConfigTest { - @FilePatternSource(pattern = "doc/user/examples/**/" + ROUTER_CONFIG_FILENAME) + @FilePatternSource( + pattern = { + "doc/user/examples/**/" + ROUTER_CONFIG_FILENAME, + "src/test/resources/standalone/config/**/" + ROUTER_CONFIG_FILENAME, + } + ) @ParameterizedTest(name = "Check validity of {0}") void routerConfig(Path filename) { testConfig(filename, a -> new RouterConfig(a, true)); @@ -31,7 +36,8 @@ void routerConfig(Path filename) { @FilePatternSource( pattern = { "doc/user/examples/**/" + BUILD_CONFIG_FILENAME, - "test/performance/**/" + BUILD_CONFIG_FILENAME, + "doc/user/examples/**/" + BUILD_CONFIG_FILENAME, + "src/test/resources/standalone/config/**/" + BUILD_CONFIG_FILENAME, } ) @ParameterizedTest(name = "Check validity of {0}") diff --git a/src/test/java/org/opentripplanner/standalone/server/EtagRequestFilterTest.java b/src/test/java/org/opentripplanner/standalone/server/EtagRequestFilterTest.java index ae19643db72..100edd8f5ae 100644 --- a/src/test/java/org/opentripplanner/standalone/server/EtagRequestFilterTest.java +++ b/src/test/java/org/opentripplanner/standalone/server/EtagRequestFilterTest.java @@ -7,7 +7,6 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.glassfish.jersey.message.internal.OutboundJaxrsResponse; import org.glassfish.jersey.message.internal.OutboundMessageContext; import org.glassfish.jersey.message.internal.Statuses; @@ -84,7 +83,6 @@ void ifNoneMatch(String ifNoneMatch, int expectedStatus, byte[] expectedEntity) assertArrayEquals(expectedEntity, (byte[]) response.getEntity()); } - @Nonnull private static ContainerResponse response(int status, ContainerRequest request) { return new ContainerResponse( request, @@ -92,7 +90,6 @@ private static ContainerResponse response(int status, ContainerRequest request) ); } - @Nonnull private static byte[] bytes(String input) { return input.getBytes(StandardCharsets.UTF_8); } diff --git a/src/test/java/org/opentripplanner/street/integration/BarrierRoutingTest.java b/src/test/java/org/opentripplanner/street/integration/BarrierRoutingTest.java index d1ea3c8e59d..f969c5d18ed 100644 --- a/src/test/java/org/opentripplanner/street/integration/BarrierRoutingTest.java +++ b/src/test/java/org/opentripplanner/street/integration/BarrierRoutingTest.java @@ -179,7 +179,7 @@ private static String computePolyline( options.accept(request); - var temporaryVertices = new TemporaryVerticesContainer(graph, request, streetMode, streetMode); + var temporaryVertices = new TemporaryVerticesContainer(graph, from, to, streetMode, streetMode); var gpf = new GraphPathFinder(null); var paths = gpf.graphPathFinderEntryPoint(request, temporaryVertices); diff --git a/src/test/java/org/opentripplanner/street/integration/BicycleRoutingTest.java b/src/test/java/org/opentripplanner/street/integration/BicycleRoutingTest.java index db937ff2a6e..2c52d0649f5 100644 --- a/src/test/java/org/opentripplanner/street/integration/BicycleRoutingTest.java +++ b/src/test/java/org/opentripplanner/street/integration/BicycleRoutingTest.java @@ -82,7 +82,8 @@ private static String computePolyline(Graph graph, GenericLocation from, Generic request.journey().direct().setMode(StreetMode.BIKE); var temporaryVertices = new TemporaryVerticesContainer( graph, - request, + request.from(), + request.to(), request.journey().direct().mode(), request.journey().direct().mode() ); diff --git a/src/test/java/org/opentripplanner/street/integration/CarRoutingTest.java b/src/test/java/org/opentripplanner/street/integration/CarRoutingTest.java index 471d6bf80ce..caddf972e86 100644 --- a/src/test/java/org/opentripplanner/street/integration/CarRoutingTest.java +++ b/src/test/java/org/opentripplanner/street/integration/CarRoutingTest.java @@ -133,7 +133,8 @@ private static String computePolyline(Graph graph, GenericLocation from, Generic request.journey().direct().setMode(StreetMode.CAR); var temporaryVertices = new TemporaryVerticesContainer( graph, - request, + from, + to, StreetMode.CAR, StreetMode.CAR ); diff --git a/src/test/java/org/opentripplanner/street/integration/SplitEdgeTurnRestrictionsTest.java b/src/test/java/org/opentripplanner/street/integration/SplitEdgeTurnRestrictionsTest.java index 28ab5e91725..e664b3fe2b7 100644 --- a/src/test/java/org/opentripplanner/street/integration/SplitEdgeTurnRestrictionsTest.java +++ b/src/test/java/org/opentripplanner/street/integration/SplitEdgeTurnRestrictionsTest.java @@ -156,7 +156,8 @@ private static String computeCarPolyline(Graph graph, GenericLocation from, Gene request.journey().direct().setMode(StreetMode.CAR); var temporaryVertices = new TemporaryVerticesContainer( graph, - request, + from, + to, StreetMode.CAR, StreetMode.CAR ); diff --git a/src/test/java/org/opentripplanner/street/integration/WalkRoutingTest.java b/src/test/java/org/opentripplanner/street/integration/WalkRoutingTest.java index 3fbb546c3f7..1dad09faec4 100644 --- a/src/test/java/org/opentripplanner/street/integration/WalkRoutingTest.java +++ b/src/test/java/org/opentripplanner/street/integration/WalkRoutingTest.java @@ -62,7 +62,8 @@ private static List> route( try ( var temporaryVertices = new TemporaryVerticesContainer( graph, - request, + request.from(), + request.to(), request.journey().direct().mode(), request.journey().direct().mode() ) diff --git a/src/test/java/org/opentripplanner/street/model/_data/StreetModelForTest.java b/src/test/java/org/opentripplanner/street/model/_data/StreetModelForTest.java index 4a0a0d1b848..a57e4227a6b 100644 --- a/src/test/java/org/opentripplanner/street/model/_data/StreetModelForTest.java +++ b/src/test/java/org/opentripplanner/street/model/_data/StreetModelForTest.java @@ -2,7 +2,6 @@ import static org.opentripplanner.transit.model._data.TransitModelForTest.id; -import javax.annotation.Nonnull; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.LineString; import org.opentripplanner.framework.geometry.GeometryUtils; @@ -42,7 +41,6 @@ public static IntersectionVertex intersectionVertex(String label, double lat, do return new LabelledIntersectionVertex(label, lon, lat, false, false); } - @Nonnull public static TransitEntranceVertex transitEntranceVertex(String id, double lat, double lon) { var entrance = Entrance .of(id(id)) diff --git a/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java b/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java index 60859290646..1cfff635c45 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/EscalatorEdgeTest.java @@ -1,5 +1,6 @@ package org.opentripplanner.street.model.edge; +import static com.google.common.truth.Truth.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Locale; @@ -42,7 +43,7 @@ void testCycling() { var edge = EscalatorEdge.createEscalatorEdge(from, to, 10); var req = StreetSearchRequest.of().withMode(StreetMode.BIKE); var res = edge.traverse(new State(from, req.build())); - assertEquals(res.length, 0); + assertThat(res).isEmpty(); } @Test @@ -50,7 +51,7 @@ void testWheelchair() { var edge = EscalatorEdge.createEscalatorEdge(from, to, 10); var req = StreetSearchRequest.of().withMode(StreetMode.WALK).withWheelchair(true); var res = edge.traverse(new State(from, req.build())); - assertEquals(res.length, 0); + assertThat(res).isEmpty(); } @Test @@ -59,4 +60,10 @@ void name() { assertEquals("Rolltreppe", edge.getName().toString(Locale.GERMANY)); assertEquals("escalator", edge.getName().toString(Locale.ENGLISH)); } + + @Test + void geometry() { + var edge = EscalatorEdge.createEscalatorEdge(from, to, 10); + assertThat(edge.getGeometry().getCoordinates()).isNotEmpty(); + } } diff --git a/src/test/java/org/opentripplanner/street/model/edge/PathwayEdgeTest.java b/src/test/java/org/opentripplanner/street/model/edge/PathwayEdgeTest.java index 6929f665938..6d5574b8027 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/PathwayEdgeTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/PathwayEdgeTest.java @@ -7,7 +7,6 @@ import java.util.Optional; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -241,7 +240,6 @@ void emptySignpostedAs() { assertEquals(PathwayEdge.DEFAULT_NAME, edge.getName()); } - @Nonnull private PathwayEdge pathwayEdge(I18NString sign) { return PathwayEdge.createPathwayEdge( from, diff --git a/src/test/java/org/opentripplanner/street/model/edge/RentalRestrictionExtensionTest.java b/src/test/java/org/opentripplanner/street/model/edge/RentalRestrictionExtensionTest.java index 2a5589d58bf..41b648cea64 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/RentalRestrictionExtensionTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/RentalRestrictionExtensionTest.java @@ -13,7 +13,6 @@ import static org.opentripplanner.street.search.state.VehicleRentalState.RENTING_FLOATING; import java.util.Set; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.opentripplanner.routing.api.request.StreetMode; @@ -167,7 +166,6 @@ private State[] traverse(StreetEdge edge) { return edge.traverse(state); } - @Nonnull private State state(String network) { var req = StreetSearchRequest.of().withMode(StreetMode.SCOOTER_RENTAL).build(); var editor = new StateEditor(V1, req); diff --git a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeGeofencingTest.java b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeGeofencingTest.java index cc38b04230a..3375d94f5af 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeGeofencingTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeGeofencingTest.java @@ -14,7 +14,6 @@ import static org.opentripplanner.street.search.state.VehicleRentalState.RENTING_FLOATING; import java.util.Set; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.opentripplanner.routing.api.request.StreetMode; @@ -297,7 +296,6 @@ private static StreetSearchRequest defaultArriveByRequest() { } } - @Nonnull private static GeofencingZoneExtension noDropOffRestriction(String networkTier) { return new GeofencingZoneExtension( new GeofencingZone(new FeedScopedId(networkTier, "a-park"), null, true, false) @@ -309,12 +307,10 @@ private State[] traverseFromV1(StreetEdge edge) { return edge.traverse(state); } - @Nonnull private State forwardState(String network) { return initialState(V1, network, false); } - @Nonnull private State initialState(Vertex startVertex, String network, boolean arriveBy) { var req = StreetSearchRequest .of() diff --git a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeRentalTraversalTest.java b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeRentalTraversalTest.java index 44de661f327..e2187acad18 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeRentalTraversalTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeRentalTraversalTest.java @@ -14,7 +14,6 @@ import static org.opentripplanner.street.model._data.StreetModelForTest.streetEdge; import java.util.stream.Stream; -import javax.annotation.Nonnull; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -31,7 +30,6 @@ public class StreetEdgeRentalTraversalTest { StreetVertex v0 = intersectionVertex(0.0, 0.0); StreetVertex v1 = intersectionVertex(2.0, 2.0); - @Nonnull private static Stream baseCases(StreetTraversalPermission p) { return Stream.of( of(SCOOTER, SCOOTER_RENTAL, p), diff --git a/src/test/java/org/opentripplanner/street/model/edge/VehicleRentalEdgeTest.java b/src/test/java/org/opentripplanner/street/model/edge/VehicleRentalEdgeTest.java index a3fbfcb018e..e8a1282327c 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/VehicleRentalEdgeTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/VehicleRentalEdgeTest.java @@ -6,7 +6,6 @@ import static org.junit.jupiter.api.Assertions.fail; import java.util.Set; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.opentripplanner.routing.api.request.StreetMode; @@ -254,7 +253,6 @@ void startedOutsideNoDropOffZone() { assertEquals(VehicleRentalState.BEFORE_RENTING, afterTraversal.getVehicleRentalState()); } - @Nonnull private GeofencingZoneExtension noDropOffZone() { return new GeofencingZoneExtension( new GeofencingZone(new FeedScopedId(NETWORK, "zone"), null, true, false) diff --git a/src/test/java/org/opentripplanner/street/model/vertex/OsmVertexTest.java b/src/test/java/org/opentripplanner/street/model/vertex/OsmVertexTest.java index b3a2b0baebb..61da00ed2c6 100644 --- a/src/test/java/org/opentripplanner/street/model/vertex/OsmVertexTest.java +++ b/src/test/java/org/opentripplanner/street/model/vertex/OsmVertexTest.java @@ -5,7 +5,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.site.AreaStop; @@ -38,7 +37,6 @@ void addAreaStop() { assertEquals(2, vertex.areaStops().size()); } - @Nonnull private static OsmVertex vertex() { return new OsmVertex(1, 2, 1); } diff --git a/src/test/java/org/opentripplanner/street/model/vertex/SimpleVertex.java b/src/test/java/org/opentripplanner/street/model/vertex/SimpleVertex.java index 294bed335cc..b2df8bc6d16 100644 --- a/src/test/java/org/opentripplanner/street/model/vertex/SimpleVertex.java +++ b/src/test/java/org/opentripplanner/street/model/vertex/SimpleVertex.java @@ -1,6 +1,5 @@ package org.opentripplanner.street.model.vertex; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; public class SimpleVertex extends StreetVertex { @@ -12,7 +11,6 @@ public SimpleVertex(String label, double lat, double lon) { this.label = label; } - @Nonnull @Override public I18NString getName() { return I18NString.of(label); diff --git a/src/test/java/org/opentripplanner/street/model/vertex/TemporaryVertexDisposeTest.java b/src/test/java/org/opentripplanner/street/model/vertex/TemporaryVertexDisposeTest.java index 7f559f2ad35..e4880f891e2 100644 --- a/src/test/java/org/opentripplanner/street/model/vertex/TemporaryVertexDisposeTest.java +++ b/src/test/java/org/opentripplanner/street/model/vertex/TemporaryVertexDisposeTest.java @@ -2,7 +2,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.street.model.edge.FreeEdge; @@ -232,7 +231,6 @@ public String toString() { return getLabelString(); } - @Nonnull @Override public I18NString getName() { return NO_NAME; diff --git a/src/test/java/org/opentripplanner/street/model/vertex/VertexTest.java b/src/test/java/org/opentripplanner/street/model/vertex/VertexTest.java index 62194fdbc41..c839386f872 100644 --- a/src/test/java/org/opentripplanner/street/model/vertex/VertexTest.java +++ b/src/test/java/org/opentripplanner/street/model/vertex/VertexTest.java @@ -1,9 +1,11 @@ package org.opentripplanner.street.model.vertex; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; +import org.opentripplanner.framework.geometry.WgsCoordinate; class VertexTest { @@ -25,4 +27,10 @@ void testDifferentLocation() { Vertex v2 = new SimpleVertex("", LAT + EPSILON_10_E_MINUS_7, LON); assertFalse(v1.sameLocation(v2)); } + + @Test + void testWgsCoordinate() { + Vertex v1 = new SimpleVertex("", LAT, LON); + assertEquals(new WgsCoordinate(LAT, LON), v1.toWgsCoordinate()); + } } diff --git a/src/test/java/org/opentripplanner/street/search/state/TestStateBuilder.java b/src/test/java/org/opentripplanner/street/search/state/TestStateBuilder.java index 3750c4619b9..1b66152ec09 100644 --- a/src/test/java/org/opentripplanner/street/search/state/TestStateBuilder.java +++ b/src/test/java/org/opentripplanner/street/search/state/TestStateBuilder.java @@ -8,7 +8,6 @@ import java.time.OffsetDateTime; import java.util.Arrays; import java.util.List; -import javax.annotation.Nonnull; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.routing.algorithm.raptoradapter.router.street.AccessEgressType; @@ -277,7 +276,6 @@ public TestStateBuilder pathway(String s) { return this; } - @Nonnull private TestStateBuilder arriveAtStop(RegularStop stop) { var from = (StreetVertex) currentState.vertex; var to = TransitStopVertex.of().withStop(stop).build(); @@ -296,7 +294,6 @@ private TestStateBuilder arriveAtStop(RegularStop stop) { return this; } - @Nonnull private static ElevatorOffboardVertex elevatorOffBoard(int count, String suffix) { return new ElevatorOffboardVertex( StreetModelForTest.intersectionVertex(count, count), @@ -305,7 +302,6 @@ private static ElevatorOffboardVertex elevatorOffBoard(int count, String suffix) ); } - @Nonnull private static ElevatorOnboardVertex elevatorOnBoard(int count, String suffix) { return new ElevatorOnboardVertex( StreetModelForTest.intersectionVertex(count, count), diff --git a/src/test/java/org/opentripplanner/transit/model/_data/PatternTestModel.java b/src/test/java/org/opentripplanner/transit/model/_data/PatternTestModel.java index c3afd0ddf61..0b149ae4ee4 100644 --- a/src/test/java/org/opentripplanner/transit/model/_data/PatternTestModel.java +++ b/src/test/java/org/opentripplanner/transit/model/_data/PatternTestModel.java @@ -29,18 +29,17 @@ public class PatternTestModel { * Creates a trip pattern that has a stop pattern, trip times and a trip with a service id. */ public static TripPattern pattern() { - var pattern = TransitModelForTest - .tripPattern("1", ROUTE_1) - .withStopPattern(STOP_PATTERN) - .build(); - var tt = ScheduledTripTimes .of() .withTrip(TRIP) .withArrivalTimes("10:00 10:05") .withDepartureTimes("10:00 10:05") .build(); - pattern.add(tt); - return pattern; + + return TransitModelForTest + .tripPattern("1", ROUTE_1) + .withStopPattern(STOP_PATTERN) + .withScheduledTimeTableBuilder(builder -> builder.addTripTimes(tt)) + .build(); } } diff --git a/src/test/java/org/opentripplanner/transit/model/filter/expr/AndMatcherTest.java b/src/test/java/org/opentripplanner/transit/model/filter/expr/AndMatcherTest.java new file mode 100644 index 00000000000..c79260193ff --- /dev/null +++ b/src/test/java/org/opentripplanner/transit/model/filter/expr/AndMatcherTest.java @@ -0,0 +1,39 @@ +package org.opentripplanner.transit.model.filter.expr; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class AndMatcherTest { + + @Test + void testMatchSingleMatcher() { + var matcher = AndMatcher.of(List.of(new EqualityMatcher<>("int", 42, i -> i))); + assertTrue(matcher.match(42)); + assertFalse(matcher.match(43)); + } + + @Test + void testMatchMultiple() { + var matcher = AndMatcher.of( + List.of(new EqualityMatcher<>("int", 42, i -> i), new EqualityMatcher<>("int", 43, i -> i)) + ); + assertFalse(matcher.match(42)); + assertFalse(matcher.match(43)); + assertFalse(matcher.match(44)); + } + + @Test + void testMatchComposites() { + var matcher = AndMatcher.of( + List.of( + OrMatcher.of(List.of(new EqualityMatcher<>("int", 42, i -> i))), + OrMatcher.of(List.of(new EqualityMatcher<>("int", 43, i -> i))) + ) + ); + assertFalse(matcher.match(42)); + assertFalse(matcher.match(43)); + assertFalse(matcher.match(44)); + } +} diff --git a/src/test/java/org/opentripplanner/transit/model/filter/expr/ContainsMatcherTest.java b/src/test/java/org/opentripplanner/transit/model/filter/expr/ContainsMatcherTest.java new file mode 100644 index 00000000000..1709fc7bf86 --- /dev/null +++ b/src/test/java/org/opentripplanner/transit/model/filter/expr/ContainsMatcherTest.java @@ -0,0 +1,33 @@ +package org.opentripplanner.transit.model.filter.expr; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class ContainsMatcherTest { + + private static final Map> integerListMap = Map.of( + 1, + List.of("foo"), + 2, + List.of("bar"), + 3, + List.of("foo", "bar") + ); + + @Test + void testMatch() { + var matcher = new ContainsMatcher<>( + "contains", + integerListMap::get, + new EqualityMatcher<>("string", "foo", s -> s) + ); + + assertTrue(matcher.match(1)); + assertFalse(matcher.match(2)); + assertTrue(matcher.match(3)); + assertFalse(matcher.match(4)); + } +} diff --git a/src/test/java/org/opentripplanner/transit/model/filter/expr/EqualityMatcherTest.java b/src/test/java/org/opentripplanner/transit/model/filter/expr/EqualityMatcherTest.java new file mode 100644 index 00000000000..31d208a768a --- /dev/null +++ b/src/test/java/org/opentripplanner/transit/model/filter/expr/EqualityMatcherTest.java @@ -0,0 +1,22 @@ +package org.opentripplanner.transit.model.filter.expr; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class EqualityMatcherTest { + + @Test + void testMatchesPrimitive() { + var matcher = new EqualityMatcher<>("int", 42, i -> i); + assertTrue(matcher.match(42)); + assertFalse(matcher.match(43)); + } + + @Test + void testMatchesObject() { + var matcher = new EqualityMatcher<>("string", "foo", s -> s); + assertTrue(matcher.match("foo")); + assertFalse(matcher.match("bar")); + } +} diff --git a/src/test/java/org/opentripplanner/transit/model/filter/expr/OrMatcherTest.java b/src/test/java/org/opentripplanner/transit/model/filter/expr/OrMatcherTest.java new file mode 100644 index 00000000000..415f64e40ed --- /dev/null +++ b/src/test/java/org/opentripplanner/transit/model/filter/expr/OrMatcherTest.java @@ -0,0 +1,31 @@ +package org.opentripplanner.transit.model.filter.expr; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class OrMatcherTest { + + @Test + void testMatch() { + var matcher = OrMatcher.of( + new EqualityMatcher<>("int", 42, i -> i), + new EqualityMatcher<>("int", 43, i -> i) + ); + assertTrue(matcher.match(42)); + assertTrue(matcher.match(43)); + assertFalse(matcher.match(44)); + } + + @Test + void testMatchComposites() { + var matcher = OrMatcher.of( + AndMatcher.of(List.of(new EqualityMatcher<>("int", 42, i -> i))), + AndMatcher.of(List.of(new EqualityMatcher<>("int", 43, i -> i))) + ); + assertTrue(matcher.match(42)); + assertTrue(matcher.match(43)); + assertFalse(matcher.match(44)); + } +} diff --git a/src/test/java/org/opentripplanner/transit/model/filter/transit/TripOnServiceDateMatcherFactoryTest.java b/src/test/java/org/opentripplanner/transit/model/filter/transit/TripOnServiceDateMatcherFactoryTest.java new file mode 100644 index 00000000000..b7cb7aa6698 --- /dev/null +++ b/src/test/java/org/opentripplanner/transit/model/filter/transit/TripOnServiceDateMatcherFactoryTest.java @@ -0,0 +1,151 @@ +package org.opentripplanner.transit.model.filter.transit; + +import static org.junit.jupiter.api.Assertions.*; + +import java.time.LocalDate; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opentripplanner.transit.api.request.TripOnServiceDateRequest; +import org.opentripplanner.transit.model.basic.TransitMode; +import org.opentripplanner.transit.model.filter.expr.Matcher; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.network.Route; +import org.opentripplanner.transit.model.organization.Agency; +import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; + +class TripOnServiceDateMatcherFactoryTest { + + private TripOnServiceDate tripOnServiceDateRut; + private TripOnServiceDate tripOnServiceDateRut2; + private TripOnServiceDate tripOnServiceDateAkt; + + @BeforeEach + void setup() { + tripOnServiceDateRut = + TripOnServiceDate + .of(new FeedScopedId("RUT:route:trip:date", "123")) + .withTrip( + Trip + .of(new FeedScopedId("RUT:route:trip", "1")) + .withRoute( + Route + .of(new FeedScopedId("RUT:route", "2")) + .withAgency( + Agency + .of(new FeedScopedId("RUT", "3")) + .withName("RUT") + .withTimezone("Europe/Oslo") + .build() + ) + .withMode(TransitMode.BUS) + .withShortName("BUS") + .build() + ) + .build() + ) + .withServiceDate(LocalDate.of(2024, 2, 22)) + .build(); + + tripOnServiceDateRut2 = + TripOnServiceDate + .of(new FeedScopedId("RUT:route:trip:date", "123")) + .withTrip( + Trip + .of(new FeedScopedId("RUT:route:trip2", "1")) + .withRoute( + Route + .of(new FeedScopedId("RUT:route", "2")) + .withAgency( + Agency + .of(new FeedScopedId("RUT", "3")) + .withName("RUT") + .withTimezone("Europe/Oslo") + .build() + ) + .withMode(TransitMode.BUS) + .withShortName("BUS") + .build() + ) + .build() + ) + .withServiceDate(LocalDate.of(2024, 2, 22)) + .build(); + + tripOnServiceDateAkt = + TripOnServiceDate + .of(new FeedScopedId("AKT:route:trip:date", "123")) + .withTrip( + Trip + .of(new FeedScopedId("AKT:route:trip", "1")) + .withRoute( + Route + .of(new FeedScopedId("AKT:route", "2")) + .withAgency( + Agency + .of(new FeedScopedId("AKT", "3")) + .withName("AKT") + .withTimezone("Europe/Oslo") + .build() + ) + .withMode(TransitMode.BUS) + .withShortName("BUS") + .build() + ) + .build() + ) + .withServiceDate(LocalDate.of(2024, 2, 22)) + .build(); + } + + @Test + void testMatchOperatingDays() { + TripOnServiceDateRequest request = TripOnServiceDateRequest + .of() + .withOperatingDays(List.of(LocalDate.of(2024, 2, 22))) + .build(); + + Matcher matcher = TripOnServiceDateMatcherFactory.of(request); + + assertTrue(matcher.match(tripOnServiceDateRut)); + assertTrue(matcher.match(tripOnServiceDateRut2)); + assertTrue(matcher.match(tripOnServiceDateAkt)); + } + + @Test + void testMatchMultiple() { + TripOnServiceDateRequest request = TripOnServiceDateRequest + .of() + .withOperatingDays(List.of(LocalDate.of(2024, 2, 22))) + .withAuthorities(List.of(new FeedScopedId("RUT", "3"))) + .withLines(List.of(new FeedScopedId("RUT:route", "2"))) + .withServiceJourneys(List.of(new FeedScopedId("RUT:route:trip", "1"))) + .build(); + + Matcher matcher = TripOnServiceDateMatcherFactory.of(request); + + assertTrue(matcher.match(tripOnServiceDateRut)); + assertFalse(matcher.match(tripOnServiceDateRut2)); + assertFalse(matcher.match(tripOnServiceDateAkt)); + } + + @Test + void testMatchMultipleServiceJourneyMatchers() { + TripOnServiceDateRequest request = TripOnServiceDateRequest + .of() + .withOperatingDays(List.of(LocalDate.of(2024, 2, 22))) + .withAuthorities(List.of(new FeedScopedId("RUT", "3"))) + .withLines(List.of(new FeedScopedId("RUT:route", "2"))) + .withServiceJourneys( + List.of(new FeedScopedId("RUT:route:trip", "1"), new FeedScopedId("RUT:route:trip2", "1")) + ) + .build(); + + Matcher matcher = TripOnServiceDateMatcherFactory.of(request); + + assertTrue(matcher.match(tripOnServiceDateRut)); + assertTrue(matcher.match(tripOnServiceDateRut2)); + assertFalse(matcher.match(tripOnServiceDateAkt)); + } +} diff --git a/src/test/java/org/opentripplanner/transit/model/framework/EntityByIdTest.java b/src/test/java/org/opentripplanner/transit/model/framework/EntityByIdTest.java index 146c2c9e5ae..f7f1aed4666 100644 --- a/src/test/java/org/opentripplanner/transit/model/framework/EntityByIdTest.java +++ b/src/test/java/org/opentripplanner/transit/model/framework/EntityByIdTest.java @@ -5,7 +5,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collections; -import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; import org.opentripplanner.transit.model._data.TransitModelForTest; @@ -84,11 +83,10 @@ public static TestEntityBuilder of(FeedScopedId id) { } @Override - public boolean sameAs(@Nonnull TestEntity other) { + public boolean sameAs(TestEntity other) { return getId().equals(other.getId()); } - @Nonnull @Override public TransitBuilder copy() { return new TestEntityBuilder(this); diff --git a/src/test/java/org/opentripplanner/transit/speed_test/SpeedIntegrationTest.java b/src/test/java/org/opentripplanner/transit/speed_test/SpeedIntegrationTest.java index e5006d949b6..61a23b10ff6 100644 --- a/src/test/java/org/opentripplanner/transit/speed_test/SpeedIntegrationTest.java +++ b/src/test/java/org/opentripplanner/transit/speed_test/SpeedIntegrationTest.java @@ -24,14 +24,14 @@ import org.opentripplanner.transit.speed_test.options.SpeedTestConfig; /** - * This test run the SpeedTest on the Portland dataset. It tests all SpeedTest + * This test runs the SpeedTest on the Portland dataset. It tests all SpeedTest * profiles. This is also a good integration-test for the OTP routing query. */ public class SpeedIntegrationTest { /** - * We need to use a relative path here, because the test will update the results files in case - * the results differ. This make it easy to maintain the test. + * We need to use a relative path here, because the test will update the result files in case + * the results differ. This makes it easy to maintain the test. */ private static final File BASE_DIR = Path.of("src", "test", "resources", "speedtest").toFile(); diff --git a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestConstants.java b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestConstants.java new file mode 100644 index 00000000000..bad0c1982e9 --- /dev/null +++ b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestConstants.java @@ -0,0 +1,48 @@ +package org.opentripplanner.updater.trip; + +import static org.opentripplanner.transit.model._data.TransitModelForTest.id; + +import java.time.LocalDate; +import java.time.ZoneId; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.network.Route; +import org.opentripplanner.transit.model.organization.Operator; +import org.opentripplanner.transit.model.site.RegularStop; +import org.opentripplanner.transit.model.site.Station; +import org.opentripplanner.transit.service.StopModel; + +public interface RealtimeTestConstants { + LocalDate SERVICE_DATE = LocalDate.of(2024, 5, 8); + FeedScopedId SERVICE_ID = TransitModelForTest.id("CAL_1"); + String STOP_A1_ID = "A1"; + String STOP_B1_ID = "B1"; + String STOP_C1_ID = "C1"; + String TRIP_1_ID = "TestTrip1"; + String TRIP_2_ID = "TestTrip2"; + String OPERATOR_1_ID = "TestOperator1"; + Operator OPERATOR1 = Operator.of(id(OPERATOR_1_ID)).withName(OPERATOR_1_ID).build(); + String ROUTE_1_ID = "TestRoute1"; + + TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + ZoneId TIME_ZONE = ZoneId.of(TransitModelForTest.TIME_ZONE_ID); + Station STATION_A = TEST_MODEL.station("A").build(); + Station STATION_B = TEST_MODEL.station("B").build(); + Station STATION_C = TEST_MODEL.station("C").build(); + Station STATION_D = TEST_MODEL.station("D").build(); + RegularStop STOP_A1 = TEST_MODEL.stop(STOP_A1_ID).withParentStation(STATION_A).build(); + RegularStop STOP_B1 = TEST_MODEL.stop(STOP_B1_ID).withParentStation(STATION_B).build(); + RegularStop STOP_B2 = TEST_MODEL.stop("B2").withParentStation(STATION_B).build(); + RegularStop STOP_C1 = TEST_MODEL.stop(STOP_C1_ID).withParentStation(STATION_C).build(); + RegularStop STOP_D1 = TEST_MODEL.stop("D1").withParentStation(STATION_D).build(); + StopModel STOP_MODEL = TEST_MODEL + .stopModelBuilder() + .withRegularStop(STOP_A1) + .withRegularStop(STOP_B1) + .withRegularStop(STOP_B2) + .withRegularStop(STOP_C1) + .withRegularStop(STOP_D1) + .build(); + + Route ROUTE_1 = TransitModelForTest.route(ROUTE_1_ID).build(); +} diff --git a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java index a40bd8bd797..682bff038b6 100644 --- a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java +++ b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java @@ -1,41 +1,25 @@ package org.opentripplanner.updater.trip; +import static org.opentripplanner.transit.model._data.TransitModelForTest.id; import static org.opentripplanner.updater.trip.UpdateIncrementality.DIFFERENTIAL; import static org.opentripplanner.updater.trip.UpdateIncrementality.FULL_DATASET; import com.google.transit.realtime.GtfsRealtime; import java.time.Duration; import java.time.LocalDate; -import java.time.ZoneId; import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.IntStream; import org.opentripplanner.DateTimeHelper; import org.opentripplanner.ext.siri.SiriTimetableSnapshotSource; import org.opentripplanner.ext.siri.updater.EstimatedTimetableHandler; -import org.opentripplanner.framework.i18n.I18NString; -import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; -import org.opentripplanner.model.StopTime; import org.opentripplanner.model.TimetableSnapshot; -import org.opentripplanner.model.calendar.CalendarServiceData; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.transit.model._data.TransitModelForTest; -import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; -import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.network.TripPattern; -import org.opentripplanner.transit.model.organization.Operator; -import org.opentripplanner.transit.model.site.RegularStop; -import org.opentripplanner.transit.model.site.Station; -import org.opentripplanner.transit.model.site.StopLocation; -import org.opentripplanner.transit.model.timetable.Trip; -import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; -import org.opentripplanner.transit.model.timetable.TripTimesFactory; import org.opentripplanner.transit.model.timetable.TripTimesStringBuilder; import org.opentripplanner.transit.service.DefaultTransitService; -import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.updater.DefaultRealTimeUpdateContext; @@ -50,47 +34,20 @@ *

* It is however a goal to change that and then these two can be combined. */ -public final class RealtimeTestEnvironment { +public final class RealtimeTestEnvironment implements RealtimeTestConstants { + // static constants private static final TimetableSnapshotSourceParameters PARAMETERS = new TimetableSnapshotSourceParameters( Duration.ZERO, false ); - public static final LocalDate SERVICE_DATE = LocalDate.of(2024, 5, 8); - public static final FeedScopedId SERVICE_ID = TransitModelForTest.id("CAL_1"); - public static final String STOP_A1_ID = "A1"; - public static final String STOP_B1_ID = "B1"; - public static final String STOP_C1_ID = "C1"; - private final TransitModelForTest testModel = TransitModelForTest.of(); - public final ZoneId timeZone = ZoneId.of(TransitModelForTest.TIME_ZONE_ID); - public final Station stationA = testModel.station("A").build(); - public final Station stationB = testModel.station("B").build(); - public final Station stationC = testModel.station("C").build(); - public final Station stationD = testModel.station("D").build(); - public final RegularStop stopA1 = testModel.stop(STOP_A1_ID).withParentStation(stationA).build(); - public final RegularStop stopB1 = testModel.stop(STOP_B1_ID).withParentStation(stationB).build(); - public final RegularStop stopB2 = testModel.stop("B2").withParentStation(stationB).build(); - public final RegularStop stopC1 = testModel.stop(STOP_C1_ID).withParentStation(stationC).build(); - public final RegularStop stopD1 = testModel.stop("D1").withParentStation(stationD).build(); - public final StopModel stopModel = testModel - .stopModelBuilder() - .withRegularStop(stopA1) - .withRegularStop(stopB1) - .withRegularStop(stopB2) - .withRegularStop(stopC1) - .withRegularStop(stopD1) - .build(); - public final FeedScopedId operator1Id = TransitModelForTest.id("TestOperator1"); - public final FeedScopedId route1Id = TransitModelForTest.id("TestRoute1"); - public final Trip trip1; - public final Trip trip2; - public final Operator operator1; + public final TransitModel transitModel; private final SiriTimetableSnapshotSource siriSource; private final TimetableSnapshotSource gtfsSource; private final DateTimeHelper dateTimeHelper; - private enum SourceType { + enum SourceType { GTFS_RT, SIRI, } @@ -98,54 +55,22 @@ private enum SourceType { /** * Siri and GTFS-RT cannot be run at the same time, so you need to decide. */ - public static RealtimeTestEnvironment siri() { - return new RealtimeTestEnvironment(SourceType.SIRI); + public static RealtimeTestEnvironmentBuilder siri() { + return new RealtimeTestEnvironmentBuilder().withSourceType(SourceType.SIRI); } /** * Siri and GTFS-RT cannot be run at the same time, so you need to decide. */ - public static RealtimeTestEnvironment gtfs() { - return new RealtimeTestEnvironment(SourceType.GTFS_RT); + public static RealtimeTestEnvironmentBuilder gtfs() { + return new RealtimeTestEnvironmentBuilder().withSourceType(SourceType.GTFS_RT); } - private RealtimeTestEnvironment(SourceType sourceType) { - transitModel = new TransitModel(stopModel, new Deduplicator()); - transitModel.initTimeZone(timeZone); - transitModel.addAgency(TransitModelForTest.AGENCY); - - operator1 = Operator.of(operator1Id).withName("Operator 1").build(); - transitModel.getOperators().add(operator1); - - Route route1 = TransitModelForTest.route(route1Id).withOperator(operator1).build(); - - trip1 = - createTrip( - "TestTrip1", - route1, - List.of(new StopCall(stopA1, 10, 11), new StopCall(stopB1, 20, 21)) - ); - trip2 = - createTrip( - "TestTrip2", - route1, - List.of( - new StopCall(stopA1, 60, 61), - new StopCall(stopB1, 70, 71), - new StopCall(stopC1, 80, 81) - ) - ); - - CalendarServiceData calendarServiceData = new CalendarServiceData(); - calendarServiceData.putServiceDatesForServiceId( - SERVICE_ID, - List.of(SERVICE_DATE.minusDays(1), SERVICE_DATE, SERVICE_DATE.plusDays(1)) - ); - transitModel.getServiceCodes().put(SERVICE_ID, 0); - transitModel.updateCalendarServiceData(true, calendarServiceData, DataImportIssueStore.NOOP); - - transitModel.index(); + RealtimeTestEnvironment(SourceType sourceType, TransitModel transitModel) { + Objects.requireNonNull(sourceType); + this.transitModel = transitModel; + this.transitModel.index(); // SIRI and GTFS-RT cannot be registered with the transit model at the same time // we are actively refactoring to remove this restriction // for the time being you cannot run a SIRI and GTFS-RT test at the same time @@ -156,11 +81,7 @@ private RealtimeTestEnvironment(SourceType sourceType) { gtfsSource = new TimetableSnapshotSource(PARAMETERS, transitModel); siriSource = null; } - dateTimeHelper = new DateTimeHelper(timeZone, RealtimeTestEnvironment.SERVICE_DATE); - } - - public static FeedScopedId id(String id) { - return TransitModelForTest.id(id); + dateTimeHelper = new DateTimeHelper(TIME_ZONE, SERVICE_DATE); } /** @@ -190,7 +111,11 @@ private EstimatedTimetableHandler getEstimatedTimetableHandler(boolean fuzzyMatc } public TripPattern getPatternForTrip(FeedScopedId tripId) { - return getPatternForTrip(tripId, RealtimeTestEnvironment.SERVICE_DATE); + return getPatternForTrip(tripId, SERVICE_DATE); + } + + public TripPattern getPatternForTrip(String id) { + return getPatternForTrip(id(id)); } public TripPattern getPatternForTrip(FeedScopedId tripId, LocalDate serviceDate) { @@ -199,13 +124,6 @@ public TripPattern getPatternForTrip(FeedScopedId tripId, LocalDate serviceDate) return transitService.getPatternForTrip(trip.getTrip(), serviceDate); } - /** - * Find the current TripTimes for a trip id on the default serviceDate - */ - public TripTimes getTripTimesForTrip(Trip trip) { - return getTripTimesForTrip(trip.getId(), SERVICE_DATE); - } - /** * Find the current TripTimes for a trip id on the default serviceDate */ @@ -217,10 +135,6 @@ public DateTimeHelper getDateTimeHelper() { return dateTimeHelper; } - public TripPattern getPatternForTrip(Trip trip) { - return getTransitService().getPatternForTrip(trip); - } - public TimetableSnapshot getTimetableSnapshot() { if (siriSource != null) { return siriSource.getTimetableSnapshot(); @@ -233,10 +147,6 @@ public String getRealtimeTimetable(String tripId) { return getRealtimeTimetable(id(tripId), SERVICE_DATE); } - public String getRealtimeTimetable(Trip trip) { - return getRealtimeTimetable(trip.getId(), SERVICE_DATE); - } - public String getRealtimeTimetable(FeedScopedId tripId, LocalDate serviceDate) { var tt = getTripTimesForTrip(tripId, serviceDate); var pattern = getPatternForTrip(tripId); @@ -325,59 +235,4 @@ private void commitTimetableSnapshot() { gtfsSource.flushBuffer(); } } - - private Trip createTrip(String id, Route route, List stops) { - var trip = Trip - .of(id(id)) - .withRoute(route) - .withHeadsign(I18NString.of("Headsign of %s".formatted(id))) - .withServiceId(SERVICE_ID) - .build(); - - var tripOnServiceDate = TripOnServiceDate - .of(trip.getId()) - .withTrip(trip) - .withServiceDate(SERVICE_DATE) - .build(); - - transitModel.addTripOnServiceDate(tripOnServiceDate.getId(), tripOnServiceDate); - - var stopTimes = IntStream - .range(0, stops.size()) - .mapToObj(i -> { - var stop = stops.get(i); - return createStopTime(trip, i, stop.stop(), stop.arrivalTime(), stop.departureTime()); - }) - .collect(Collectors.toList()); - - TripTimes tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, null); - - final TripPattern pattern = TransitModelForTest - .tripPattern(id + "Pattern", route) - .withStopPattern(TransitModelForTest.stopPattern(stops.stream().map(StopCall::stop).toList())) - .build(); - pattern.add(tripTimes); - - transitModel.addTripPattern(pattern.getId(), pattern); - - return trip; - } - - private StopTime createStopTime( - Trip trip, - int stopSequence, - StopLocation stop, - int arrivalTime, - int departureTime - ) { - var st = new StopTime(); - st.setTrip(trip); - st.setStopSequence(stopSequence); - st.setStop(stop); - st.setArrivalTime(arrivalTime); - st.setDepartureTime(departureTime); - return st; - } - - private record StopCall(RegularStop stop, int arrivalTime, int departureTime) {} } diff --git a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironmentBuilder.java b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironmentBuilder.java new file mode 100644 index 00000000000..88f4bf41012 --- /dev/null +++ b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironmentBuilder.java @@ -0,0 +1,114 @@ +package org.opentripplanner.updater.trip; + +import static org.opentripplanner.transit.model._data.TransitModelForTest.id; + +import java.util.List; +import java.util.Objects; +import java.util.stream.IntStream; +import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; +import org.opentripplanner.model.StopTime; +import org.opentripplanner.model.calendar.CalendarServiceData; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.framework.Deduplicator; +import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.site.StopLocation; +import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; +import org.opentripplanner.transit.model.timetable.TripTimes; +import org.opentripplanner.transit.model.timetable.TripTimesFactory; +import org.opentripplanner.transit.service.TransitModel; + +public class RealtimeTestEnvironmentBuilder implements RealtimeTestConstants { + + private RealtimeTestEnvironment.SourceType sourceType; + private final TransitModel transitModel = new TransitModel(STOP_MODEL, new Deduplicator()); + + RealtimeTestEnvironmentBuilder withSourceType(RealtimeTestEnvironment.SourceType sourceType) { + this.sourceType = sourceType; + return this; + } + + public RealtimeTestEnvironmentBuilder addTrip(TripInput trip) { + createTrip(trip); + transitModel.index(); + return this; + } + + public RealtimeTestEnvironment build() { + Objects.requireNonNull(sourceType, "sourceType cannot be null"); + transitModel.initTimeZone(TIME_ZONE); + transitModel.addAgency(TransitModelForTest.AGENCY); + + CalendarServiceData calendarServiceData = new CalendarServiceData(); + calendarServiceData.putServiceDatesForServiceId( + SERVICE_ID, + List.of(SERVICE_DATE.minusDays(1), SERVICE_DATE, SERVICE_DATE.plusDays(1)) + ); + transitModel.getServiceCodes().put(SERVICE_ID, 0); + transitModel.updateCalendarServiceData(true, calendarServiceData, DataImportIssueStore.NOOP); + + return new RealtimeTestEnvironment(sourceType, transitModel); + } + + private Trip createTrip(TripInput tripInput) { + var trip = Trip + .of(id(tripInput.id())) + .withRoute(tripInput.route()) + .withHeadsign(I18NString.of("Headsign of %s".formatted(tripInput.id()))) + .withServiceId(SERVICE_ID) + .build(); + + var tripOnServiceDate = TripOnServiceDate + .of(trip.getId()) + .withTrip(trip) + .withServiceDate(SERVICE_DATE) + .build(); + + transitModel.addTripOnServiceDate(tripOnServiceDate.getId(), tripOnServiceDate); + + if (tripInput.route().getOperator() != null) { + transitModel.getOperators().add(tripInput.route().getOperator()); + } + + var stopTimes = IntStream + .range(0, tripInput.stops().size()) + .mapToObj(i -> { + var stop = tripInput.stops().get(i); + return createStopTime(trip, i, stop.stop(), stop.arrivalTime(), stop.departureTime()); + }) + .toList(); + + TripTimes tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, null); + + final TripPattern pattern = TransitModelForTest + .tripPattern(tripInput.id() + "Pattern", tripInput.route()) + .withStopPattern( + TransitModelForTest.stopPattern( + tripInput.stops().stream().map(TripInput.StopCall::stop).toList() + ) + ) + .withScheduledTimeTableBuilder(builder -> builder.addTripTimes(tripTimes)) + .build(); + + transitModel.addTripPattern(pattern.getId(), pattern); + + return trip; + } + + private static StopTime createStopTime( + Trip trip, + int stopSequence, + StopLocation stop, + int arrivalTime, + int departureTime + ) { + var st = new StopTime(); + st.setTrip(trip); + st.setStopSequence(stopSequence); + st.setStop(stop); + st.setArrivalTime(arrivalTime); + st.setDepartureTime(departureTime); + return st; + } +} diff --git a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java index e53ba07cbcf..21d6591485c 100644 --- a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotSourceTest.java @@ -19,7 +19,6 @@ import java.time.Duration; import java.time.LocalDate; import java.util.List; -import javax.annotation.Nonnull; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.opentripplanner.ConstantsForTests; @@ -282,7 +281,6 @@ public void testHandleModifiedTrip() { } } - @Nonnull private TimetableSnapshotSource defaultUpdater() { return new TimetableSnapshotSource( new TimetableSnapshotSourceParameters(Duration.ZERO, true), diff --git a/src/test/java/org/opentripplanner/updater/trip/TripInput.java b/src/test/java/org/opentripplanner/updater/trip/TripInput.java new file mode 100644 index 00000000000..e4d9309061a --- /dev/null +++ b/src/test/java/org/opentripplanner/updater/trip/TripInput.java @@ -0,0 +1,47 @@ +package org.opentripplanner.updater.trip; + +import java.util.ArrayList; +import java.util.List; +import org.opentripplanner.framework.time.TimeUtils; +import org.opentripplanner.transit.model.network.Route; +import org.opentripplanner.transit.model.site.RegularStop; + +/** + * A simple data structure that is used by the {@link RealtimeTestEnvironment} to create + * trips, trips on date and patterns. + */ +public record TripInput(String id, Route route, List stops) { + public static TripInputBuilder of(String id) { + return new TripInputBuilder(id); + } + + public static class TripInputBuilder implements RealtimeTestConstants { + + private final String id; + private final List stops = new ArrayList<>(); + // can be made configurable if needed + private Route route = ROUTE_1; + + TripInputBuilder(String id) { + this.id = id; + } + + public TripInputBuilder addStop(RegularStop stopId, String arrivalTime, String departureTime) { + this.stops.add( + new StopCall(stopId, TimeUtils.time(arrivalTime), TimeUtils.time(departureTime)) + ); + return this; + } + + public TripInput build() { + return new TripInput(id, route, stops); + } + + public TripInputBuilder withRoute(Route route) { + this.route = route; + return this; + } + } + + record StopCall(RegularStop stop, int arrivalTime, int departureTime) {} +} diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java index 2e0b9d3d88e..6b662e8c8f3 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java @@ -7,10 +7,6 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertSuccess; -import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; -import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.STOP_A1_ID; -import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.STOP_B1_ID; -import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.STOP_C1_ID; import de.mfdz.MfdzRealtimeExtensions.StopTimePropertiesExtension.DropOffPickupType; import java.util.List; @@ -23,18 +19,19 @@ import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.updater.spi.UpdateSuccess; +import org.opentripplanner.updater.trip.RealtimeTestConstants; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; import org.opentripplanner.updater.trip.TripUpdateBuilder; -class AddedTest { +class AddedTest implements RealtimeTestConstants { final String ADDED_TRIP_ID = "added_trip"; @Test void addedTrip() { - var env = RealtimeTestEnvironment.gtfs(); + var env = RealtimeTestEnvironment.gtfs().build(); - var tripUpdate = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone) + var tripUpdate = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, TIME_ZONE) .addStopTime(STOP_A1_ID, 30) .addStopTime(STOP_B1_ID, 40) .addStopTime(STOP_C1_ID, 55) @@ -46,8 +43,8 @@ void addedTrip() { @Test void addedTripWithNewRoute() { - var env = RealtimeTestEnvironment.gtfs(); - var tripUpdate = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone) + var env = RealtimeTestEnvironment.gtfs().build(); + var tripUpdate = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, TIME_ZONE) .addTripExtension() .addStopTime(STOP_A1_ID, 30, DropOffPickupType.PHONE_AGENCY) .addStopTime(STOP_B1_ID, 40, DropOffPickupType.COORDINATE_WITH_DRIVER) @@ -81,8 +78,8 @@ void addedTripWithNewRoute() { @Test void addedWithUnknownStop() { - var env = RealtimeTestEnvironment.gtfs(); - var tripUpdate = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone) + var env = RealtimeTestEnvironment.gtfs().build(); + var tripUpdate = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, TIME_ZONE) // add extension to set route name, url, mode .addTripExtension() .addStopTime(STOP_A1_ID, 30, DropOffPickupType.PHONE_AGENCY) @@ -105,8 +102,8 @@ void addedWithUnknownStop() { @Test void repeatedlyAddedTripWithNewRoute() { - var env = RealtimeTestEnvironment.gtfs(); - var tripUpdate = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, env.timeZone) + var env = RealtimeTestEnvironment.gtfs().build(); + var tripUpdate = new TripUpdateBuilder(ADDED_TRIP_ID, SERVICE_DATE, ADDED, TIME_ZONE) // add extension to set route name, url, mode .addTripExtension() .addStopTime(STOP_A1_ID, 30, DropOffPickupType.PHONE_AGENCY) @@ -135,7 +132,7 @@ private TripPattern assertAddedTrip(String tripId, RealtimeTestEnvironment env) assertNotNull(trip); assertNotNull(transitService.getPatternForTrip(trip)); - var stopA = env.transitModel.getStopModel().getRegularStop(env.stopA1.getId()); + var stopA = env.transitModel.getStopModel().getRegularStop(STOP_A1.getId()); // Get the trip pattern of the added trip which goes through stopA var patternsAtA = env.getTimetableSnapshot().getPatternsForStop(stopA); diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java index c85225b7828..b8bd5c4574b 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/cancellation/CancellationDeletionTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.transit.model._data.TransitModelForTest.id; import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertSuccess; import static org.opentripplanner.updater.trip.UpdateIncrementality.DIFFERENTIAL; @@ -13,14 +14,16 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.transit.model.timetable.RealTimeState; +import org.opentripplanner.updater.trip.RealtimeTestConstants; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; +import org.opentripplanner.updater.trip.TripInput; import org.opentripplanner.updater.trip.TripUpdateBuilder; /** * Cancellations and deletions should end up in the internal data model and make trips unavailable * for routing. */ -public class CancellationDeletionTest { +public class CancellationDeletionTest implements RealtimeTestConstants { static List cases() { return List.of( @@ -32,22 +35,25 @@ static List cases() { @ParameterizedTest @MethodSource("cases") void cancelledTrip(ScheduleRelationship relationship, RealTimeState state) { - var env = RealtimeTestEnvironment.gtfs(); - var pattern1 = env.getPatternForTrip(env.trip1); + var env = RealtimeTestEnvironment + .gtfs() + .addTrip( + TripInput + .of(TRIP_1_ID) + .addStop(STOP_A1, "0:00:10", "0:00:11") + .addStop(STOP_B1, "0:00:20", "0:00:21") + .build() + ) + .build(); + var pattern1 = env.getPatternForTrip(TRIP_1_ID); - final int tripIndex1 = pattern1.getScheduledTimetable().getTripIndex(env.trip1.getId()); + final int tripIndex1 = pattern1.getScheduledTimetable().getTripIndex(id(TRIP_1_ID)); - var update = new TripUpdateBuilder( - env.trip1.getId().getId(), - RealtimeTestEnvironment.SERVICE_DATE, - relationship, - env.timeZone - ) - .build(); + var update = new TripUpdateBuilder(TRIP_1_ID, SERVICE_DATE, relationship, TIME_ZONE).build(); assertSuccess(env.applyTripUpdate(update)); var snapshot = env.getTimetableSnapshot(); - var forToday = snapshot.resolve(pattern1, RealtimeTestEnvironment.SERVICE_DATE); + var forToday = snapshot.resolve(pattern1, SERVICE_DATE); var schedule = snapshot.resolve(pattern1, null); assertNotSame(forToday, schedule); assertNotSame(forToday.getTripTimes(tripIndex1), schedule.getTripTimes(tripIndex1)); @@ -71,41 +77,34 @@ void cancelledTrip(ScheduleRelationship relationship, RealTimeState state) { @ParameterizedTest @MethodSource("cases") void cancelingAddedTrip(ScheduleRelationship relationship, RealTimeState state) { - var env = RealtimeTestEnvironment.gtfs(); + var env = RealtimeTestEnvironment.gtfs().build(); var addedTripId = "added-trip"; // First add ADDED trip var update = new TripUpdateBuilder( addedTripId, - RealtimeTestEnvironment.SERVICE_DATE, + SERVICE_DATE, ScheduleRelationship.ADDED, - env.timeZone + TIME_ZONE ) - .addStopTime(env.stopA1.getId().getId(), 30) - .addStopTime(env.stopB1.getId().getId(), 40) - .addStopTime(env.stopC1.getId().getId(), 55) + .addStopTime(STOP_A1_ID, 30) + .addStopTime(STOP_B1_ID, 40) + .addStopTime(STOP_C1_ID, 55) .build(); assertSuccess(env.applyTripUpdate(update, DIFFERENTIAL)); // Cancel or delete the added trip - update = - new TripUpdateBuilder( - addedTripId, - RealtimeTestEnvironment.SERVICE_DATE, - relationship, - env.timeZone - ) - .build(); + update = new TripUpdateBuilder(addedTripId, SERVICE_DATE, relationship, TIME_ZONE).build(); assertSuccess(env.applyTripUpdate(update, DIFFERENTIAL)); var snapshot = env.getTimetableSnapshot(); // Get the trip pattern of the added trip which goes through stopA - var patternsAtA = snapshot.getPatternsForStop(env.stopA1); + var patternsAtA = snapshot.getPatternsForStop(STOP_A1); assertNotNull(patternsAtA, "Added trip pattern should be found"); var tripPattern = patternsAtA.stream().findFirst().get(); - var forToday = snapshot.resolve(tripPattern, RealtimeTestEnvironment.SERVICE_DATE); + var forToday = snapshot.resolve(tripPattern, SERVICE_DATE); var schedule = snapshot.resolve(tripPattern, null); assertNotSame(forToday, schedule); diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java index 5298853f36d..f45a82b9ba0 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/DelayedTest.java @@ -5,34 +5,34 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.transit.model._data.TransitModelForTest.id; import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertSuccess; -import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; import org.junit.jupiter.api.Test; -import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.RealTimeState; -import org.opentripplanner.transit.model.timetable.TripTimes; +import org.opentripplanner.updater.trip.RealtimeTestConstants; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; +import org.opentripplanner.updater.trip.TripInput; import org.opentripplanner.updater.trip.TripUpdateBuilder; /** * Delays should be applied to the first trip but should leave the second trip untouched. */ -class DelayedTest { +class DelayedTest implements RealtimeTestConstants { private static final int DELAY = 1; private static final int STOP_SEQUENCE = 1; @Test void singleStopDelay() { - var env = RealtimeTestEnvironment.gtfs(); - - var tripUpdate = new TripUpdateBuilder( - env.trip1.getId().getId(), - RealtimeTestEnvironment.SERVICE_DATE, - SCHEDULED, - env.timeZone - ) + var TRIP_INPUT = TripInput + .of(TRIP_1_ID) + .addStop(STOP_A1, "0:00:10", "0:00:11") + .addStop(STOP_B1, "0:00:20", "0:00:21") + .build(); + var env = RealtimeTestEnvironment.gtfs().addTrip(TRIP_INPUT).build(); + + var tripUpdate = new TripUpdateBuilder(TRIP_1_ID, SERVICE_DATE, SCHEDULED, TIME_ZONE) .addDelayedStopTime(STOP_SEQUENCE, DELAY) .build(); @@ -40,11 +40,11 @@ void singleStopDelay() { assertEquals(1, result.successful()); - var pattern1 = env.getPatternForTrip(env.trip1); - int trip1Index = pattern1.getScheduledTimetable().getTripIndex(env.trip1.getId()); + var pattern1 = env.getPatternForTrip(TRIP_1_ID); + int trip1Index = pattern1.getScheduledTimetable().getTripIndex(id(TRIP_1_ID)); var snapshot = env.getTimetableSnapshot(); - var trip1Realtime = snapshot.resolve(pattern1, RealtimeTestEnvironment.SERVICE_DATE); + var trip1Realtime = snapshot.resolve(pattern1, SERVICE_DATE); var trip1Scheduled = snapshot.resolve(pattern1, null); assertNotSame(trip1Realtime, trip1Scheduled); @@ -59,11 +59,11 @@ void singleStopDelay() { assertEquals( "SCHEDULED | A1 0:00:10 0:00:11 | B1 0:00:20 0:00:21", - env.getScheduledTimetable(env.trip1.getId()) + env.getScheduledTimetable(TRIP_1_ID) ); assertEquals( "UPDATED | A1 [ND] 0:00:10 0:00:11 | B1 0:00:21 0:00:22", - env.getRealtimeTimetable(env.trip1.getId().getId()) + env.getRealtimeTimetable(TRIP_1_ID) ); } @@ -72,11 +72,15 @@ void singleStopDelay() { */ @Test void complexDelay() { - var env = RealtimeTestEnvironment.gtfs(); - - var tripId = env.trip2.getId().getId(); + var tripInput = TripInput + .of(TRIP_2_ID) + .addStop(STOP_A1, "0:01:00", "0:01:01") + .addStop(STOP_B1, "0:01:10", "0:01:11") + .addStop(STOP_C1, "0:01:20", "0:01:21") + .build(); + var env = RealtimeTestEnvironment.gtfs().addTrip(tripInput).build(); - var tripUpdate = new TripUpdateBuilder(tripId, SERVICE_DATE, SCHEDULED, env.timeZone) + var tripUpdate = new TripUpdateBuilder(TRIP_2_ID, SERVICE_DATE, SCHEDULED, TIME_ZONE) .addDelayedStopTime(0, 0) .addDelayedStopTime(1, 60, 80) .addDelayedStopTime(2, 90, 90) @@ -86,19 +90,20 @@ void complexDelay() { var snapshot = env.getTimetableSnapshot(); - final TripPattern originalTripPattern = env.getTransitService().getPatternForTrip(env.trip2); + var trip2 = env.getTransitService().getTripForId(id(TRIP_2_ID)); + var originalTripPattern = env.getTransitService().getPatternForTrip(trip2); var originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); var originalTimetableScheduled = snapshot.resolve(originalTripPattern, null); assertNotSame(originalTimetableForToday, originalTimetableScheduled); - final int originalTripIndexScheduled = originalTimetableScheduled.getTripIndex(tripId); + final int originalTripIndexScheduled = originalTimetableScheduled.getTripIndex(TRIP_2_ID); assertTrue( originalTripIndexScheduled > -1, "Original trip should be found in scheduled time table" ); - final TripTimes originalTripTimesScheduled = originalTimetableScheduled.getTripTimes( + var originalTripTimesScheduled = originalTimetableScheduled.getTripTimes( originalTripIndexScheduled ); assertFalse( @@ -107,7 +112,7 @@ void complexDelay() { ); assertEquals(RealTimeState.SCHEDULED, originalTripTimesScheduled.getRealTimeState()); - final int originalTripIndexForToday = originalTimetableForToday.getTripIndex(tripId); + final int originalTripIndexForToday = originalTimetableForToday.getTripIndex(TRIP_2_ID); assertTrue( originalTripIndexForToday > -1, "Original trip should be found in time table for service date" @@ -115,11 +120,11 @@ void complexDelay() { assertEquals( "SCHEDULED | A1 0:01 0:01:01 | B1 0:01:10 0:01:11 | C1 0:01:20 0:01:21", - env.getScheduledTimetable(env.trip2.getId()) + env.getScheduledTimetable(TRIP_2_ID) ); assertEquals( "UPDATED | A1 0:01 0:01:01 | B1 0:02:10 0:02:31 | C1 0:02:50 0:02:51", - env.getRealtimeTimetable(env.trip2) + env.getRealtimeTimetable(TRIP_2_ID) ); } } diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java index de699324bb6..f9799ba6512 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/delay/SkippedTest.java @@ -6,28 +6,35 @@ import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.opentripplanner.transit.model._data.TransitModelForTest.id; import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertSuccess; -import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; import static org.opentripplanner.updater.trip.UpdateIncrementality.DIFFERENTIAL; import org.junit.jupiter.api.Test; -import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.timetable.RealTimeState; import org.opentripplanner.transit.model.timetable.TripTimesStringBuilder; +import org.opentripplanner.updater.trip.RealtimeTestConstants; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; +import org.opentripplanner.updater.trip.TripInput; import org.opentripplanner.updater.trip.TripUpdateBuilder; /** * A mixture of delayed and skipped stops should result in both delayed and cancelled stops. */ -public class SkippedTest { +class SkippedTest implements RealtimeTestConstants { + + private static final TripInput TRIP_INPUT = TripInput + .of(TRIP_2_ID) + .addStop(STOP_A1, "0:01:00", "0:01:01") + .addStop(STOP_B1, "0:01:10", "0:01:11") + .addStop(STOP_C1, "0:01:20", "0:01:21") + .build(); @Test void scheduledTripWithSkippedAndScheduled() { - var env = RealtimeTestEnvironment.gtfs(); - String scheduledTripId = env.trip2.getId().getId(); + var env = RealtimeTestEnvironment.gtfs().addTrip(TRIP_INPUT).build(); - var tripUpdate = new TripUpdateBuilder(scheduledTripId, SERVICE_DATE, SCHEDULED, env.timeZone) + var tripUpdate = new TripUpdateBuilder(TRIP_2_ID, SERVICE_DATE, SCHEDULED, TIME_ZONE) .addDelayedStopTime(0, 0) .addSkippedStop(1) .addDelayedStopTime(2, 90) @@ -35,13 +42,13 @@ void scheduledTripWithSkippedAndScheduled() { assertSuccess(env.applyTripUpdate(tripUpdate)); - assertOriginalTripPatternIsDeleted(env, env.trip2.getId()); + assertOriginalTripPatternIsDeleted(env, TRIP_2_ID); - assertNewTripTimesIsUpdated(env, env.trip2.getId()); + assertNewTripTimesIsUpdated(env, TRIP_2_ID); assertEquals( "UPDATED | A1 0:01 0:01:01 | B1 [C] 0:01:52 0:01:58 | C1 0:02:50 0:02:51", - env.getRealtimeTimetable(scheduledTripId) + env.getRealtimeTimetable(TRIP_2_ID) ); } @@ -56,10 +63,9 @@ void scheduledTripWithSkippedAndScheduled() { */ @Test void scheduledTripWithPreviouslySkipped() { - var env = RealtimeTestEnvironment.gtfs(); - var tripId = env.trip2.getId(); + var env = RealtimeTestEnvironment.gtfs().addTrip(TRIP_INPUT).build(); - var tripUpdate = new TripUpdateBuilder(tripId.getId(), SERVICE_DATE, SCHEDULED, env.timeZone) + var tripUpdate = new TripUpdateBuilder(TRIP_2_ID, SERVICE_DATE, SCHEDULED, TIME_ZONE) .addDelayedStopTime(0, 0) .addSkippedStop(1) .addDelayedStopTime(2, 90) @@ -68,12 +74,7 @@ void scheduledTripWithPreviouslySkipped() { assertSuccess(env.applyTripUpdate(tripUpdate, DIFFERENTIAL)); // Create update to the same trip but now the skipped stop is no longer skipped - var scheduledBuilder = new TripUpdateBuilder( - tripId.getId(), - SERVICE_DATE, - SCHEDULED, - env.timeZone - ) + var scheduledBuilder = new TripUpdateBuilder(TRIP_2_ID, SERVICE_DATE, SCHEDULED, TIME_ZONE) .addDelayedStopTime(0, 0) .addDelayedStopTime(1, 50) .addDelayedStopTime(2, 90); @@ -87,17 +88,17 @@ void scheduledTripWithPreviouslySkipped() { // stoptime updates have gone through var snapshot = env.getTimetableSnapshot(); - assertNull(snapshot.getRealtimeAddedTripPattern(tripId, SERVICE_DATE)); + assertNull(snapshot.getRealtimeAddedTripPattern(id(TRIP_2_ID), SERVICE_DATE)); - assertNewTripTimesIsUpdated(env, tripId); + assertNewTripTimesIsUpdated(env, TRIP_2_ID); assertEquals( "SCHEDULED | A1 0:01 0:01:01 | B1 0:01:10 0:01:11 | C1 0:01:20 0:01:21", - env.getScheduledTimetable(tripId) + env.getScheduledTimetable(TRIP_2_ID) ); assertEquals( "UPDATED | A1 0:01 0:01:01 | B1 0:02 0:02:01 | C1 0:02:50 0:02:51", - env.getRealtimeTimetable(tripId, SERVICE_DATE) + env.getRealtimeTimetable(id(TRIP_2_ID), SERVICE_DATE) ); } @@ -106,11 +107,11 @@ void scheduledTripWithPreviouslySkipped() { */ @Test void skippedNoData() { - var env = RealtimeTestEnvironment.gtfs(); + var env = RealtimeTestEnvironment.gtfs().addTrip(TRIP_INPUT).build(); - final FeedScopedId tripId = env.trip2.getId(); + String tripId = TRIP_2_ID; - var tripUpdate = new TripUpdateBuilder(tripId.getId(), SERVICE_DATE, SCHEDULED, env.timeZone) + var tripUpdate = new TripUpdateBuilder(tripId, SERVICE_DATE, SCHEDULED, TIME_ZONE) .addNoDataStop(0) .addSkippedStop(1) .addNoDataStop(2) @@ -124,15 +125,15 @@ void skippedNoData() { assertEquals( "UPDATED | A1 [ND] 0:01 0:01:01 | B1 [C] 0:01:10 0:01:11 | C1 [ND] 0:01:20 0:01:21", - env.getRealtimeTimetable(env.trip2) + env.getRealtimeTimetable(tripId) ); } private static void assertOriginalTripPatternIsDeleted( RealtimeTestEnvironment env, - FeedScopedId tripId + String tripId ) { - var trip = env.getTransitService().getTripForId(tripId); + var trip = env.getTransitService().getTripForId(id(tripId)); var originalTripPattern = env.getTransitService().getPatternForTrip(trip); var snapshot = env.getTimetableSnapshot(); var originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); @@ -174,11 +175,9 @@ private static void assertOriginalTripPatternIsDeleted( assertEquals(RealTimeState.DELETED, originalTripTimesForToday.getRealTimeState()); } - private static void assertNewTripTimesIsUpdated( - RealtimeTestEnvironment env, - FeedScopedId tripId - ) { - var originalTripPattern = env.getTransitService().getPatternForTrip(env.trip2); + private static void assertNewTripTimesIsUpdated(RealtimeTestEnvironment env, String tripId) { + var trip = env.getTransitService().getTripForId(id(tripId)); + var originalTripPattern = env.getTransitService().getPatternForTrip(trip); var snapshot = env.getTimetableSnapshot(); var originalTimetableForToday = snapshot.resolve(originalTripPattern, SERVICE_DATE); diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java index da362451753..2ba6749b4b0 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidInputTest.java @@ -4,20 +4,21 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opentripplanner.updater.spi.UpdateError.UpdateErrorType.NO_SERVICE_ON_DATE; import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertFailure; -import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; import java.time.LocalDate; import java.util.List; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.updater.trip.RealtimeTestConstants; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; +import org.opentripplanner.updater.trip.TripInput; import org.opentripplanner.updater.trip.TripUpdateBuilder; /** * A trip with start date that is outside the service period shouldn't throw an exception and is * ignored instead. */ -class InvalidInputTest { +class InvalidInputTest implements RealtimeTestConstants { public static List cases() { return List.of(SERVICE_DATE.minusYears(10), SERVICE_DATE.plusYears(10)); @@ -26,9 +27,14 @@ public static List cases() { @ParameterizedTest @MethodSource("cases") void invalidTripDate(LocalDate date) { - var env = RealtimeTestEnvironment.gtfs(); + var tripInput = TripInput + .of(TRIP_1_ID) + .addStop(STOP_A1, "0:00:10", "0:00:11") + .addStop(STOP_B1, "0:00:20", "0:00:21") + .build(); + var env = RealtimeTestEnvironment.gtfs().addTrip(tripInput).build(); - var update = new TripUpdateBuilder(env.trip1.getId().getId(), date, SCHEDULED, env.timeZone) + var update = new TripUpdateBuilder(TRIP_1_ID, date, SCHEDULED, TIME_ZONE) .addDelayedStopTime(2, 60, 80) .build(); diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java index 83c2547dbc7..699e8fe865c 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/rejection/InvalidTripIdTest.java @@ -22,7 +22,7 @@ static Stream invalidCases() { @ParameterizedTest(name = "tripId=\"{0}\"") @MethodSource("invalidCases") void invalidTripId(String tripId) { - var env = RealtimeTestEnvironment.gtfs(); + var env = RealtimeTestEnvironment.gtfs().build(); var tripDescriptorBuilder = GtfsRealtime.TripDescriptor.newBuilder(); if (tripId != null) { tripDescriptorBuilder.setTripId(tripId); diff --git a/src/test/java/org/opentripplanner/updater/vehicle_position/RealtimeVehicleMatcherTest.java b/src/test/java/org/opentripplanner/updater/vehicle_position/RealtimeVehicleMatcherTest.java index ea5d86cd0e1..4f10fb2d64b 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_position/RealtimeVehicleMatcherTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_position/RealtimeVehicleMatcherTest.java @@ -383,10 +383,10 @@ private static TripPattern tripPattern(Trip trip, List stopTimes) { .of(trip.getId()) .withStopPattern(stopPattern) .withRoute(ROUTE) + .withScheduledTimeTableBuilder(builder -> + builder.addTripTimes(TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator())) + ) .build(); - pattern - .getScheduledTimetable() - .addTripTimes(TripTimesFactory.tripTimes(trip, stopTimes, new Deduplicator())); return pattern; } diff --git a/src/test/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdaterTest.java b/src/test/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdaterTest.java index 0016bf4c00c..17eb3fb3c63 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdaterTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdaterTest.java @@ -9,7 +9,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.junit.jupiter.api.Test; import org.opentripplanner.routing.graph.Graph; @@ -81,7 +80,6 @@ private boolean hasFailed() { static class FakeParams implements VehicleRentalDataSourceParameters { - @Nonnull @Override public String url() { return "https://example.com"; @@ -93,13 +91,11 @@ public String network() { return "Test"; } - @Nonnull @Override public VehicleRentalSourceType sourceType() { return VehicleRentalSourceType.GBFS; } - @Nonnull @Override public HttpHeaders httpHeaders() { return HttpHeaders.empty(); diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-extended.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-extended.json deleted file mode 100644 index ea58480be8e..00000000000 --- a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-extended.json +++ /dev/null @@ -1,344 +0,0 @@ -{ - "data" : { - "plan" : { - "itineraries" : [ - { - "start" : "2020-02-02T11:00:00Z", - "end" : "2020-02-02T12:00:00Z", - "startTime" : 1580641200000, - "endTime" : 1580644800000, - "generalizedCost" : 4072, - "accessibilityScore" : 0.5, - "emissionsPerPerson" : { - "co2" : 123.0 - }, - "numberOfTransfers" : 1, - "walkDistance" : 28.0, - "walkTime" : 20, - "legs" : [ - { - "mode" : "WALK", - "start" : { - "scheduledTime" : "2020-02-02T11:00:00Z", - "estimated" : null - }, - "end" : { - "scheduledTime" : "2020-02-02T11:00:20Z", - "estimated" : null - }, - "from" : { - "name" : "A", - "lat" : 5.0, - "lon" : 8.0, - "arrival" : { - "scheduledTime" : "2020-02-02T11:00:00Z", - "estimated" : null - }, - "departure" : { - "scheduledTime" : "2020-02-02T11:00:00Z", - "estimated" : null - }, - "departureTime" : 1580641200000, - "arrivalTime" : 1580641200000 - }, - "to" : { - "name" : "B", - "lat" : 6.0, - "lon" : 8.5, - "arrival" : { - "scheduledTime" : "2020-02-02T11:00:20Z", - "estimated" : null - }, - "departure" : { - "scheduledTime" : "2020-02-02T11:00:20Z", - "estimated" : null - }, - "departureTime" : 1580641220000, - "arrivalTime" : 1580641220000 - }, - "startTime" : 1580641200000, - "endTime" : 1580641220000, - "generalizedCost" : 40, - "headsign" : null, - "trip" : null, - "intermediatePlaces" : null, - "alerts" : [ ], - "rideHailingEstimate" : null, - "accessibilityScore" : null - }, - { - "mode" : "BUS", - "start" : { - "scheduledTime" : "2020-02-02T10:51:00Z", - "estimated" : { - "time" : "2020-02-02T11:01:00Z", - "delay" : "PT10M" - } - }, - "end" : { - "scheduledTime" : "2020-02-02T11:05:00Z", - "estimated" : { - "time" : "2020-02-02T11:15:00Z", - "delay" : "PT10M" - } - }, - "from" : { - "name" : "B", - "lat" : 6.0, - "lon" : 8.5, - "arrival" : { - "scheduledTime" : "2020-02-02T10:51:00Z", - "estimated" : { - "delay" : "PT10M", - "time" : "2020-02-02T11:01:00Z" - } - }, - "departure" : { - "scheduledTime" : "2020-02-02T10:51:00Z", - "estimated" : { - "delay" : "PT10M", - "time" : "2020-02-02T11:01:00Z" - } - }, - "departureTime" : 1580641260000, - "arrivalTime" : 1580641260000 - }, - "to" : { - "name" : "C", - "lat" : 7.0, - "lon" : 9.0, - "arrival" : { - "scheduledTime" : "2020-02-02T11:05:00Z", - "estimated" : { - "delay" : "PT10M", - "time" : "2020-02-02T11:15:00Z" - } - }, - "departure" : { - "scheduledTime" : "2020-02-02T11:05:00Z", - "estimated" : { - "delay" : "PT10M", - "time" : "2020-02-02T11:15:00Z" - } - }, - "departureTime" : 1580642100000, - "arrivalTime" : 1580642100000 - }, - "startTime" : 1580641260000, - "endTime" : 1580642100000, - "generalizedCost" : 992, - "headsign" : "Headsign at boarding (stop index 5)", - "trip" : { - "tripHeadsign" : "Trip headsign 122" - }, - "intermediatePlaces" : [ - { - "arrival" : { - "scheduledTime" : "2020-02-02T11:01:00Z", - "estimated" : { - "time" : "2020-02-02T11:11:00Z", - "delay" : "PT10M" - } - }, - "departure" : { - "scheduledTime" : "2020-02-02T11:01:00Z", - "estimated" : { - "time" : "2020-02-02T11:11:00Z", - "delay" : "PT10M" - } - }, - "stop" : { - "name" : "B" - } - } - ], - "alerts" : [ ], - "rideHailingEstimate" : null, - "accessibilityScore" : null - }, - { - "mode" : "RAIL", - "start" : { - "scheduledTime" : "2020-02-02T11:20:00Z", - "estimated" : { - "time" : "2020-02-02T11:30:00Z", - "delay" : "PT10M" - } - }, - "end" : { - "scheduledTime" : "2020-02-02T11:40:00Z", - "estimated" : { - "time" : "2020-02-02T11:50:00Z", - "delay" : "PT10M" - } - }, - "from" : { - "name" : "C", - "lat" : 7.0, - "lon" : 9.0, - "arrival" : { - "scheduledTime" : "2020-02-02T11:20:00Z", - "estimated" : { - "delay" : "PT10M", - "time" : "2020-02-02T11:30:00Z" - } - }, - "departure" : { - "scheduledTime" : "2020-02-02T11:20:00Z", - "estimated" : { - "delay" : "PT10M", - "time" : "2020-02-02T11:30:00Z" - } - }, - "departureTime" : 1580643000000, - "arrivalTime" : 1580643000000 - }, - "to" : { - "name" : "D", - "lat" : 8.0, - "lon" : 9.5, - "arrival" : { - "scheduledTime" : "2020-02-02T11:40:00Z", - "estimated" : { - "delay" : "PT10M", - "time" : "2020-02-02T11:50:00Z" - } - }, - "departure" : { - "scheduledTime" : "2020-02-02T11:40:00Z", - "estimated" : { - "delay" : "PT10M", - "time" : "2020-02-02T11:50:00Z" - } - }, - "departureTime" : 1580644200000, - "arrivalTime" : 1580644200000 - }, - "startTime" : 1580643000000, - "endTime" : 1580644200000, - "generalizedCost" : 2040, - "headsign" : "Headsign at boarding (stop index 5)", - "trip" : { - "tripHeadsign" : "Trip headsign 439" - }, - "intermediatePlaces" : [ - { - "arrival" : { - "scheduledTime" : "2020-02-02T11:30:00Z", - "estimated" : { - "time" : "2020-02-02T11:40:00Z", - "delay" : "PT10M" - } - }, - "departure" : { - "scheduledTime" : "2020-02-02T11:30:00Z", - "estimated" : { - "time" : "2020-02-02T11:40:00Z", - "delay" : "PT10M" - } - }, - "stop" : { - "name" : "C" - } - } - ], - "alerts" : [ - { - "id" : "QWxlcnQ6Rjphbi1hbGVydA", - "alertHeaderText" : "A header", - "alertDescriptionText" : "A description", - "alertEffect" : "REDUCED_SERVICE", - "alertCause" : "MAINTENANCE", - "alertSeverityLevel" : "SEVERE", - "alertUrl" : "https://example.com", - "effectiveStartDate" : 1676459008, - "effectiveEndDate" : 1676545408, - "entities" : [ - { - "name" : "A", - "gtfsId" : "F:A", - "lat" : 5.0, - "lon" : 8.0 - } - ] - } - ], - "rideHailingEstimate" : null, - "accessibilityScore" : null - }, - { - "mode" : "CAR", - "start" : { - "scheduledTime" : "2020-02-02T11:50:00Z", - "estimated" : null - }, - "end" : { - "scheduledTime" : "2020-02-02T12:00:00Z", - "estimated" : null - }, - "from" : { - "name" : "D", - "lat" : 8.0, - "lon" : 9.5, - "arrival" : { - "scheduledTime" : "2020-02-02T11:50:00Z", - "estimated" : null - }, - "departure" : { - "scheduledTime" : "2020-02-02T11:50:00Z", - "estimated" : null - }, - "departureTime" : 1580644200000, - "arrivalTime" : 1580644200000 - }, - "to" : { - "name" : "E", - "lat" : 9.0, - "lon" : 10.0, - "arrival" : { - "scheduledTime" : "2020-02-02T12:00:00Z", - "estimated" : null - }, - "departure" : { - "scheduledTime" : "2020-02-02T12:00:00Z", - "estimated" : null - }, - "departureTime" : 1580644800000, - "arrivalTime" : 1580644800000 - }, - "startTime" : 1580644200000, - "endTime" : 1580644800000, - "generalizedCost" : 1000, - "headsign" : null, - "trip" : null, - "intermediatePlaces" : null, - "alerts" : [ ], - "rideHailingEstimate" : { - "provider" : { - "id" : "uber" - }, - "productName" : "UberX", - "minPrice" : { - "currency" : { - "code" : "EUR", - "digits" : 2 - }, - "amount" : 10.0 - }, - "maxPrice" : { - "currency" : { - "code" : "EUR", - "digits" : 2 - }, - "amount" : 20.0 - }, - "arrival" : "PT10M" - }, - "accessibilityScore" : null - } - ] - } - ] - } - } -} \ No newline at end of file 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 deleted file mode 100644 index 1defc81ded1..00000000000 --- a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-fares.json +++ /dev/null @@ -1,191 +0,0 @@ -{ - "data" : { - "plan" : { - "itineraries" : [ - { - "legs" : [ - { - "mode" : "WALK", - "from" : { - "name" : "A", - "lat" : 5.0, - "lon" : 8.0, - "departureTime" : 1580641200000, - "arrivalTime" : 1580641200000 - }, - "to" : { - "name" : "B", - "lat" : 6.0, - "lon" : 8.5, - "departureTime" : 1580641220000, - "arrivalTime" : 1580641220000 - }, - "startTime" : 1580641200000, - "endTime" : 1580641220000, - "generalizedCost" : 40, - "fareProducts" : [ ] - }, - { - "mode" : "BUS", - "from" : { - "name" : "B", - "lat" : 6.0, - "lon" : 8.5, - "departureTime" : 1580641260000, - "arrivalTime" : 1580641260000 - }, - "to" : { - "name" : "C", - "lat" : 7.0, - "lon" : 9.0, - "departureTime" : 1580642100000, - "arrivalTime" : 1580642100000 - }, - "startTime" : 1580641260000, - "endTime" : 1580642100000, - "generalizedCost" : 992, - "fareProducts" : [ - { - "id" : "5d8f889c-42cb-3bcc-89d5-480b995c78c8", - "product" : { - "id" : "F:day-pass", - "name" : "day-pass", - "__typename" : "DefaultFareProduct", - "price" : { - "currency" : { - "digits" : 2, - "code" : "EUR" - }, - "amount" : 10.0 - }, - "riderCategory" : { - "id" : "F:senior-citizens", - "name" : "Senior citizens" - }, - "medium" : { - "id" : "F:oyster", - "name" : "TfL Oyster Card" - } - } - }, - { - "id" : "09bb5f2b-6af9-3355-8b5d-5e93a27ce280", - "product" : { - "id" : "F:single-ticket", - "name" : "single-ticket", - "__typename" : "DefaultFareProduct", - "price" : { - "currency" : { - "digits" : 2, - "code" : "EUR" - }, - "amount" : 10.0 - }, - "riderCategory" : { - "id" : "F:senior-citizens", - "name" : "Senior citizens" - }, - "medium" : { - "id" : "F:oyster", - "name" : "TfL Oyster Card" - } - } - } - ] - }, - { - "mode" : "RAIL", - "from" : { - "name" : "C", - "lat" : 7.0, - "lon" : 9.0, - "departureTime" : 1580643000000, - "arrivalTime" : 1580643000000 - }, - "to" : { - "name" : "D", - "lat" : 8.0, - "lon" : 9.5, - "departureTime" : 1580644200000, - "arrivalTime" : 1580644200000 - }, - "startTime" : 1580643000000, - "endTime" : 1580644200000, - "generalizedCost" : 2040, - "fareProducts" : [ - { - "id" : "5d8f889c-42cb-3bcc-89d5-480b995c78c8", - "product" : { - "id" : "F:day-pass", - "name" : "day-pass", - "__typename" : "DefaultFareProduct", - "price" : { - "currency" : { - "digits" : 2, - "code" : "EUR" - }, - "amount" : 10.0 - }, - "riderCategory" : { - "id" : "F:senior-citizens", - "name" : "Senior citizens" - }, - "medium" : { - "id" : "F:oyster", - "name" : "TfL Oyster Card" - } - } - }, - { - "id" : "46190ddd-93b0-3136-adb7-a18394f8b0ef", - "product" : { - "id" : "F:single-ticket", - "name" : "single-ticket", - "__typename" : "DefaultFareProduct", - "price" : { - "currency" : { - "digits" : 2, - "code" : "EUR" - }, - "amount" : 10.0 - }, - "riderCategory" : { - "id" : "F:senior-citizens", - "name" : "Senior citizens" - }, - "medium" : { - "id" : "F:oyster", - "name" : "TfL Oyster Card" - } - } - } - ] - }, - { - "mode" : "CAR", - "from" : { - "name" : "D", - "lat" : 8.0, - "lon" : 9.5, - "departureTime" : 1580644200000, - "arrivalTime" : 1580644200000 - }, - "to" : { - "name" : "E", - "lat" : 9.0, - "lon" : 10.0, - "departureTime" : 1580644800000, - "arrivalTime" : 1580644800000 - }, - "startTime" : 1580644200000, - "endTime" : 1580644800000, - "generalizedCost" : 1000, - "fareProducts" : [ ] - } - ], - "fares" : [ ] - } - ] - } - } -} \ No newline at end of file diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-stop-positions.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-stop-positions.json deleted file mode 100644 index 8b7e9771958..00000000000 --- a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-stop-positions.json +++ /dev/null @@ -1,116 +0,0 @@ -{ - "data" : { - "plan" : { - "itineraries" : [ - { - "startTime" : 1580641200000, - "endTime" : 1580644800000, - "generalizedCost" : 4072, - "accessibilityScore" : 0.5, - "legs" : [ - { - "mode" : "WALK", - "from" : { - "name" : "A", - "lat" : 5.0, - "lon" : 8.0, - "departureTime" : 1580641200000, - "arrivalTime" : 1580641200000, - "stopPosition" : null - }, - "to" : { - "name" : "B", - "lat" : 6.0, - "lon" : 8.5, - "departureTime" : 1580641220000, - "arrivalTime" : 1580641220000, - "stopPosition" : null - }, - "startTime" : 1580641200000, - "endTime" : 1580641220000, - "generalizedCost" : 40 - }, - { - "mode" : "BUS", - "from" : { - "name" : "B", - "lat" : 6.0, - "lon" : 8.5, - "departureTime" : 1580641260000, - "arrivalTime" : 1580641260000, - "stopPosition" : { - "__typename" : "PositionAtStop", - "position" : 0 - } - }, - "to" : { - "name" : "C", - "lat" : 7.0, - "lon" : 9.0, - "departureTime" : 1580642100000, - "arrivalTime" : 1580642100000, - "stopPosition" : { - "__typename" : "PositionAtStop", - "position" : 0 - } - }, - "startTime" : 1580641260000, - "endTime" : 1580642100000, - "generalizedCost" : 992 - }, - { - "mode" : "RAIL", - "from" : { - "name" : "C", - "lat" : 7.0, - "lon" : 9.0, - "departureTime" : 1580643000000, - "arrivalTime" : 1580643000000, - "stopPosition" : { - "__typename" : "PositionAtStop", - "position" : 0 - } - }, - "to" : { - "name" : "D", - "lat" : 8.0, - "lon" : 9.5, - "departureTime" : 1580644200000, - "arrivalTime" : 1580644200000, - "stopPosition" : { - "__typename" : "PositionAtStop", - "position" : 0 - } - }, - "startTime" : 1580643000000, - "endTime" : 1580644200000, - "generalizedCost" : 2040 - }, - { - "mode" : "CAR", - "from" : { - "name" : "D", - "lat" : 8.0, - "lon" : 9.5, - "departureTime" : 1580644200000, - "arrivalTime" : 1580644200000, - "stopPosition" : null - }, - "to" : { - "name" : "E", - "lat" : 9.0, - "lon" : 10.0, - "departureTime" : 1580644800000, - "arrivalTime" : 1580644800000, - "stopPosition" : null - }, - "startTime" : 1580644200000, - "endTime" : 1580644800000, - "generalizedCost" : 1000 - } - ] - } - ] - } - } -} \ No newline at end of file diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-tutorial.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-tutorial.json deleted file mode 100644 index d213443f7cd..00000000000 --- a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan-tutorial.json +++ /dev/null @@ -1,130 +0,0 @@ -{ - "data" : { - "plan" : { - "itineraries" : [ - { - "start" : "2020-02-02T11:00:00Z", - "end" : "2020-02-02T12:00:00Z", - "legs" : [ - { - "mode" : "WALK", - "from" : { - "name" : "A", - "lat" : 5.0, - "lon" : 8.0, - "departure" : { - "scheduledTime" : "2020-02-02T11:00:00Z", - "estimated" : null - } - }, - "to" : { - "name" : "B", - "lat" : 6.0, - "lon" : 8.5, - "arrival" : { - "scheduledTime" : "2020-02-02T11:00:20Z", - "estimated" : null - } - }, - "route" : null, - "legGeometry" : null - }, - { - "mode" : "BUS", - "from" : { - "name" : "B", - "lat" : 6.0, - "lon" : 8.5, - "departure" : { - "scheduledTime" : "2020-02-02T10:51:00Z", - "estimated" : { - "time" : "2020-02-02T11:01:00Z", - "delay" : "PT10M" - } - } - }, - "to" : { - "name" : "C", - "lat" : 7.0, - "lon" : 9.0, - "arrival" : { - "scheduledTime" : "2020-02-02T11:05:00Z", - "estimated" : { - "time" : "2020-02-02T11:15:00Z", - "delay" : "PT10M" - } - } - }, - "route" : { - "gtfsId" : "F:BUS", - "longName" : "Long name for BUS", - "shortName" : "RBUS" - }, - "legGeometry" : { - "points" : "_{rc@_d{r@????_ibE_t`B" - } - }, - { - "mode" : "RAIL", - "from" : { - "name" : "C", - "lat" : 7.0, - "lon" : 9.0, - "departure" : { - "scheduledTime" : "2020-02-02T11:20:00Z", - "estimated" : { - "time" : "2020-02-02T11:30:00Z", - "delay" : "PT10M" - } - } - }, - "to" : { - "name" : "D", - "lat" : 8.0, - "lon" : 9.5, - "arrival" : { - "scheduledTime" : "2020-02-02T11:40:00Z", - "estimated" : { - "time" : "2020-02-02T11:50:00Z", - "delay" : "PT10M" - } - } - }, - "route" : { - "gtfsId" : "F:2", - "longName" : null, - "shortName" : "R2" - }, - "legGeometry" : { - "points" : "_evi@_y|u@????_ibE_t`B" - } - }, - { - "mode" : "CAR", - "from" : { - "name" : "D", - "lat" : 8.0, - "lon" : 9.5, - "departure" : { - "scheduledTime" : "2020-02-02T11:50:00Z", - "estimated" : null - } - }, - "to" : { - "name" : "E", - "lat" : 9.0, - "lon" : 10.0, - "arrival" : { - "scheduledTime" : "2020-02-02T12:00:00Z", - "estimated" : null - } - }, - "route" : null, - "legGeometry" : null - } - ] - } - ] - } - } -} \ No newline at end of file diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan.json new file mode 100644 index 00000000000..879c1503438 --- /dev/null +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/plan.json @@ -0,0 +1,74 @@ +{ + "data" : { + "plan" : { + "itineraries" : [ + { + "start" : "2020-02-02T11:00:00Z", + "end" : "2020-02-02T12:00:00Z", + "legs" : [ + { + "mode" : "WALK", + "start" : { + "scheduledTime" : "2020-02-02T11:00:00Z" + }, + "end" : { + "scheduledTime" : "2020-02-02T11:00:20Z" + }, + "from" : { + "name" : "A" + }, + "to" : { + "name" : "B" + } + }, + { + "mode" : "BUS", + "start" : { + "scheduledTime" : "2020-02-02T10:51:00Z" + }, + "end" : { + "scheduledTime" : "2020-02-02T11:05:00Z" + }, + "from" : { + "name" : "B" + }, + "to" : { + "name" : "C" + } + }, + { + "mode" : "RAIL", + "start" : { + "scheduledTime" : "2020-02-02T11:20:00Z" + }, + "end" : { + "scheduledTime" : "2020-02-02T11:40:00Z" + }, + "from" : { + "name" : "C" + }, + "to" : { + "name" : "D" + } + }, + { + "mode" : "CAR", + "start" : { + "scheduledTime" : "2020-02-02T11:50:00Z" + }, + "end" : { + "scheduledTime" : "2020-02-02T12:00:00Z" + }, + "from" : { + "name" : "D" + }, + "to" : { + "name" : "E" + } + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/planConnection-extended.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/planConnection-extended.json new file mode 100644 index 00000000000..07f613ebd7f --- /dev/null +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/planConnection-extended.json @@ -0,0 +1,364 @@ +{ + "data" : { + "planConnection" : { + "searchDateTime" : "2023-01-27T21:08:35+01:00", + "routingErrors" : [ ], + "pageInfo" : { + "hasNextPage" : false, + "hasPreviousPage" : false, + "startCursor" : null, + "endCursor" : null, + "searchWindowUsed" : null + }, + "edges" : [ + { + "cursor" : "NoCursor", + "node" : { + "start" : "2020-02-02T11:00:00Z", + "end" : "2020-02-02T12:00:00Z", + "startTime" : 1580641200000, + "endTime" : 1580644800000, + "generalizedCost" : 4072, + "accessibilityScore" : 0.5, + "emissionsPerPerson" : { + "co2" : 123.0 + }, + "numberOfTransfers" : 1, + "walkDistance" : 28.0, + "walkTime" : 20, + "legs" : [ + { + "mode" : "WALK", + "start" : { + "scheduledTime" : "2020-02-02T11:00:00Z", + "estimated" : null + }, + "end" : { + "scheduledTime" : "2020-02-02T11:00:20Z", + "estimated" : null + }, + "from" : { + "name" : "A", + "lat" : 5.0, + "lon" : 8.0, + "arrival" : { + "scheduledTime" : "2020-02-02T11:00:00Z", + "estimated" : null + }, + "departure" : { + "scheduledTime" : "2020-02-02T11:00:00Z", + "estimated" : null + }, + "departureTime" : 1580641200000, + "arrivalTime" : 1580641200000 + }, + "to" : { + "name" : "B", + "lat" : 6.0, + "lon" : 8.5, + "arrival" : { + "scheduledTime" : "2020-02-02T11:00:20Z", + "estimated" : null + }, + "departure" : { + "scheduledTime" : "2020-02-02T11:00:20Z", + "estimated" : null + }, + "departureTime" : 1580641220000, + "arrivalTime" : 1580641220000 + }, + "startTime" : 1580641200000, + "endTime" : 1580641220000, + "generalizedCost" : 40, + "headsign" : null, + "trip" : null, + "intermediatePlaces" : null, + "alerts" : [ ], + "rideHailingEstimate" : null, + "accessibilityScore" : null, + "id" : null, + "realtimeState" : null + }, + { + "mode" : "BUS", + "start" : { + "scheduledTime" : "2020-02-02T10:51:00Z", + "estimated" : { + "time" : "2020-02-02T11:01:00Z", + "delay" : "PT10M" + } + }, + "end" : { + "scheduledTime" : "2020-02-02T11:05:00Z", + "estimated" : { + "time" : "2020-02-02T11:15:00Z", + "delay" : "PT10M" + } + }, + "from" : { + "name" : "B", + "lat" : 6.0, + "lon" : 8.5, + "arrival" : { + "scheduledTime" : "2020-02-02T10:51:00Z", + "estimated" : { + "delay" : "PT10M", + "time" : "2020-02-02T11:01:00Z" + } + }, + "departure" : { + "scheduledTime" : "2020-02-02T10:51:00Z", + "estimated" : { + "delay" : "PT10M", + "time" : "2020-02-02T11:01:00Z" + } + }, + "departureTime" : 1580641260000, + "arrivalTime" : 1580641260000 + }, + "to" : { + "name" : "C", + "lat" : 7.0, + "lon" : 9.0, + "arrival" : { + "scheduledTime" : "2020-02-02T11:05:00Z", + "estimated" : { + "delay" : "PT10M", + "time" : "2020-02-02T11:15:00Z" + } + }, + "departure" : { + "scheduledTime" : "2020-02-02T11:05:00Z", + "estimated" : { + "delay" : "PT10M", + "time" : "2020-02-02T11:15:00Z" + } + }, + "departureTime" : 1580642100000, + "arrivalTime" : 1580642100000 + }, + "startTime" : 1580641260000, + "endTime" : 1580642100000, + "generalizedCost" : 992, + "headsign" : "Headsign at boarding (stop index 5)", + "trip" : { + "tripHeadsign" : "Trip headsign 122" + }, + "intermediatePlaces" : [ + { + "arrival" : { + "scheduledTime" : "2020-02-02T11:01:00Z", + "estimated" : { + "time" : "2020-02-02T11:11:00Z", + "delay" : "PT10M" + } + }, + "departure" : { + "scheduledTime" : "2020-02-02T11:01:00Z", + "estimated" : { + "time" : "2020-02-02T11:11:00Z", + "delay" : "PT10M" + } + }, + "stop" : { + "name" : "B" + } + } + ], + "alerts" : [ ], + "rideHailingEstimate" : null, + "accessibilityScore" : null, + "id" : "rO0ABXdBABhTQ0hFRFVMRURfVFJBTlNJVF9MRUdfVjMABUY6MTIyAAoyMDIwLTAyLTAyAAAABQAAAAcAA0Y6QgADRjpDAAA=", + "realtimeState" : "UPDATED" + }, + { + "mode" : "RAIL", + "start" : { + "scheduledTime" : "2020-02-02T11:20:00Z", + "estimated" : { + "time" : "2020-02-02T11:30:00Z", + "delay" : "PT10M" + } + }, + "end" : { + "scheduledTime" : "2020-02-02T11:40:00Z", + "estimated" : { + "time" : "2020-02-02T11:50:00Z", + "delay" : "PT10M" + } + }, + "from" : { + "name" : "C", + "lat" : 7.0, + "lon" : 9.0, + "arrival" : { + "scheduledTime" : "2020-02-02T11:20:00Z", + "estimated" : { + "delay" : "PT10M", + "time" : "2020-02-02T11:30:00Z" + } + }, + "departure" : { + "scheduledTime" : "2020-02-02T11:20:00Z", + "estimated" : { + "delay" : "PT10M", + "time" : "2020-02-02T11:30:00Z" + } + }, + "departureTime" : 1580643000000, + "arrivalTime" : 1580643000000 + }, + "to" : { + "name" : "D", + "lat" : 8.0, + "lon" : 9.5, + "arrival" : { + "scheduledTime" : "2020-02-02T11:40:00Z", + "estimated" : { + "delay" : "PT10M", + "time" : "2020-02-02T11:50:00Z" + } + }, + "departure" : { + "scheduledTime" : "2020-02-02T11:40:00Z", + "estimated" : { + "delay" : "PT10M", + "time" : "2020-02-02T11:50:00Z" + } + }, + "departureTime" : 1580644200000, + "arrivalTime" : 1580644200000 + }, + "startTime" : 1580643000000, + "endTime" : 1580644200000, + "generalizedCost" : 2040, + "headsign" : "Headsign at boarding (stop index 5)", + "trip" : { + "tripHeadsign" : "Trip headsign 439" + }, + "intermediatePlaces" : [ + { + "arrival" : { + "scheduledTime" : "2020-02-02T11:30:00Z", + "estimated" : { + "time" : "2020-02-02T11:40:00Z", + "delay" : "PT10M" + } + }, + "departure" : { + "scheduledTime" : "2020-02-02T11:30:00Z", + "estimated" : { + "time" : "2020-02-02T11:40:00Z", + "delay" : "PT10M" + } + }, + "stop" : { + "name" : "C" + } + } + ], + "alerts" : [ + { + "id" : "QWxlcnQ6Rjphbi1hbGVydA", + "alertHeaderText" : "A header", + "alertDescriptionText" : "A description", + "alertEffect" : "REDUCED_SERVICE", + "alertCause" : "MAINTENANCE", + "alertSeverityLevel" : "SEVERE", + "alertUrl" : "https://example.com", + "effectiveStartDate" : 1676459008, + "effectiveEndDate" : 1676545408, + "entities" : [ + { + "name" : "A", + "gtfsId" : "F:A", + "lat" : 5.0, + "lon" : 8.0 + } + ] + } + ], + "rideHailingEstimate" : null, + "accessibilityScore" : null, + "id" : "rO0ABXdBABhTQ0hFRFVMRURfVFJBTlNJVF9MRUdfVjMABUY6NDM5AAoyMDIwLTAyLTAyAAAABQAAAAcAA0Y6QwADRjpEAAA=", + "realtimeState" : "UPDATED" + }, + { + "mode" : "CAR", + "start" : { + "scheduledTime" : "2020-02-02T11:50:00Z", + "estimated" : null + }, + "end" : { + "scheduledTime" : "2020-02-02T12:00:00Z", + "estimated" : null + }, + "from" : { + "name" : "D", + "lat" : 8.0, + "lon" : 9.5, + "arrival" : { + "scheduledTime" : "2020-02-02T11:50:00Z", + "estimated" : null + }, + "departure" : { + "scheduledTime" : "2020-02-02T11:50:00Z", + "estimated" : null + }, + "departureTime" : 1580644200000, + "arrivalTime" : 1580644200000 + }, + "to" : { + "name" : "E", + "lat" : 9.0, + "lon" : 10.0, + "arrival" : { + "scheduledTime" : "2020-02-02T12:00:00Z", + "estimated" : null + }, + "departure" : { + "scheduledTime" : "2020-02-02T12:00:00Z", + "estimated" : null + }, + "departureTime" : 1580644800000, + "arrivalTime" : 1580644800000 + }, + "startTime" : 1580644200000, + "endTime" : 1580644800000, + "generalizedCost" : 1000, + "headsign" : null, + "trip" : null, + "intermediatePlaces" : null, + "alerts" : [ ], + "rideHailingEstimate" : { + "provider" : { + "id" : "uber" + }, + "productName" : "UberX", + "minPrice" : { + "currency" : { + "code" : "EUR", + "digits" : 2 + }, + "amount" : 10.0 + }, + "maxPrice" : { + "currency" : { + "code" : "EUR", + "digits" : 2 + }, + "amount" : 20.0 + }, + "arrival" : "PT10M" + }, + "accessibilityScore" : null, + "id" : null, + "realtimeState" : null + } + ] + } + } + ] + } + } +} \ No newline at end of file diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/planConnection-fares.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/planConnection-fares.json new file mode 100644 index 00000000000..d56142820b5 --- /dev/null +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/planConnection-fares.json @@ -0,0 +1,168 @@ +{ + "data" : { + "planConnection" : { + "edges" : [ + { + "node" : { + "legs" : [ + { + "mode" : "WALK", + "from" : { + "name" : "A", + "lat" : 5.0, + "lon" : 8.0 + }, + "to" : { + "name" : "B", + "lat" : 6.0, + "lon" : 8.5 + }, + "generalizedCost" : 40, + "fareProducts" : [ ] + }, + { + "mode" : "BUS", + "from" : { + "name" : "B", + "lat" : 6.0, + "lon" : 8.5 + }, + "to" : { + "name" : "C", + "lat" : 7.0, + "lon" : 9.0 + }, + "generalizedCost" : 992, + "fareProducts" : [ + { + "id" : "5d8f889c-42cb-3bcc-89d5-480b995c78c8", + "product" : { + "id" : "F:day-pass", + "name" : "day-pass", + "__typename" : "DefaultFareProduct", + "price" : { + "currency" : { + "digits" : 2, + "code" : "EUR" + }, + "amount" : 10.0 + }, + "riderCategory" : { + "id" : "F:senior-citizens", + "name" : "Senior citizens" + }, + "medium" : { + "id" : "F:oyster", + "name" : "TfL Oyster Card" + } + } + }, + { + "id" : "09bb5f2b-6af9-3355-8b5d-5e93a27ce280", + "product" : { + "id" : "F:single-ticket", + "name" : "single-ticket", + "__typename" : "DefaultFareProduct", + "price" : { + "currency" : { + "digits" : 2, + "code" : "EUR" + }, + "amount" : 10.0 + }, + "riderCategory" : { + "id" : "F:senior-citizens", + "name" : "Senior citizens" + }, + "medium" : { + "id" : "F:oyster", + "name" : "TfL Oyster Card" + } + } + } + ] + }, + { + "mode" : "RAIL", + "from" : { + "name" : "C", + "lat" : 7.0, + "lon" : 9.0 + }, + "to" : { + "name" : "D", + "lat" : 8.0, + "lon" : 9.5 + }, + "generalizedCost" : 2040, + "fareProducts" : [ + { + "id" : "5d8f889c-42cb-3bcc-89d5-480b995c78c8", + "product" : { + "id" : "F:day-pass", + "name" : "day-pass", + "__typename" : "DefaultFareProduct", + "price" : { + "currency" : { + "digits" : 2, + "code" : "EUR" + }, + "amount" : 10.0 + }, + "riderCategory" : { + "id" : "F:senior-citizens", + "name" : "Senior citizens" + }, + "medium" : { + "id" : "F:oyster", + "name" : "TfL Oyster Card" + } + } + }, + { + "id" : "46190ddd-93b0-3136-adb7-a18394f8b0ef", + "product" : { + "id" : "F:single-ticket", + "name" : "single-ticket", + "__typename" : "DefaultFareProduct", + "price" : { + "currency" : { + "digits" : 2, + "code" : "EUR" + }, + "amount" : 10.0 + }, + "riderCategory" : { + "id" : "F:senior-citizens", + "name" : "Senior citizens" + }, + "medium" : { + "id" : "F:oyster", + "name" : "TfL Oyster Card" + } + } + } + ] + }, + { + "mode" : "CAR", + "from" : { + "name" : "D", + "lat" : 8.0, + "lon" : 9.5 + }, + "to" : { + "name" : "E", + "lat" : 9.0, + "lon" : 10.0 + }, + "generalizedCost" : 1000, + "fareProducts" : [ ] + } + ] + } + } + ] + } + } +} \ No newline at end of file diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/planConnection-stop-positions.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/planConnection-stop-positions.json new file mode 100644 index 00000000000..dbf73734975 --- /dev/null +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/planConnection-stop-positions.json @@ -0,0 +1,94 @@ +{ + "data" : { + "planConnection" : { + "edges" : [ + { + "node" : { + "start" : "2020-02-02T11:00:00Z", + "end" : "2020-02-02T12:00:00Z", + "generalizedCost" : 4072, + "accessibilityScore" : 0.5, + "legs" : [ + { + "mode" : "WALK", + "from" : { + "name" : "A", + "lat" : 5.0, + "lon" : 8.0, + "stopPosition" : null + }, + "to" : { + "name" : "B", + "lat" : 6.0, + "lon" : 8.5, + "stopPosition" : null + }, + "generalizedCost" : 40 + }, + { + "mode" : "BUS", + "from" : { + "name" : "B", + "lat" : 6.0, + "lon" : 8.5, + "stopPosition" : { + "__typename" : "PositionAtStop", + "position" : 0 + } + }, + "to" : { + "name" : "C", + "lat" : 7.0, + "lon" : 9.0, + "stopPosition" : { + "__typename" : "PositionAtStop", + "position" : 0 + } + }, + "generalizedCost" : 992 + }, + { + "mode" : "RAIL", + "from" : { + "name" : "C", + "lat" : 7.0, + "lon" : 9.0, + "stopPosition" : { + "__typename" : "PositionAtStop", + "position" : 0 + } + }, + "to" : { + "name" : "D", + "lat" : 8.0, + "lon" : 9.5, + "stopPosition" : { + "__typename" : "PositionAtStop", + "position" : 0 + } + }, + "generalizedCost" : 2040 + }, + { + "mode" : "CAR", + "from" : { + "name" : "D", + "lat" : 8.0, + "lon" : 9.5, + "stopPosition" : null + }, + "to" : { + "name" : "E", + "lat" : 9.0, + "lon" : 10.0, + "stopPosition" : null + }, + "generalizedCost" : 1000 + } + ] + } + } + ] + } + } +} \ No newline at end of file diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/planConnection-tutorial.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/planConnection-tutorial.json new file mode 100644 index 00000000000..eff2b223eff --- /dev/null +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/planConnection-tutorial.json @@ -0,0 +1,132 @@ +{ + "data" : { + "planConnection" : { + "edges" : [ + { + "node" : { + "start" : "2020-02-02T11:00:00Z", + "end" : "2020-02-02T12:00:00Z", + "legs" : [ + { + "mode" : "WALK", + "from" : { + "name" : "A", + "lat" : 5.0, + "lon" : 8.0, + "departure" : { + "scheduledTime" : "2020-02-02T11:00:00Z", + "estimated" : null + } + }, + "to" : { + "name" : "B", + "lat" : 6.0, + "lon" : 8.5, + "arrival" : { + "scheduledTime" : "2020-02-02T11:00:20Z", + "estimated" : null + } + }, + "route" : null, + "legGeometry" : null + }, + { + "mode" : "BUS", + "from" : { + "name" : "B", + "lat" : 6.0, + "lon" : 8.5, + "departure" : { + "scheduledTime" : "2020-02-02T10:51:00Z", + "estimated" : { + "time" : "2020-02-02T11:01:00Z", + "delay" : "PT10M" + } + } + }, + "to" : { + "name" : "C", + "lat" : 7.0, + "lon" : 9.0, + "arrival" : { + "scheduledTime" : "2020-02-02T11:05:00Z", + "estimated" : { + "time" : "2020-02-02T11:15:00Z", + "delay" : "PT10M" + } + } + }, + "route" : { + "gtfsId" : "F:BUS", + "longName" : "Long name for BUS", + "shortName" : "RBUS" + }, + "legGeometry" : { + "points" : "_{rc@_d{r@????_ibE_t`B" + } + }, + { + "mode" : "RAIL", + "from" : { + "name" : "C", + "lat" : 7.0, + "lon" : 9.0, + "departure" : { + "scheduledTime" : "2020-02-02T11:20:00Z", + "estimated" : { + "time" : "2020-02-02T11:30:00Z", + "delay" : "PT10M" + } + } + }, + "to" : { + "name" : "D", + "lat" : 8.0, + "lon" : 9.5, + "arrival" : { + "scheduledTime" : "2020-02-02T11:40:00Z", + "estimated" : { + "time" : "2020-02-02T11:50:00Z", + "delay" : "PT10M" + } + } + }, + "route" : { + "gtfsId" : "F:2", + "longName" : null, + "shortName" : "R2" + }, + "legGeometry" : { + "points" : "_evi@_y|u@????_ibE_t`B" + } + }, + { + "mode" : "CAR", + "from" : { + "name" : "D", + "lat" : 8.0, + "lon" : 9.5, + "departure" : { + "scheduledTime" : "2020-02-02T11:50:00Z", + "estimated" : null + } + }, + "to" : { + "name" : "E", + "lat" : 9.0, + "lon" : 10.0, + "arrival" : { + "scheduledTime" : "2020-02-02T12:00:00Z", + "estimated" : null + } + }, + "route" : null, + "legGeometry" : null + } + ] + } + } + ] + } + } +} \ No newline at end of file diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/planConnection.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/planConnection.json deleted file mode 100644 index 53ab016cc93..00000000000 --- a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/planConnection.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "data" : { - "planConnection" : { - "searchDateTime" : "2023-01-27T21:08:35+01:00", - "routingErrors" : [ ], - "pageInfo" : { - "hasNextPage" : false, - "hasPreviousPage" : false, - "startCursor" : null, - "endCursor" : null, - "searchWindowUsed" : null - }, - "edges" : [ - { - "cursor" : "NoCursor", - "node" : { - "start" : "2020-02-02T11:00:00Z", - "end" : "2020-02-02T12:00:00Z", - "legs" : [ - { - "mode" : "WALK" - }, - { - "mode" : "BUS" - }, - { - "mode" : "RAIL" - }, - { - "mode" : "CAR" - } - ] - } - } - ] - } - } -} diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json index 703663dc7ec..be584a875be 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json +++ b/src/test/resources/org/opentripplanner/apis/gtfs/expectations/walk-steps.json @@ -1,35 +1,37 @@ { "data" : { - "plan" : { - "itineraries" : [ + "planConnection" : { + "edges" : [ { - "legs" : [ - { - "steps" : [ - { - "streetName" : "street", - "area" : false, - "relativeDirection" : "DEPART", - "absoluteDirection" : "NORTHEAST" - }, - { - "streetName" : "elevator", - "area" : false, - "relativeDirection" : "ELEVATOR", - "absoluteDirection" : null - } - ] - }, - { - "steps" : [ ] - }, - { - "steps" : [ ] - }, - { - "steps" : [ ] - } - ] + "node" : { + "legs" : [ + { + "steps" : [ + { + "streetName" : "street", + "area" : false, + "relativeDirection" : "DEPART", + "absoluteDirection" : "NORTHEAST" + }, + { + "streetName" : "elevator", + "area" : false, + "relativeDirection" : "ELEVATOR", + "absoluteDirection" : null + } + ] + }, + { + "steps" : [ ] + }, + { + "steps" : [ ] + }, + { + "steps" : [ ] + } + ] + } } ] } diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-extended.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-extended.graphql deleted file mode 100644 index 76bf8aa84e0..00000000000 --- a/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-extended.graphql +++ /dev/null @@ -1,157 +0,0 @@ -{ - plan( - from: { lat: 52.3092, lon: 13.0291 } - to: { lat: 52.5147, lon: 13.3927 } - date: "2023-02-15" - time: "11:37" - parking: { - unpreferredCost: 555 - preferred: [{ not: [{ tags: ["a", "b", "c"] }] }] - filters: [{ select: [{ tags: ["e"] }] }] - } - transportModes: [{ mode: CAR, qualifier: HAIL }] - ) { - itineraries { - start - end - # next two are deprecated - startTime - endTime - generalizedCost - accessibilityScore - emissionsPerPerson { - co2 - } - numberOfTransfers - walkDistance - walkTime - legs { - mode - start { - scheduledTime - estimated { - time - delay - } - } - end { - scheduledTime - estimated { - time - delay - } - } - from { - name - lat - lon - arrival { - scheduledTime - estimated { - delay - time - } - } - departure { - scheduledTime - estimated { - delay - time - } - } - departureTime - arrivalTime - } - to { - name - lat - lon - arrival { - scheduledTime - estimated { - delay - time - } - } - departure { - scheduledTime - estimated { - delay - time - } - } - departureTime - arrivalTime - } - startTime - endTime - mode - generalizedCost - headsign - trip { - tripHeadsign - } - intermediatePlaces { - arrival { - scheduledTime - estimated { - time - delay - } - } - departure { - scheduledTime - estimated { - time - delay - } - } - stop { - name - } - } - alerts { - id - alertHeaderText - alertDescriptionText - alertEffect - alertCause - alertSeverityLevel - alertUrl - effectiveStartDate - effectiveEndDate - entities { - ... on Stop { - name - gtfsId - lat - lon - } - } - } - rideHailingEstimate { - provider { - id - } - productName - minPrice { - currency { - code - digits - } - amount - } - maxPrice { - currency { - code - digits - } - amount - } - arrival - } - accessibilityScore - } - } - } -} diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-fares.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-fares.graphql deleted file mode 100644 index c5081b1c8ed..00000000000 --- a/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-fares.graphql +++ /dev/null @@ -1,71 +0,0 @@ -{ - plan( - from: { lat: 52.3092, lon: 13.0291 } - to: { lat: 52.5147, lon: 13.3927 } - date: "2023-02-15" - time: "11:37" - transportModes: [{ mode: TRANSIT }] - ) { - itineraries { - legs { - mode - from { - name - lat - lon - departureTime - arrivalTime - } - to { - name - lat - lon - departureTime - arrivalTime - } - startTime - endTime - mode - generalizedCost - fareProducts { - id - product { - id - name - __typename - ... on DefaultFareProduct { - price { - currency { - digits - code - } - amount - } - } - riderCategory { - id - name - } - medium { - id - name - } - } - } - } - fares { - type - cents - currency - components { - currency - cents - fareId - routes { - gtfsId - } - } - } - } - } -} diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-stop-positions.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-stop-positions.graphql deleted file mode 100644 index dd2217e4288..00000000000 --- a/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-stop-positions.graphql +++ /dev/null @@ -1,43 +0,0 @@ -{ - plan(fromPlace: "from", toPlace: "to", date: "2023-02-15", time: "11:37") { - itineraries { - startTime - endTime - generalizedCost - accessibilityScore - legs { - mode - from { - name - lat - lon - departureTime - arrivalTime - stopPosition { - __typename - ... on PositionAtStop { - position - } - } - } - to { - name - lat - lon - departureTime - arrivalTime - stopPosition { - __typename - ... on PositionAtStop { - position - } - } - } - startTime - endTime - mode - generalizedCost - } - } - } -} diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-tutorial.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-tutorial.graphql deleted file mode 100644 index dd461060029..00000000000 --- a/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan-tutorial.graphql +++ /dev/null @@ -1,53 +0,0 @@ -{ - plan( - # these coordinates are in Portland, change this to YOUR origin - from: { lat: 45.5552, lon: -122.6534 } - # these coordinates are in Portland, change this to YOUR destination - to: { lat: 45.4908, lon: -122.5519 } - # use the correct date and time of your request - date: "2023-02-15" - time: "11:37" - # choose the transport modes you need - transportModes: [{ mode: WALK }, { mode: TRANSIT }] - ) { - itineraries { - start - end - legs { - mode - from { - name - lat - lon - departure { - scheduledTime - estimated { - time - delay - } - } - } - to { - name - lat - lon - arrival { - scheduledTime - estimated { - time - delay - } - } - } - route { - gtfsId - longName - shortName - } - legGeometry { - points - } - } - } - } -} diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan.graphql new file mode 100644 index 00000000000..6a51cc10d2c --- /dev/null +++ b/src/test/resources/org/opentripplanner/apis/gtfs/queries/plan.graphql @@ -0,0 +1,35 @@ +{ + plan( + from: { lat: 52.3092, lon: 13.0291 } + to: { lat: 52.5147, lon: 13.3927 } + date: "2023-02-15" + time: "11:37" + parking: { + unpreferredCost: 555 + preferred: [{ not: [{ tags: ["a", "b", "c"] }] }] + filters: [{ select: [{ tags: ["e"] }] }] + } + transportModes: [{ mode: CAR, qualifier: HAIL }] + ) { + itineraries { + start + end + legs { + mode + start { + scheduledTime + } + end { + scheduledTime + } + from { + name + } + to { + name + } + mode + } + } + } +} diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection-extended.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection-extended.graphql new file mode 100644 index 00000000000..e0266281aa4 --- /dev/null +++ b/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection-extended.graphql @@ -0,0 +1,240 @@ +{ + planConnection( + dateTime: { earliestDeparture: "2023-06-13T14:30+03:00" } + searchWindow: "PT2H30M" + first: 5 + origin: { + location: { coordinate: { latitude: 45.5552, longitude: -122.6534 } } + label: "Home" + } + destination: { + location: { coordinate: { latitude: 45.4908, longitude: -122.5519 } } + label: "Work" + } + modes: { + directOnly: false + transitOnly: false + direct: [WALK] + transit: { + access: [BICYCLE_RENTAL, WALK] + transfer: [WALK] + egress: [BICYCLE_RENTAL, WALK] + transit: [{ mode: TRAM, cost: { reluctance: 1.3 } }, { mode: BUS }] + } + } + preferences: { + accessibility: { wheelchair: { enabled: true } } + street: { + car: { + reluctance: 6.5 + rental: { + allowedNetworks: ["foo", "bar"] + bannedNetworks: ["foobar"] + } + parking: { + unpreferredCost: 200 + preferred: [{ select: [{ tags: ["best-park"] }] }] + filters: [{ not: [{ tags: ["worst-park"] }] }] + } + } + bicycle: { + reluctance: 3.0 + speed: 7.4 + optimization: { type: SAFEST_STREETS } + boardCost: 200 + walk: { + speed: 1.3 + cost: { mountDismountCost: 100, reluctance: 3.5 } + mountDismountTime: "PT5S" + } + rental: { + destinationBicyclePolicy: { allowKeeping: true, keepingCost: 300 } + allowedNetworks: ["foo", "bar"] + bannedNetworks: ["foobar"] + } + parking: { + unpreferredCost: 200 + preferred: [{ select: [{ tags: ["best-park"] }] }] + filters: [{ not: [{ tags: ["worst-park"] }] }] + } + } + walk: { speed: 2.4, reluctance: 1.5, safetyFactor: 0.5, boardCost: 200 } + } + transit: { + board: { waitReluctance: 3.2, slack: "PT1M30S" } + alight: { slack: "PT0S" } + transfer: { + cost: 200 + slack: "PT2M" + maximumAdditionalTransfers: 2 + maximumTransfers: 5 + } + timetable: { + excludeRealTimeUpdates: false + includePlannedCancellations: false + includeRealTimeCancellations: true + } + } + } + locale: "en" + ) { + searchDateTime + routingErrors { + code + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + searchWindowUsed + } + edges { + cursor + node { + start + end + # next two are deprecated + startTime + endTime + generalizedCost + accessibilityScore + emissionsPerPerson { + co2 + } + numberOfTransfers + walkDistance + walkTime + legs { + mode + start { + scheduledTime + estimated { + time + delay + } + } + end { + scheduledTime + estimated { + time + delay + } + } + from { + name + lat + lon + arrival { + scheduledTime + estimated { + delay + time + } + } + departure { + scheduledTime + estimated { + delay + time + } + } + departureTime + arrivalTime + } + to { + name + lat + lon + arrival { + scheduledTime + estimated { + delay + time + } + } + departure { + scheduledTime + estimated { + delay + time + } + } + departureTime + arrivalTime + } + startTime + endTime + mode + generalizedCost + headsign + trip { + tripHeadsign + } + intermediatePlaces { + arrival { + scheduledTime + estimated { + time + delay + } + } + departure { + scheduledTime + estimated { + time + delay + } + } + stop { + name + } + } + alerts { + id + alertHeaderText + alertDescriptionText + alertEffect + alertCause + alertSeverityLevel + alertUrl + effectiveStartDate + effectiveEndDate + entities { + ... on Stop { + name + gtfsId + lat + lon + } + } + } + rideHailingEstimate { + provider { + id + } + productName + minPrice { + currency { + code + digits + } + amount + } + maxPrice { + currency { + code + digits + } + amount + } + arrival + } + accessibilityScore + id + realtimeState + } + } + } + } +} diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection-fares.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection-fares.graphql new file mode 100644 index 00000000000..7f8972ff208 --- /dev/null +++ b/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection-fares.graphql @@ -0,0 +1,60 @@ +{ + planConnection( + origin: { + location: { coordinate: { latitude: 45.5552, longitude: -122.6534 } } + } + destination: { + location: { coordinate: { latitude: 45.4908, longitude: -122.5519 } } + } + dateTime: { earliestDeparture: "2023-06-13T14:30-07:00" } + modes: { + direct: [WALK] + transit: { transit: [{ mode: BUS }, { mode: RAIL }] } + } + ) { + edges { + node { + legs { + mode + from { + name + lat + lon + } + to { + name + lat + lon + } + mode + generalizedCost + fareProducts { + id + product { + id + name + __typename + ... on DefaultFareProduct { + price { + currency { + digits + code + } + amount + } + } + riderCategory { + id + name + } + medium { + id + name + } + } + } + } + } + } + } +} diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection-stop-positions.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection-stop-positions.graphql new file mode 100644 index 00000000000..74063a93d81 --- /dev/null +++ b/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection-stop-positions.graphql @@ -0,0 +1,51 @@ +{ + planConnection( + origin: { + location: { coordinate: { latitude: 45.5552, longitude: -122.6534 } } + } + destination: { + location: { coordinate: { latitude: 45.4908, longitude: -122.5519 } } + } + dateTime: { earliestDeparture: "2023-06-13T14:30-07:00" } + modes: { + direct: [WALK] + transit: { transit: [{ mode: BUS }, { mode: RAIL }] } + } + ) { + edges { + node { + start + end + generalizedCost + accessibilityScore + legs { + mode + from { + name + lat + lon + stopPosition { + __typename + ... on PositionAtStop { + position + } + } + } + to { + name + lat + lon + stopPosition { + __typename + ... on PositionAtStop { + position + } + } + } + mode + generalizedCost + } + } + } + } +} diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection-tutorial.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection-tutorial.graphql new file mode 100644 index 00000000000..43b0be615c5 --- /dev/null +++ b/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection-tutorial.graphql @@ -0,0 +1,61 @@ +{ + planConnection( + origin: { + # these coordinates are in Portland, change this to YOUR origin + location: { coordinate: { latitude: 45.5552, longitude: -122.6534 } } + } + destination: { + # these coordinates are in Portland, change this to YOUR destination + location: { coordinate: { latitude: 45.4908, longitude: -122.5519 } } + } + # use the correct date and time of your request + dateTime: { earliestDeparture: "2023-06-13T14:30-07:00" } + # choose the transport modes you need + modes: { + direct: [WALK] + transit: { transit: [{ mode: BUS }, { mode: RAIL }] } + } + ) { + edges { + node { + start + end + legs { + mode + from { + name + lat + lon + departure { + scheduledTime + estimated { + time + delay + } + } + } + to { + name + lat + lon + arrival { + scheduledTime + estimated { + time + delay + } + } + } + route { + gtfsId + longName + shortName + } + legGeometry { + points + } + } + } + } + } +} diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection.graphql deleted file mode 100644 index 5691f130119..00000000000 --- a/src/test/resources/org/opentripplanner/apis/gtfs/queries/planConnection.graphql +++ /dev/null @@ -1,103 +0,0 @@ -{ - planConnection( - dateTime: { earliestDeparture: "2023-06-13T14:30+03:00" } - searchWindow: "PT2H30M" - first: 5 - origin: { - location: { coordinate: { latitude: 45.5552, longitude: -122.6534 } } - label: "Home" - } - destination: { - location: { coordinate: { latitude: 45.4908, longitude: -122.5519 } } - label: "Work" - } - modes: { - directOnly: false - transitOnly: false - direct: [WALK] - transit: { - access: [BICYCLE_RENTAL, WALK] - transfer: [WALK] - egress: [BICYCLE_RENTAL, WALK] - transit: [{ mode: TRAM, cost: { reluctance: 1.3 } }, { mode: BUS }] - } - } - preferences: { - accessibility: { wheelchair: { enabled: true } } - street: { - car: { - reluctance: 6.5 - rental: { - allowedNetworks: ["foo", "bar"] - bannedNetworks: ["foobar"] - } - parking: { - unpreferredCost: 200 - preferred: [{ select: [{ tags: ["best-park"] }] }] - filters: [{ not: [{ tags: ["worst-park"] }] }] - } - } - bicycle: { - reluctance: 3.0 - speed: 7.4 - optimization: { type: SAFEST_STREETS } - boardCost: 200 - walk: { - speed: 1.3 - cost: { mountDismountCost: 100, reluctance: 3.5 } - mountDismountTime: "PT5S" - } - rental: { - destinationBicyclePolicy: { allowKeeping: true, keepingCost: 300 } - allowedNetworks: ["foo", "bar"] - bannedNetworks: ["foobar"] - } - parking: { - unpreferredCost: 200 - preferred: [{ select: [{ tags: ["best-park"] }] }] - filters: [{ not: [{ tags: ["worst-park"] }] }] - } - } - walk: { speed: 2.4, reluctance: 1.5, safetyFactor: 0.5, boardCost: 200 } - } - transit: { - board: { waitReluctance: 3.2, slack: "PT1M30S" } - alight: { slack: "PT0S" } - transfer: { - cost: 200 - slack: "PT2M" - maximumAdditionalTransfers: 2 - maximumTransfers: 5 - } - timetable: { - excludeRealTimeUpdates: false - includePlannedCancellations: false - includeRealTimeCancellations: true - } - } - } - locale: "en" - ) { - searchDateTime - routingErrors { - code - } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - searchWindowUsed - } - edges { - cursor - node { - start - end - legs { - mode - } - } - } - } -} diff --git a/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql b/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql index c88958840d4..dd2b96395ad 100644 --- a/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql +++ b/src/test/resources/org/opentripplanner/apis/gtfs/queries/walk-steps.graphql @@ -1,18 +1,26 @@ { - plan( - fromPlace: "from" - toPlace: "to" - date: "2023-02-15" - time: "11:37" - transportModes: [{ mode: WALK }] + planConnection( + origin: { + location: { coordinate: { latitude: 45.5552, longitude: -122.6534 } } + } + destination: { + location: { coordinate: { latitude: 45.4908, longitude: -122.5519 } } + } + dateTime: { earliestDeparture: "2023-06-13T14:30-07:00" } + modes: { + direct: [WALK] + transit: { transit: [{ mode: BUS }, { mode: RAIL }] } + } ) { - itineraries { - legs { - steps { - streetName - area - relativeDirection - absoluteDirection + edges { + node { + legs { + steps { + streetName + area + relativeDirection + absoluteDirection + } } } } diff --git a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json index 5a2ed9572e2..9c7572f4b6e 100644 --- a/src/test/resources/org/opentripplanner/apis/vectortiles/style.json +++ b/src/test/resources/org/opentripplanner/apis/vectortiles/style.json @@ -143,7 +143,8 @@ "StreetTransitEntranceLink", "BoardingLocationToStopLink", "StreetVehicleRentalLink", - "StreetVehicleParkingLink" + "StreetVehicleParkingLink", + "StreetStationCentroidLink" ], "layout" : { "line-cap" : "round", diff --git a/src/test/resources/standalone/config/netex-tutorial/build-config.json b/src/test/resources/standalone/config/netex-tutorial/build-config.json new file mode 100644 index 00000000000..467580a73a3 --- /dev/null +++ b/src/test/resources/standalone/config/netex-tutorial/build-config.json @@ -0,0 +1,20 @@ +{ + "transitFeeds": [ + { + "type": "netex", + "feedId": "NO", + "source": "https://storage.googleapis.com/marduk-production/outbound/netex/rb_norway-aggregated-netex.zip", + "sharedFilePattern": "_stops.xml", + "sharedGroupFilePattern": "_(\\w{3})(_flexible)?_shared_data.xml", + "groupFilePattern": "(\\w{3})_.*\\.xml" + } + ], + "osm": [ + { + "source": "norway.osm.pbf", + "osmTagMapping": "norway", + "timeZone": "Europe/Oslo" + } + ], + "osmCacheDataInMem": true +} \ No newline at end of file diff --git a/src/test/resources/standalone/config/netex-tutorial/router-config.json b/src/test/resources/standalone/config/netex-tutorial/router-config.json new file mode 100644 index 00000000000..c8f21dd527a --- /dev/null +++ b/src/test/resources/standalone/config/netex-tutorial/router-config.json @@ -0,0 +1,19 @@ +{ + "updaters": [ + { + "type": "siri-sx-updater", + "frequency": "1m", + "url": "https://api.entur.io/realtime/v1/services", + "feedId": "NO", + "blockReadinessUntilInitialized": true + }, + { + "type": "siri-et-updater", + "frequency": "1m", + "previewInterval": "1h30m", + "url": "https://api.entur.io/realtime/v1/services", + "feedId": "NO", + "blockReadinessUntilInitialized": true + } + ] +} \ No newline at end of file diff --git a/src/test/resources/standalone/config/router-config.json b/src/test/resources/standalone/config/router-config.json index c526de423c1..f1eac1cb41e 100644 --- a/src/test/resources/standalone/config/router-config.json +++ b/src/test/resources/standalone/config/router-config.json @@ -334,12 +334,6 @@ "Authorization": "${BIKELY_AUTHORIZATION}" } }, - { - "type": "vehicle-parking", - "feedId": "noi", - "sourceType": "noi-open-data-hub", - "url": "https://parking.otp.opendatahub.com/parking/all.json" - }, { "type": "stop-time-updater", "frequency": "1m",