diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml index b48a34e7081..ede129d92d1 100644 --- a/.github/workflows/cibuild.yml +++ b/.github/workflows/cibuild.yml @@ -45,9 +45,11 @@ jobs: - name: Send coverage data to codecov.io if: github.repository_owner == 'opentripplanner' - uses: codecov/codecov-action@v3.1.1 + uses: codecov/codecov-action@v4 with: files: target/site/jacoco/jacoco.xml + token: ${{ secrets.CODECOV_TOKEN }} + verbose: true - name: Deploy to Github Package Registry if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev-1.x' || github.ref == 'refs/heads/dev-2.x') diff --git a/.github/workflows/performance-test.yml b/.github/workflows/performance-test.yml index 1b8201a01ae..60b3e69610d 100644 --- a/.github/workflows/performance-test.yml +++ b/.github/workflows/performance-test.yml @@ -75,7 +75,7 @@ jobs: - name: Set up Maven if: matrix.profile == 'core' || github.ref == 'refs/heads/dev-2.x' - uses: stCarolas/setup-maven@v.4.5 + uses: stCarolas/setup-maven@v5 with: maven-version: 3.8.2 @@ -102,7 +102,7 @@ jobs: - name: Archive travel results file if: matrix.profile == 'core' || github.ref == 'refs/heads/dev-2.x' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.location }}-travelSearch-results.csv path: test/performance/${{ matrix.location }}/travelSearch-results.csv diff --git a/README.md b/README.md index 8d7e3968cb1..4a5fa77c7ee 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ ## Overview [](https://gitter.im/opentripplanner/OpenTripPlanner) +[](https://matrix.to/#/#opentripplanner_OpenTripPlanner:gitter.im) [](https://codecov.io/gh/opentripplanner/OpenTripPlanner) +[](https://github.com/opentripplanner/OpenTripPlanner/graphs/contributors) [](https://hub.docker.com/r/opentripplanner/opentripplanner) OpenTripPlanner (OTP) is an open source multi-modal trip planner, focusing on travel by scheduled diff --git a/client-next/package-lock.json b/client-next/package-lock.json index 03aa0171357..abe2c5d3ed6 100644 --- a/client-next/package-lock.json +++ b/client-next/package-lock.json @@ -12,36 +12,36 @@ "bootstrap": "5.3.3", "graphql": "16.8.1", "graphql-request": "6.1.0", - "maplibre-gl": "4.1.1", - "react": "18.2.0", + "maplibre-gl": "4.1.3", + "react": "18.3.1", "react-bootstrap": "2.10.2", - "react-dom": "18.2.0", + "react-dom": "18.3.1", "react-map-gl": "7.1.7" }, "devDependencies": { "@graphql-codegen/cli": "5.0.2", - "@graphql-codegen/client-preset": "4.2.4", + "@graphql-codegen/client-preset": "4.2.5", "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.4.1", - "@testing-library/react": "14.2.2", - "@types/react": "18.2.70", - "@types/react-dom": "18.2.22", - "@typescript-eslint/eslint-plugin": "7.4.0", - "@typescript-eslint/parser": "7.4.0", + "@testing-library/react": "15.0.6", + "@types/react": "18.3.1", + "@types/react-dom": "18.3.0", + "@typescript-eslint/eslint-plugin": "7.8.0", + "@typescript-eslint/parser": "7.8.0", "@vitejs/plugin-react": "4.2.1", - "@vitest/coverage-v8": "1.4.0", + "@vitest/coverage-v8": "1.5.3", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.29.1", "eslint-plugin-jsx-a11y": "6.8.0", "eslint-plugin-react": "7.34.1", - "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-react-hooks": "4.6.2", "eslint-plugin-react-refresh": "0.4.6", "jsdom": "24.0.0", "prettier": "3.2.5", - "typescript": "5.4.3", - "vite": "5.2.6", - "vitest": "1.4.0" + "typescript": "5.4.5", + "vite": "5.2.11", + "vitest": "1.5.3" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1693,9 +1693,9 @@ } }, "node_modules/@graphql-codegen/client-preset": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.2.4.tgz", - "integrity": "sha512-k1c8v2YxJhhITGQGxViG9asLAoop9m7X9duU7Zztqjc98ooxsUzXICfvAWsH3mLAUibXAx4Ax6BPzKsTtQmBPg==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.2.5.tgz", + "integrity": "sha512-hAdB6HN8EDmkoBtr0bPUN/7NH6svzqbcTDMWBCRXPESXkl7y80po+IXrXUjsSrvhKG8xkNXgJNz/2mjwHzywcA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", @@ -3353,40 +3353,46 @@ } }, "node_modules/@testing-library/dom": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", - "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.0.0.tgz", + "integrity": "sha512-PmJPnogldqoVFf+EwbHvbBJ98MmqASV8kLrBYgsDNxQcFMeIS7JFL48sfyXvuMtgmWO/wMhh25odr+8VhDmn4g==", "dev": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", + "aria-query": "5.3.0", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "pretty-format": "^27.0.2" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/@testing-library/react": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.2.2.tgz", - "integrity": "sha512-SOUuM2ysCvjUWBXTNfQ/ztmnKDmqaiPV3SvoIuyxMUca45rbSWWAT/qB8CUs/JQ/ux/8JFs9DNdFQ3f6jH3crA==", + "version": "15.0.6", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-15.0.6.tgz", + "integrity": "sha512-UlbazRtEpQClFOiYp+1BapMT+xyqWMnE+hh9tn5DQ6gmlE7AIZWcGpzZukmDZuFk3By01oiqOf8lRedLS4k6xQ==", "dev": true, "dependencies": { "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^9.0.0", + "@testing-library/dom": "^10.0.0", "@types/react-dom": "^18.0.0" }, "engines": { - "node": ">=14" + "node": ">=18" }, "peerDependencies": { + "@types/react": "^18.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/@types/aria-query": { @@ -3455,12 +3461,6 @@ "@types/geojson": "*" } }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, "node_modules/@types/js-yaml": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", @@ -3528,19 +3528,18 @@ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/react": { - "version": "18.2.70", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.70.tgz", - "integrity": "sha512-hjlM2hho2vqklPhopNkXkdkeq6Lv8WSZTpr7956zY+3WS5cfYUewtCzsJLsbW5dEv3lfSeQ4W14ZFeKC437JRQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz", + "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==", "dependencies": { "@types/prop-types": "*", - "@types/scheduler": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.2.22", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.22.tgz", - "integrity": "sha512-fHkBXPeNtfvri6gdsMYyW+dW7RXFo6Ad09nLFK0VQWR7yGLai/Cyvyj696gbwYvBnhGtevUG9cET0pmUbMtoPQ==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", "dev": true, "dependencies": { "@types/react": "*" @@ -3554,11 +3553,6 @@ "@types/react": "*" } }, - "node_modules/@types/scheduler": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" - }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", @@ -3588,22 +3582,22 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.4.0.tgz", - "integrity": "sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.8.0.tgz", + "integrity": "sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/type-utils": "7.4.0", - "@typescript-eslint/utils": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.8.0", + "@typescript-eslint/type-utils": "7.8.0", + "@typescript-eslint/utils": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0", "debug": "^4.3.4", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3656,15 +3650,15 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.4.0.tgz", - "integrity": "sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.8.0.tgz", + "integrity": "sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/typescript-estree": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@typescript-eslint/scope-manager": "7.8.0", + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/typescript-estree": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0", "debug": "^4.3.4" }, "engines": { @@ -3684,13 +3678,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.4.0.tgz", - "integrity": "sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.8.0.tgz", + "integrity": "sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0" + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3701,15 +3695,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.4.0.tgz", - "integrity": "sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.8.0.tgz", + "integrity": "sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.4.0", - "@typescript-eslint/utils": "7.4.0", + "@typescript-eslint/typescript-estree": "7.8.0", + "@typescript-eslint/utils": "7.8.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3728,9 +3722,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.4.0.tgz", - "integrity": "sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz", + "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3741,19 +3735,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.4.0.tgz", - "integrity": "sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.8.0.tgz", + "integrity": "sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/visitor-keys": "7.8.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3802,18 +3796,18 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.4.0.tgz", - "integrity": "sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.8.0.tgz", + "integrity": "sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/typescript-estree": "7.4.0", - "semver": "^7.5.4" + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "7.8.0", + "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/typescript-estree": "7.8.0", + "semver": "^7.6.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3860,13 +3854,13 @@ "dev": true }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.4.0.tgz", - "integrity": "sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.8.0.tgz", + "integrity": "sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.4.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.8.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3902,9 +3896,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.4.0.tgz", - "integrity": "sha512-4hDGyH1SvKpgZnIByr9LhGgCEuF9DKM34IBLCC/fVfy24Z3+PZ+Ii9hsVBsHvY1umM1aGPEjceRkzxCfcQ10wg==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.5.3.tgz", + "integrity": "sha512-DPyGSu/fPHOJuPxzFSQoT4N/Fu/2aJfZRtEpEp8GI7NHsXBGE94CQ+pbEGBUMFjatsHPDJw/+TAF9r4ens2CNw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.1", @@ -3919,24 +3913,23 @@ "picocolors": "^1.0.0", "std-env": "^3.5.0", "strip-literal": "^2.0.0", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.2.0" + "test-exclude": "^6.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.4.0" + "vitest": "1.5.3" } }, "node_modules/@vitest/expect": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz", - "integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.3.tgz", + "integrity": "sha512-y+waPz31pOFr3rD7vWTbwiLe5+MgsMm40jTZbQE8p8/qXyBX3CQsIXRx9XK12IbY7q/t5a5aM/ckt33b4PxK2g==", "dev": true, "dependencies": { - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/spy": "1.5.3", + "@vitest/utils": "1.5.3", "chai": "^4.3.10" }, "funding": { @@ -3944,12 +3937,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz", - "integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.3.tgz", + "integrity": "sha512-7PlfuReN8692IKQIdCxwir1AOaP5THfNkp0Uc4BKr2na+9lALNit7ub9l3/R7MP8aV61+mHKRGiqEKRIwu6iiQ==", "dev": true, "dependencies": { - "@vitest/utils": "1.4.0", + "@vitest/utils": "1.5.3", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -3985,9 +3978,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz", - "integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.3.tgz", + "integrity": "sha512-K3mvIsjyKYBhNIDujMD2gfQEzddLe51nNOAf45yKRt/QFJcUIeTQd2trRvv6M6oCBHNVnZwFWbQ4yj96ibiDsA==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -4031,9 +4024,9 @@ "dev": true }, "node_modules/@vitest/spy": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.4.0.tgz", - "integrity": "sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.3.tgz", + "integrity": "sha512-Llj7Jgs6lbnL55WoshJUUacdJfjU2honvGcAJBxhra5TPEzTJH8ZuhI3p/JwqqfnTr4PmP7nDmOXP53MS7GJlg==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -4043,9 +4036,9 @@ } }, "node_modules/@vitest/utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz", - "integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.3.tgz", + "integrity": "sha512-rE9DTN1BRhzkzqNQO+kw8ZgfeEBCLXiHJwetk668shmNBpSagQxneT5eSqEBLP+cqSiAeecvQmbpFfdMyLcIQA==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -4084,9 +4077,9 @@ } }, "node_modules/@vitest/utils/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, "node_modules/@whatwg-node/events": { @@ -4238,12 +4231,12 @@ "dev": true }, "node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "dependencies": { - "deep-equal": "^2.0.5" + "dequal": "^2.0.3" } }, "node_modules/arr-union": { @@ -5290,38 +5283,6 @@ "node": ">=6" } }, - "node_modules/deep-equal": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", - "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.5", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.2", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -5619,26 +5580,6 @@ "node": ">= 0.4" } }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/es-iterator-helpers": { "version": "1.0.18", "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.18.tgz", @@ -5992,15 +5933,6 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -6056,9 +5988,9 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", "dev": true, "engines": { "node": ">=10" @@ -7317,22 +7249,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", @@ -8423,9 +8339,9 @@ } }, "node_modules/maplibre-gl": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.1.1.tgz", - "integrity": "sha512-DmHru9FTHCOngNHzIx9W2+MlUziYPfPxd2qjyeWwczBYNx2SDpmH394MkuCvSgnfUm5Zvs4NaYCqMu44jUga1Q==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.1.3.tgz", + "integrity": "sha512-nMy5h0kzq9Z66C6AIb3p2BvLIVHz75dGGQow22x+h9/VOihr0IPQI26ylAi6lHqvEy2VqjiRmKAMlFwt0xFKfQ==", "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", @@ -8538,9 +8454,9 @@ } }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -8750,22 +8666,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -9405,9 +9305,9 @@ "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dependencies": { "loose-envify": "^1.1.0" }, @@ -9445,15 +9345,15 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.3.1" } }, "node_modules/react-is": { @@ -9884,9 +9784,9 @@ } }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "dependencies": { "loose-envify": "^1.1.0" } @@ -10170,18 +10070,6 @@ "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", "dev": true }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "dev": true, - "dependencies": { - "internal-slot": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -10729,9 +10617,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -10950,20 +10838,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "node_modules/v8-to-istanbul": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, "node_modules/value-or-promise": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", @@ -10974,13 +10848,13 @@ } }, "node_modules/vite": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.6.tgz", - "integrity": "sha512-FPtnxFlSIKYjZ2eosBQamz4CbyrTizbZ3hnGJlh/wMtCrlp1Hah6AzBLjGI5I2urTfNnpovpHdrL6YRuBOPnCA==", + "version": "5.2.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz", + "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==", "dev": true, "dependencies": { "esbuild": "^0.20.1", - "postcss": "^8.4.36", + "postcss": "^8.4.38", "rollup": "^4.13.0" }, "bin": { @@ -11029,9 +10903,9 @@ } }, "node_modules/vite-node": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz", - "integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.3.tgz", + "integrity": "sha512-axFo00qiCpU/JLd8N1gu9iEYL3xTbMbMrbe5nDp9GL0nb6gurIdZLkkFogZXWnE8Oyy5kfSLwNVIcVsnhE7lgQ==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -11051,16 +10925,16 @@ } }, "node_modules/vitest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz", - "integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.3.tgz", + "integrity": "sha512-2oM7nLXylw3mQlW6GXnRriw+7YvZFk/YNV8AxIC3Z3MfFbuziLGWP9GPxxu/7nRlXhqyxBikpamr+lEEj1sUEw==", "dev": true, "dependencies": { - "@vitest/expect": "1.4.0", - "@vitest/runner": "1.4.0", - "@vitest/snapshot": "1.4.0", - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/expect": "1.5.3", + "@vitest/runner": "1.5.3", + "@vitest/snapshot": "1.5.3", + "@vitest/spy": "1.5.3", + "@vitest/utils": "1.5.3", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -11072,9 +10946,9 @@ "std-env": "^3.5.0", "strip-literal": "^2.0.0", "tinybench": "^2.5.1", - "tinypool": "^0.8.2", + "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.4.0", + "vite-node": "1.5.3", "why-is-node-running": "^2.2.2" }, "bin": { @@ -11089,8 +10963,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.4.0", - "@vitest/ui": "1.4.0", + "@vitest/browser": "1.5.3", + "@vitest/ui": "1.5.3", "happy-dom": "*", "jsdom": "*" }, diff --git a/client-next/package.json b/client-next/package.json index 36569ee11f8..1677c6a3b1f 100644 --- a/client-next/package.json +++ b/client-next/package.json @@ -21,35 +21,35 @@ "bootstrap": "5.3.3", "graphql": "16.8.1", "graphql-request": "6.1.0", - "maplibre-gl": "4.1.1", - "react": "18.2.0", + "maplibre-gl": "4.1.3", + "react": "18.3.1", "react-bootstrap": "2.10.2", - "react-dom": "18.2.0", + "react-dom": "18.3.1", "react-map-gl": "7.1.7" }, "devDependencies": { "@graphql-codegen/cli": "5.0.2", - "@graphql-codegen/client-preset": "4.2.4", + "@graphql-codegen/client-preset": "4.2.5", "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.4.1", - "@testing-library/react": "14.2.2", - "@types/react": "18.2.70", - "@types/react-dom": "18.2.22", - "@typescript-eslint/eslint-plugin": "7.4.0", - "@typescript-eslint/parser": "7.4.0", + "@testing-library/react": "15.0.6", + "@types/react": "18.3.1", + "@types/react-dom": "18.3.0", + "@typescript-eslint/eslint-plugin": "7.8.0", + "@typescript-eslint/parser": "7.8.0", "@vitejs/plugin-react": "4.2.1", - "@vitest/coverage-v8": "1.4.0", + "@vitest/coverage-v8": "1.5.3", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.29.1", "eslint-plugin-jsx-a11y": "6.8.0", "eslint-plugin-react": "7.34.1", - "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-react-hooks": "4.6.2", "eslint-plugin-react-refresh": "0.4.6", "jsdom": "24.0.0", "prettier": "3.2.5", - "typescript": "5.4.3", - "vite": "5.2.6", - "vitest": "1.4.0" + "typescript": "5.4.5", + "vite": "5.2.11", + "vitest": "1.5.3" } } diff --git a/doc-templates/VehicleParking.md b/doc-templates/VehicleParking.md index 37988ef7902..5d149e40f9a 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 updater: Leonard Ehrenfried, [mail@leonard.io](mailto:mail@leonard.io) +- For Bikely, NOI and Bikeep updater: Leonard Ehrenfried, [mail@leonard.io](mailto:mail@leonard.io) ## Documentation @@ -16,6 +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/) ### Configuration @@ -39,6 +40,13 @@ All updaters have the following parameters in common: +## NOI Open Data Hub + + + +## Bikeep + + ## Changelog diff --git a/docs/Changelog.md b/docs/Changelog.md index e1773cb0a5e..cafc677ccc6 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -8,6 +8,14 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - ISO-8601 date time for GTFS API itinerary responses [#5660](https://github.com/opentripplanner/OpenTripPlanner/pull/5660) - Fix street routing on roundabout [#5732](https://github.com/opentripplanner/OpenTripPlanner/pull/5732) - Expose route sort order in GTFS API [#5764](https://github.com/opentripplanner/OpenTripPlanner/pull/5764) +- Fix issue with cancellations on trip patterns that run after midnight [#5719](https://github.com/opentripplanner/OpenTripPlanner/pull/5719) +- Fix handling of null transport mode filter [#5789](https://github.com/opentripplanner/OpenTripPlanner/pull/5789) +- Discourage instead of ban cycling on use_sidepath ways and do the same for walking on foot=use_sidepath [#5790](https://github.com/opentripplanner/OpenTripPlanner/pull/5790) +- Prune islands with mode-less stop vertices [#5782](https://github.com/opentripplanner/OpenTripPlanner/pull/5782) +- Overwrite default WALK directMode when it is not set in the request, but modes is set [#5779](https://github.com/opentripplanner/OpenTripPlanner/pull/5779) +- Fix trip duplication in Graph Builder DSJ mapping [#5794](https://github.com/opentripplanner/OpenTripPlanner/pull/5794) +- Optionally abort startup when unknown configuration parameters were detected [#5676](https://github.com/opentripplanner/OpenTripPlanner/pull/5676) +- Fix bug in heuristics cost calculation for egress legs [#5783](https://github.com/opentripplanner/OpenTripPlanner/pull/5783) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.5.0 (2024-03-13) diff --git a/docs/Frontends.md b/docs/Frontends.md index e8b299a52ac..e7946102ea1 100644 --- a/docs/Frontends.md +++ b/docs/Frontends.md @@ -32,7 +32,7 @@ While the "classic" (i.e. old) debug frontend is enabled by default as of this w // otp-config.json { "otpFeatures": { - "DebugClient": true, + "DebugUi": true, "SandboxAPIGeocoder": true } } diff --git a/docs/Migrating-Configuration.md b/docs/Migrating-Configuration.md new file mode 100644 index 00000000000..c6f1ee946f6 --- /dev/null +++ b/docs/Migrating-Configuration.md @@ -0,0 +1,37 @@ +While OTP's GraphQL APIs are very, very stable even across versions, the JSON configuration schema +is not. Changes to it are relatively frequent and you can see all PRs that change it with +the [Github tag 'config change'](https://github.com/opentripplanner/OpenTripPlanner/pulls?q=label%3A%22config+change%22). + +### Migrating the JSON configuration + +OTP validates the configuration and prints warnings during startup. This means that when you +migrate to a newer version, you should carefully inspect the logs. If you see messages like + +``` +(NodeAdapter.java:170) Unexpected config parameter: 'routingDefaults.stairsReluctance:1.65' in 'router-config.json' +``` + +this means there are properties in your configuration that are unknown to OTP and therefore likely +to be a configuration error, perhaps because the schema was changed. In such a case you should +consult the [feature](Configuration.md#otp-features), [router](RouterConfiguration.md) or +[build configuration documentation](BuildConfiguration.md) to find out what the new schema looks like. + +By default, OTP starts up even if unknown configuration parameters exist. This supports forward and backwards +migration of config parameters independent if the OTP version. This allows the configuration to follow +the lifecycle of the environment and still be able to roll back OTP. +Combined with the config parameter substitution it also allows using the same configuration in +different environments. Tip! Rolling out the configuration before rolling out a new +version of OTP, ensure you that you are safe and can roll back later. + +An example: you change the location of the graphs, a critical error occurs afterwards and you need to +roll back. Any member of the dev-ops team should then be confident that they can roll back without +risk - even if the environment changed since the last OTP version was rolled out. + +### Aborting on invalid configuration + +If you want OTP to abort the startup when encountering unknown configuration parameters, you can add +the flag `--abortOnUnknownConfig` to your regular OTP CLI commands. + +This should of course be used wisely as it can also accidentally make your production instances refuse to start up. +For some deployments this is a good solution - especially if the config substitution feature is used to inject +environment specific information. Using this feature in the graph-build phase is less risky, than in the OTP serve phase. \ No newline at end of file diff --git a/docs/RouterConfiguration.md b/docs/RouterConfiguration.md index 503b8c7370f..918717d439f 100644 --- a/docs/RouterConfiguration.md +++ b/docs/RouterConfiguration.md @@ -764,6 +764,12 @@ 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", @@ -838,6 +844,12 @@ Used to group requests when monitoring OTP. "fromDateTime" : "-P1D", "timeout" : 300000 } + }, + { + "type" : "vehicle-parking", + "feedId" : "bikeep", + "sourceType" : "bikeep", + "url" : "https://services.bikeep.com/location/v1/public-areas/no-baia-mobility/locations" } ], "rideHailingServices" : [ diff --git a/docs/UpdaterConfig.md b/docs/UpdaterConfig.md index 973874be66a..3e0907f60ad 100644 --- a/docs/UpdaterConfig.md +++ b/docs/UpdaterConfig.md @@ -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__5__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` | 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__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 | +| [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 | ##### Parameter details -
* This only maps access, egress, direct & transfer modes. Transport modes are set using filters. - * Default modes are WALK for access, egress, direct & transfer. + * Default modes are WALK for access, egress & transfer. */ static RequestModes mapRequestModes(Map modesInput) { RequestModesBuilder mBuilder = RequestModes.of(); @@ -28,9 +28,8 @@ static RequestModes mapRequestModes(Map modesInput) { if (modesInput.containsKey(egressModeKey)) { mBuilder.withEgressMode((StreetMode) modesInput.get(egressModeKey)); } - if (modesInput.containsKey(directModeKey)) { - mBuilder.withDirectMode((StreetMode) modesInput.get(directModeKey)); - } + // An unset directMode should overwrite the walk default, so we don't check for existence first. + mBuilder.withDirectMode((StreetMode) modesInput.get(directModeKey)); return mBuilder.build(); } diff --git a/src/main/java/org/opentripplanner/framework/geometry/GeometryUtils.java b/src/main/java/org/opentripplanner/framework/geometry/GeometryUtils.java index cf634ad0e23..c02ff6151e2 100644 --- a/src/main/java/org/opentripplanner/framework/geometry/GeometryUtils.java +++ b/src/main/java/org/opentripplanner/framework/geometry/GeometryUtils.java @@ -10,8 +10,6 @@ import java.util.stream.Stream; import org.geojson.GeoJsonObject; import org.geojson.LngLatAlt; -import org.geotools.api.referencing.crs.CoordinateReferenceSystem; -import org.geotools.referencing.CRS; import org.locationtech.jts.algorithm.ConvexHull; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.CoordinateSequence; @@ -28,30 +26,12 @@ import org.locationtech.jts.linearref.LengthLocationMap; import org.locationtech.jts.linearref.LinearLocation; import org.locationtech.jts.linearref.LocationIndexedLine; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class GeometryUtils { - private static final Logger LOG = LoggerFactory.getLogger(GeometryUtils.class); - private static final CoordinateSequenceFactory csf = new PackedCoordinateSequenceFactory(); private static final GeometryFactory gf = new GeometryFactory(csf); - /** A shared copy of the WGS84 CRS with longitude-first axis order. */ - public static final CoordinateReferenceSystem WGS84_XY; - - static { - try { - WGS84_XY = CRS.getAuthorityFactory(true).createCoordinateReferenceSystem("EPSG:4326"); - } catch (Exception ex) { - LOG.error("Unable to create longitude-first WGS84 CRS", ex); - throw new RuntimeException( - "Could not create longitude-first WGS84 coordinate reference system." - ); - } - } - public static Geometry makeConvexHull( Collection collection, Function mapToCoordinate diff --git a/src/main/java/org/opentripplanner/graph_builder/module/islandpruning/PruneIslands.java b/src/main/java/org/opentripplanner/graph_builder/module/islandpruning/PruneIslands.java index 00404845349..26349c0d803 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/islandpruning/PruneIslands.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/islandpruning/PruneIslands.java @@ -10,7 +10,6 @@ import java.util.List; import java.util.Map; import java.util.Queue; -import java.util.Set; import java.util.stream.Collectors; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.graph_builder.issues.GraphConnectivity; @@ -33,7 +32,6 @@ import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.request.StreetSearchRequest; import org.opentripplanner.street.search.state.State; -import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.service.TransitModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -252,16 +250,7 @@ private int processIslands( if (island.stopSize() > 0) { //for islands with stops islandsWithStops++; - boolean onlyFerry = true; - for (Iterator vIter = island.stopIterator(); vIter.hasNext();) { - TransitStopVertex v = (TransitStopVertex) vIter.next(); - Set modes = v.getModes(); - // test if stop has other transit modes than FERRY - if (!modes.isEmpty() && !modes.contains(TransitMode.FERRY)) { - onlyFerry = false; - break; - } - } + boolean onlyFerry = island.hasOnlyFerryStops(); // do not remove real islands which have only ferry stops if (!onlyFerry && island.streetSize() < pruningThresholdWithStops * adaptivePruningFactor) { double sizeCoeff = (adaptivePruningFactor > 1.0) @@ -487,8 +476,8 @@ private boolean restrictOrRemove( // note: do not unlink stop if only CAR mode is pruned // maybe this needs more logic for flex routing cases List stopLabels = new ArrayList<>(); - for (Iterator vIter = island.stopIterator(); vIter.hasNext();) { - Vertex v = vIter.next(); + for (Iterator vIter = island.stopIterator(); vIter.hasNext();) { + TransitStopVertex v = vIter.next(); stopLabels.add(v.getLabel()); Collection edges = new ArrayList<>(v.getOutgoing()); edges.addAll(v.getIncoming()); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/islandpruning/Subgraph.java b/src/main/java/org/opentripplanner/graph_builder/module/islandpruning/Subgraph.java index 019c1c12a19..94dcfaaf131 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/islandpruning/Subgraph.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/islandpruning/Subgraph.java @@ -17,11 +17,12 @@ import org.opentripplanner.street.model.vertex.OsmVertex; import org.opentripplanner.street.model.vertex.TransitStopVertex; import org.opentripplanner.street.model.vertex.Vertex; +import org.opentripplanner.transit.model.basic.TransitMode; class Subgraph { private final Set streetVertexSet; - private final Set stopsVertexSet; + private final Set stopsVertexSet; Subgraph() { streetVertexSet = new HashSet<>(); @@ -29,8 +30,8 @@ class Subgraph { } void addVertex(Vertex vertex) { - if (vertex instanceof TransitStopVertex) { - stopsVertexSet.add(vertex); + if (vertex instanceof TransitStopVertex transitStopVertex) { + stopsVertexSet.add(transitStopVertex); } else { streetVertexSet.add(vertex); } @@ -64,7 +65,7 @@ Iterator streetIterator() { return streetVertexSet.iterator(); } - Iterator stopIterator() { + Iterator stopIterator() { return stopsVertexSet.iterator(); } @@ -98,8 +99,7 @@ Iterator stopIterator() { Vertex vx = vIter.next(); envelope.expandToInclude(vx.getCoordinate()); } - for (Iterator vIter = stopIterator(); vIter.hasNext();) { - Vertex vx = vIter.next(); + for (TransitStopVertex vx : stopsVertexSet) { envelope.expandToInclude(vx.getCoordinate()); } envelope.expandBy(searchRadiusDegrees / xscale, searchRadiusDegrees); @@ -127,4 +127,21 @@ Geometry getGeometry() { return new MultiPoint(points.toArray(new Point[0]), geometryFactory); } + + /** + * Checks whether the subgraph has only transit-stops for ferries + * + * @return true if only ferries stop at the subgraph and false if other or no modes are + * stopping at the subgraph + */ + boolean hasOnlyFerryStops() { + for (TransitStopVertex v : stopsVertexSet) { + Set modes = v.getModes(); + // test if stop has other transit modes than FERRY + if (!modes.contains(TransitMode.FERRY)) { + return false; + } + } + return true; + } } diff --git a/src/main/java/org/opentripplanner/graph_builder/module/ned/ElevationModule.java b/src/main/java/org/opentripplanner/graph_builder/module/ned/ElevationModule.java index 592d34fb543..6bcab049bd4 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/ned/ElevationModule.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/ned/ElevationModule.java @@ -20,13 +20,14 @@ import java.util.concurrent.atomic.AtomicInteger; import org.geotools.api.coverage.Coverage; import org.geotools.api.coverage.PointOutsideCoverageException; +import org.geotools.api.referencing.crs.CoordinateReferenceSystem; import org.geotools.api.referencing.operation.TransformException; import org.geotools.geometry.Position2D; +import org.geotools.referencing.CRS; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.impl.PackedCoordinateSequence; import org.opentripplanner.framework.geometry.EncodedPolyline; -import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; import org.opentripplanner.framework.lang.IntUtils; import org.opentripplanner.framework.logging.ProgressTracker; @@ -61,6 +62,25 @@ public class ElevationModule implements GraphBuilderModule { private static final Logger LOG = LoggerFactory.getLogger(ElevationModule.class); + /** + * The WGS84 CRS with longitude-first axis order. The first time a CRS lookup is + * performed is surprisingly expensive (around 500ms), apparently due to initializing + * an HSQLDB JDBC connection. For this reason, the constant is defined in this + * narrower scope rather than a shared utility class, where it was seen to incur the + * initialization cost in a broader range of tests than is necessary. + */ + private static final CoordinateReferenceSystem WGS84_XY; + + static { + try { + WGS84_XY = CRS.getAuthorityFactory(true).createCoordinateReferenceSystem("EPSG:4326"); + } catch (Exception ex) { + LOG.error("Unable to create longitude-first WGS84 CRS", ex); + throw new RuntimeException( + "Could not create longitude-first WGS84 coordinate reference system." + ); + } + } /** The elevation data to be used in calculating elevations. */ private final ElevationGridCoverageFactory gridCoverageFactory; @@ -564,7 +584,7 @@ private double getElevation(Coverage coverage, double x, double y) // GeoTIFFs in various projections. Note that GeoTools defaults to strict EPSG axis ordering of (lat, long) // for DefaultGeographicCRS.WGS84, but OTP is using (long, lat) throughout and assumes unprojected DEM // rasters to also use (long, lat). - coverage.evaluate(new Position2D(GeometryUtils.WGS84_XY, x, y), values); + coverage.evaluate(new Position2D(WGS84_XY, x, y), values); } catch (PointOutsideCoverageException e) { nPointsOutsideDEM.incrementAndGet(); throw e; diff --git a/src/main/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializer.java b/src/main/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializer.java index 4f090a440b6..0ceae6cb456 100644 --- a/src/main/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializer.java +++ b/src/main/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializer.java @@ -51,8 +51,14 @@ public static LegReference decode(String legReference) { return null; } - var buf = Base64.getUrlDecoder().decode(legReference); - var input = new ByteArrayInputStream(buf); + byte[] serializedLegReference; + try { + serializedLegReference = Base64.getUrlDecoder().decode(legReference); + } catch (IllegalArgumentException e) { + LOG.info("Unable to decode leg reference (invalid base64 encoding): '{}'", legReference, e); + return null; + } + var input = new ByteArrayInputStream(serializedLegReference); try (var in = new ObjectInputStream(input)) { // The order must be the same in the encode and decode function @@ -60,7 +66,11 @@ public static LegReference decode(String legReference) { var type = readEnum(in, LegReferenceType.class); return type.getDeserializer().read(in); } catch (IOException e) { - LOG.error("Unable to decode leg reference: '" + legReference + "'", e); + LOG.warn( + "Unable to decode leg reference (incompatible serialization format): '{}'", + legReference, + e + ); return null; } } diff --git a/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java b/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java index 765a9b5c47f..740224b4489 100644 --- a/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java +++ b/src/main/java/org/opentripplanner/netex/mapping/TripPatternMapper.java @@ -67,7 +67,7 @@ class TripPatternMapper { private final ReadOnlyHierarchicalMap routeById; - private final Multimap serviceJourniesByPatternId = ArrayListMultimap.create(); + private final Multimap serviceJourneysByPatternId = ArrayListMultimap.create(); private final ReadOnlyHierarchicalMapById operatingDayById; @@ -152,12 +152,12 @@ class TripPatternMapper { this.serviceJourneyById = serviceJourneyById; // Index service journey by pattern id for (ServiceJourney sj : serviceJourneyById.localValues()) { - this.serviceJourniesByPatternId.put(sj.getJourneyPatternRef().getValue().getRef(), sj); + this.serviceJourneysByPatternId.put(sj.getJourneyPatternRef().getValue().getRef(), sj); } } Optional mapTripPattern(JourneyPattern_VersionStructure journeyPattern) { - Collection serviceJourneys = serviceJourniesByPatternId.get( + Collection serviceJourneys = serviceJourneysByPatternId.get( journeyPattern.getId() ); @@ -392,9 +392,12 @@ private Trip mapTrip( JourneyPattern_VersionStructure journeyPattern, ServiceJourney serviceJourney ) { - return tripMapper.mapServiceJourney( - serviceJourney, - () -> findTripHeadsign(journeyPattern, serviceJourney) + return deduplicator.deduplicateObject( + Trip.class, + tripMapper.mapServiceJourney( + serviceJourney, + () -> findTripHeadsign(journeyPattern, serviceJourney) + ) ); } diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java index 1ce6c698f22..6e610f99fa3 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java @@ -105,20 +105,6 @@ public boolean isOneWayReverseBicycle() { return "-1".equals(oneWayBicycle) || isTagFalse("bicycle:forward"); } - /** - * Returns true if bikes must use sidepath in forward direction - */ - public boolean isForwardDirectionSidepath() { - return "use_sidepath".equals(getTag("bicycle:forward")); - } - - /** - * Returns true if bikes must use sidepath in reverse direction - */ - public boolean isReverseDirectionSidepath() { - return "use_sidepath".equals(getTag("bicycle:backward")); - } - /** * Some cycleways allow contraflow biking. */ @@ -184,18 +170,6 @@ public StreetTraversalPermissionPair splitPermissions(StreetTraversalPermission } } - //This needs to be after adding permissions for oneway:bicycle=no - //removes bicycle permission when bicycles need to use sidepath - //TAG: bicycle:forward=use_sidepath - if (isForwardDirectionSidepath()) { - permissionsFront = permissionsFront.remove(StreetTraversalPermission.BICYCLE); - } - - //TAG bicycle:backward=use_sidepath - if (isReverseDirectionSidepath()) { - permissionsBack = permissionsBack.remove(StreetTraversalPermission.BICYCLE); - } - if (isOpposableCycleway()) { permissionsBack = permissionsBack.add(StreetTraversalPermission.BICYCLE); } diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java index 37757823362..e5feda76bc5 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java @@ -391,14 +391,10 @@ public boolean isVehicleExplicitlyAllowed() { /** * Returns true if bikes are explicitly denied access. * - * bicycle is denied if bicycle:no, bicycle:dismount, bicycle:license or bicycle:use_sidepath + * bicycle is denied if bicycle:no, bicycle:dismount or bicycle:license. */ public boolean isBicycleExplicitlyDenied() { - return ( - isTagDeniedAccess("bicycle") || - "dismount".equals(getTag("bicycle")) || - "use_sidepath".equals(getTag("bicycle")) - ); + return (isTagDeniedAccess("bicycle") || "dismount".equals(getTag("bicycle"))); } /** diff --git a/src/main/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapper.java b/src/main/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapper.java index 1463313203d..9989c102030 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapper.java +++ b/src/main/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapper.java @@ -624,6 +624,9 @@ public void populateProperties(WayPropertySet props) { props.setMixinProperties("foot=discouraged", ofWalkSafety(3)); props.setMixinProperties("bicycle=discouraged", ofBicycleSafety(3)); + props.setMixinProperties("foot=use_sidepath", ofWalkSafety(5)); + props.setMixinProperties("bicycle=use_sidepath", ofBicycleSafety(5)); + populateNotesAndNames(props); // slope overrides diff --git a/src/main/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapper.java b/src/main/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapper.java index 9bafe133f2a..5ea949e8d5c 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapper.java +++ b/src/main/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapper.java @@ -1,5 +1,6 @@ package org.opentripplanner.openstreetmap.tagmapping; +import static org.opentripplanner.openstreetmap.wayproperty.MixinPropertiesBuilder.ofWalkSafety; import static org.opentripplanner.openstreetmap.wayproperty.WayPropertiesBuilder.withModes; import static org.opentripplanner.street.model.StreetTraversalPermission.ALL; import static org.opentripplanner.street.model.StreetTraversalPermission.CAR; @@ -163,6 +164,10 @@ else if (speedLimit <= 16.65f) { props.setProperties("highway=service;tunnel=yes;access=destination", withModes(NONE)); props.setProperties("highway=service;access=destination", withModes(ALL).bicycleSafety(1.1)); + // Typically if this tag is used on a way, there is also a better option for walking. + // We don't need to set bicycle safety as cycling is not currently allowed on these ways. + props.setMixinProperties("bicycle=use_sidepath", ofWalkSafety(5)); + // Automobile speeds in Finland. // General speed limit is 80kph unless signs says otherwise. props.defaultCarSpeed = 22.22f; diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java index 107d41208f4..3dbff516347 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java @@ -140,7 +140,13 @@ private int bestNumOfTransfers(int stop) { } private int bestGeneralizedCost(int stop) { - return costCalculator.calculateMinCost(bestTravelDuration(stop), bestNumOfTransfers(stop)); + return ( + costCalculator.calculateRemainingMinCost( + bestTravelDuration(stop), + bestNumOfTransfers(stop), + stop + ) + ); } /** diff --git a/src/main/java/org/opentripplanner/raptor/spi/RaptorCostCalculator.java b/src/main/java/org/opentripplanner/raptor/spi/RaptorCostCalculator.java index d575a0e4d8c..7a9928e51aa 100644 --- a/src/main/java/org/opentripplanner/raptor/spi/RaptorCostCalculator.java +++ b/src/main/java/org/opentripplanner/raptor/spi/RaptorCostCalculator.java @@ -52,12 +52,12 @@ int boardingCost( /** * Used for estimating the remaining value for a criteria at a given stop arrival. The calculated - * value should be a an optimistic estimate for the heuristics to work properly. So, to calculate - * the generalized cost for given the {@code minTravelTime} and {@code minNumTransfers} retuning + * value should be an optimistic estimate for the heuristics to work properly. So, to calculate + * the generalized cost for given the {@code minTravelTime} and {@code minNumTransfers} returning * the greatest value, which is guaranteed to be less than the - * real value would be correct and a good choose. + * real value would be correct and a good choice. */ - int calculateMinCost(int minTravelTime, int minNumTransfers); + int calculateRemainingMinCost(int minTravelTime, int minNumTransfers, int fromStop); /** * This method allows the cost calculator to add cost in addition to the generalized-cost of the diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayer.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayer.java index 60cfb72ef7d..288f429a861 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayer.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayer.java @@ -22,6 +22,8 @@ public class TransitLayer { /** * Transit data required for routing, indexed by each local date(Graph TimeZone) it runs through. * A Trip "runs through" a date if any of its arrivals or departures is happening on that date. + * The same trip pattern can therefore have multiple running dates and trip pattern is not + * required to "run" on its service date. */ private final HashMap> tripPatternsRunningOnDate; @@ -94,7 +96,12 @@ public StopLocation getStopByIndex(int stop) { return stop == -1 ? null : this.stopModel.stopByIndex(stop); } - public Collection getTripPatternsForDate(LocalDate date) { + /** + * Returns trip patterns for the given running date. Running date is not necessarily the same + * as the service date. A Trip "runs through" a date if any of its arrivals or departures is + * happening on that date. Trip pattern can have multiple running dates. + */ + public Collection getTripPatternsForRunningDate(LocalDate date) { return tripPatternsRunningOnDate.getOrDefault(date, List.of()); } @@ -112,16 +119,29 @@ public int getStopCount() { return stopModel.stopIndexSize(); } + /** + * Returns a copy of the list of trip patterns for the given running date. Running date is not + * necessarily the same as the service date. A Trip "runs through" a date if any of its arrivals + * or departures is happening on that date. Trip pattern can have multiple running dates. + */ public List getTripPatternsRunningOnDateCopy(LocalDate runningPeriodDate) { List tripPatternForDate = tripPatternsRunningOnDate.get(runningPeriodDate); return tripPatternForDate != null ? new ArrayList<>(tripPatternForDate) : new ArrayList<>(); } - public List getTripPatternsStartingOnDateCopy(LocalDate date) { - List tripPatternsRunningOnDate = getTripPatternsRunningOnDateCopy(date); - return tripPatternsRunningOnDate + /** + * Returns a copy of the list of trip patterns for the given service date. Service date is not + * necessarily the same as any of the trip pattern's running dates. + */ + public List getTripPatternsOnServiceDateCopy(LocalDate date) { + List tripPatternsRunningOnDates = getTripPatternsRunningOnDateCopy(date); + // Trip pattern can run only after midnight. Therefore, we need to get the trip pattern's for + // the next running date as well and filter out duplicates. + tripPatternsRunningOnDates.addAll(getTripPatternsRunningOnDateCopy(date.plusDays(1))); + return tripPatternsRunningOnDates .stream() - .filter(t -> t.getLocalDate().equals(date)) + .filter(t -> t.getServiceDate().equals(date)) + .distinct() .collect(Collectors.toList()); } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TripPatternForDate.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TripPatternForDate.java index b86314aa42c..1411424dfc6 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TripPatternForDate.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TripPatternForDate.java @@ -43,16 +43,16 @@ public class TripPatternForDate implements Comparable { */ private final FrequencyEntry[] frequencies; - /** The date for which the filtering was performed. */ - private final LocalDate localDate; + /** The service date of the trip pattern. */ + private final LocalDate serviceDate; /** - * The date on which the first trip departs. + * The running date on which the first trip departs. Not necessarily the same as the service date. */ private final LocalDate startOfRunningPeriod; /** - * The date on which the last trip arrives. + * The running date on which the last trip arrives. */ private final LocalDate endOfRunningPeriod; @@ -60,19 +60,19 @@ public TripPatternForDate( RoutingTripPattern tripPattern, List tripTimes, List frequencies, - LocalDate localDate + LocalDate serviceDate ) { this.tripPattern = tripPattern; this.tripTimes = tripTimes.toArray(new TripTimes[0]); this.frequencies = frequencies.toArray(new FrequencyEntry[0]); - this.localDate = localDate; + this.serviceDate = serviceDate; // TODO: We expect a pattern only containing trips or frequencies, fix ability to merge if (hasFrequencies()) { this.startOfRunningPeriod = ServiceDateUtils .asDateTime( - localDate, + serviceDate, frequencies .stream() .mapToInt(frequencyEntry -> frequencyEntry.startTime) @@ -84,7 +84,7 @@ public TripPatternForDate( this.endOfRunningPeriod = ServiceDateUtils .asDateTime( - localDate, + serviceDate, frequencies .stream() .mapToInt(frequencyEntry -> frequencyEntry.endTime) @@ -96,11 +96,11 @@ public TripPatternForDate( // These depend on the tripTimes array being sorted var first = tripTimes.get(0); this.startOfRunningPeriod = - ServiceDateUtils.asDateTime(localDate, first.getDepartureTime(0)).toLocalDate(); + ServiceDateUtils.asDateTime(serviceDate, first.getDepartureTime(0)).toLocalDate(); var last = tripTimes.get(tripTimes.size() - 1); this.endOfRunningPeriod = ServiceDateUtils - .asDateTime(localDate, last.getArrivalTime(last.getNumStops() - 1)) + .asDateTime(serviceDate, last.getArrivalTime(last.getNumStops() - 1)) .toLocalDate(); assertValidRunningPeriod(startOfRunningPeriod, endOfRunningPeriod, first, last); } @@ -126,18 +126,31 @@ public TripTimes getTripTimes(int i) { return tripTimes[i]; } - public LocalDate getLocalDate() { - return localDate; + /** + * The service date for which the trip pattern belongs to. Not necessarily the same as the start + * of the running period in cases where the trip pattern only runs after midnight. + */ + public LocalDate getServiceDate() { + return serviceDate; } public int numberOfTripSchedules() { return tripTimes.length; } + /** + * The start of the running period. This is determined by the first departure time for this + * pattern. Not necessarily the same as the service date if the pattern runs after midnight. + */ public LocalDate getStartOfRunningPeriod() { return startOfRunningPeriod; } + /** + * Returns the running dates. A Trip "runs through" a date if any of its arrivals or departures is + * happening on that date. The same trip pattern can therefore have multiple running dates and + * trip pattern is not required to "run" on its service date. + */ public List getRunningPeriodDates() { // Add one day to ensure last day is included return startOfRunningPeriod @@ -151,14 +164,14 @@ public boolean hasFrequencies() { @Override public int compareTo(TripPatternForDate other) { - return localDate.compareTo(other.localDate); + return serviceDate.compareTo(other.serviceDate); } @Override public int hashCode() { return Objects.hash( tripPattern, - localDate, + serviceDate, Arrays.hashCode(tripTimes), Arrays.hashCode(frequencies) ); @@ -176,7 +189,7 @@ public boolean equals(Object o) { return ( tripPattern.equals(that.tripPattern) && - localDate.equals(that.localDate) && + serviceDate.equals(that.serviceDate) && Arrays.equals(tripTimes, that.tripTimes) && Arrays.equals(frequencies, that.frequencies) ); @@ -184,7 +197,9 @@ public boolean equals(Object o) { @Override public String toString() { - return "TripPatternForDate{" + "tripPattern=" + tripPattern + ", localDate=" + localDate + '}'; + return ( + "TripPatternForDate{" + "tripPattern=" + tripPattern + ", serviceDate=" + serviceDate + '}' + ); } @Nullable @@ -214,7 +229,7 @@ public TripPatternForDate newWithFilteredTripTimes(Predicate filter) return this; } - return new TripPatternForDate(tripPattern, filteredTripTimes, filteredFrequencies, localDate); + return new TripPatternForDate(tripPattern, filteredTripTimes, filteredFrequencies, serviceDate); } private static void assertValidRunningPeriod( diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java index 26a1286d10d..ee2f1282625 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java @@ -121,14 +121,21 @@ public int waitCost(int waitTimeInSeconds) { } @Override - public int calculateMinCost(int minTravelTime, int minNumTransfers) { - return ( - boardCostOnly + - boardAndTransferCost * - minNumTransfers + - transitFactors.minFactor() * - minTravelTime - ); + public int calculateRemainingMinCost(int minTravelTime, int minNumTransfers, int fromStop) { + if (minNumTransfers > -1) { + return ( + boardCostOnly + + boardAndTransferCost * + minNumTransfers + + transitFactors.minFactor() * + minTravelTime + ); + } else { + // Remove cost that was added during alighting similar as we do in the costEgress() method + int fixedCost = transitFactors.minFactor() * minTravelTime; + + return stopTransferCost == null ? fixedCost : fixedCost - stopTransferCost[fromStop]; + } } @Override 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 ecd0704bbc2..734355f0ac5 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 @@ -77,8 +77,8 @@ public int waitCost(int waitTimeInSeconds) { } @Override - public int calculateMinCost(int minTravelTime, int minNumTransfers) { - return delegate.calculateMinCost(minTravelTime, minNumTransfers); + public int calculateRemainingMinCost(int minTravelTime, int minNumTransfers, int fromStop) { + return delegate.calculateRemainingMinCost(minTravelTime, minNumTransfers, fromStop); } @Override 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 78014bd245d..d0697f78692 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 @@ -66,8 +66,8 @@ public int waitCost(int waitTimeInSeconds) { } @Override - public int calculateMinCost(int minTravelTime, int minNumTransfers) { - return delegate.calculateMinCost(minTravelTime, minNumTransfers); + public int calculateRemainingMinCost(int minTravelTime, int minNumTransfers, int fromStop) { + return delegate.calculateRemainingMinCost(minTravelTime, minNumTransfers, fromStop); } @Override diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/TripFrequencyAlightSearch.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/TripFrequencyAlightSearch.java index 451f51b2aa5..2f020e22cf5 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/TripFrequencyAlightSearch.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/TripFrequencyAlightSearch.java @@ -55,7 +55,7 @@ public RaptorBoardOrAlightEvent search( arrivalTime + headway, headway, offset, - pattern.getLocalDate() + pattern.getServiceDate() ); } } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/TripFrequencyBoardSearch.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/TripFrequencyBoardSearch.java index 897ce370a93..ea58e870547 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/TripFrequencyBoardSearch.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/TripFrequencyBoardSearch.java @@ -54,7 +54,7 @@ public RaptorBoardOrAlightEvent search( departureTime - headway, headway, offset, - pattern.getLocalDate() + pattern.getServiceDate() ); } } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerUpdater.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerUpdater.java index bed27497587..fad5de83de0 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerUpdater.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerUpdater.java @@ -96,7 +96,7 @@ public void update( if (!tripPatternsStartingOnDateMapCache.containsKey(date)) { Map map = realtimeTransitLayer - .getTripPatternsStartingOnDateCopy(date) + .getTripPatternsOnServiceDateCopy(date) .stream() .collect(Collectors.toMap(t -> t.getTripPattern().getPattern(), t -> t)); tripPatternsStartingOnDateMapCache.put(date, map); @@ -146,7 +146,7 @@ public void update( } else { LOG.debug( "NEW TripPatternForDate: {} - {}", - newTripPatternForDate.getLocalDate(), + newTripPatternForDate.getServiceDate(), newTripPatternForDate.getTripPattern().debugInfo() ); } @@ -179,7 +179,7 @@ public void update( } for (TripPatternForDate tripPatternForDate : previouslyUsedPatterns) { - if (tripPatternForDate.getLocalDate().equals(date)) { + if (tripPatternForDate.getServiceDate().equals(date)) { TripPattern pattern = tripPatternForDate.getTripPattern().getPattern(); if (!pattern.isCreatedByRealtimeUpdater()) { continue; diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java index 863a4ca9ae8..f987e4f7a21 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java @@ -120,7 +120,7 @@ static List merge( // Calculate offsets per date int[] offsets = new int[patternsSorted.length]; for (int i = 0; i < patternsSorted.length; i++) { - LocalDate serviceDate = patternsSorted[i].getLocalDate(); + LocalDate serviceDate = patternsSorted[i].getServiceDate(); if (offsetCache.containsKey(serviceDate)) { offsets[i] = offsetCache.get(serviceDate); } else { @@ -185,7 +185,9 @@ private static List filterActiveTripPatterns( filter.tripTimesPredicate(tripTimes, filter.hasSubModeFilters()); Predicate tripTimesWithoutSubmodesPredicate = tripTimes -> filter.tripTimesPredicate(tripTimes, false); - Collection tripPatternsForDate = transitLayer.getTripPatternsForDate(date); + Collection tripPatternsForDate = transitLayer.getTripPatternsForRunningDate( + date + ); List result = new ArrayList<>(tripPatternsForDate.size()); for (TripPatternForDate p : tripPatternsForDate) { if (firstDay || p.getStartOfRunningPeriod().equals(date)) { diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleWithOffset.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleWithOffset.java index 642dbd2d6e5..aca998b24ca 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleWithOffset.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleWithOffset.java @@ -124,7 +124,7 @@ private void findTripTimes() { if (index < numSchedules) { this.tripTimes = tripPatternForDate.getTripTimes(index); - this.serviceDate = tripPatternForDate.getLocalDate(); + this.serviceDate = tripPatternForDate.getServiceDate(); this.secondsOffset = pattern.tripPatternForDateOffsets(i); return; } diff --git a/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParking.java b/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParking.java index e5b0fd083dc..6ebd34e1287 100644 --- a/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParking.java +++ b/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParking.java @@ -120,9 +120,10 @@ public class VehicleParking implements Serializable { VehicleParkingSpaces availability, VehicleParkingGroup vehicleParkingGroup ) { - this.id = id; + this.id = + Objects.requireNonNull(id, "%s must have an ID".formatted(this.getClass().getSimpleName())); this.name = name; - this.coordinate = coordinate; + this.coordinate = Objects.requireNonNull(coordinate); this.detailsUrl = detailsUrl; this.imageUrl = imageUrl; this.tags = tags; diff --git a/src/main/java/org/opentripplanner/standalone/OTPMain.java b/src/main/java/org/opentripplanner/standalone/OTPMain.java index 51172d5d90a..d865f8fa28c 100644 --- a/src/main/java/org/opentripplanner/standalone/OTPMain.java +++ b/src/main/java/org/opentripplanner/standalone/OTPMain.java @@ -13,6 +13,7 @@ import org.opentripplanner.raptor.configure.RaptorConfig; import org.opentripplanner.routing.graph.SerializedGraphObject; import org.opentripplanner.standalone.config.CommandLineParameters; +import org.opentripplanner.standalone.config.ConfigModel; import org.opentripplanner.standalone.configure.ConstructApplication; import org.opentripplanner.standalone.configure.LoadApplication; import org.opentripplanner.standalone.server.GrizzlyServer; @@ -113,6 +114,8 @@ private static void startOTPServer(CommandLineParameters cli) { var loadApp = new LoadApplication(cli); var config = loadApp.config(); + detectUnusedConfigParams(cli, config); + // Validate data sources, command line arguments and config before loading and // processing input data to fail early loadApp.validateConfigAndDataSources(); @@ -173,6 +176,15 @@ private static void startOTPServer(CommandLineParameters cli) { } } + /** + * Optionally, check if the config is valid and if not abort the startup process. + */ + private static void detectUnusedConfigParams(CommandLineParameters cli, ConfigModel config) { + if (cli.abortOnUnknownConfig) { + config.abortOnUnknownParameters(); + } + } + private static void startOtpWebServer(CommandLineParameters params, ConstructApplication app) { // Index graph for travel search app.transitModel().index(); diff --git a/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java b/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java index a542c330afe..a3c6f313799 100644 --- a/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java @@ -726,4 +726,11 @@ public int getSubwayAccessTimeSeconds() { public NodeAdapter asNodeAdapter() { return root; } + + /** + * Checks if any unknown or invalid parameters were encountered while loading the configuration. + */ + public boolean hasUnknownParameters() { + return root.hasUnknownParameters(); + } } diff --git a/src/main/java/org/opentripplanner/standalone/config/CommandLineParameters.java b/src/main/java/org/opentripplanner/standalone/config/CommandLineParameters.java index fb26db4877d..3513ec252ea 100644 --- a/src/main/java/org/opentripplanner/standalone/config/CommandLineParameters.java +++ b/src/main/java/org/opentripplanner/standalone/config/CommandLineParameters.java @@ -133,6 +133,12 @@ public class CommandLineParameters { ) public boolean visualize; + @Parameter( + names = { "--abortOnUnknownConfig" }, + description = "Abort the startup if configuration files are found to contain unknown parameters." + ) + public boolean abortOnUnknownConfig = false; + /** * The remaining single parameter after the switches is the directory with the configuration * files. This directory may contain other files like the graph, input data and report files. diff --git a/src/main/java/org/opentripplanner/standalone/config/ConfigModel.java b/src/main/java/org/opentripplanner/standalone/config/ConfigModel.java index 48966aefc7b..c82dd33ddbf 100644 --- a/src/main/java/org/opentripplanner/standalone/config/ConfigModel.java +++ b/src/main/java/org/opentripplanner/standalone/config/ConfigModel.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.node.MissingNode; import org.opentripplanner.framework.application.OTPFeature; +import org.opentripplanner.framework.application.OtpAppException; import org.opentripplanner.framework.application.OtpFileNames; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -105,4 +106,24 @@ public static void initializeOtpFeatures(OtpConfig otpConfig) { OTPFeature.enableFeatures(otpConfig.otpFeatures); OTPFeature.logFeatureSetup(); } + + /** + * Checks if any unknown or invalid parameters were encountered while loading the configuration. + * + * If so it throws an exception. + */ + public void abortOnUnknownParameters() { + if ( + ( + otpConfig.hasUnknownParameters() || + buildConfig.hasUnknownParameters() || + routerConfig.hasUnknownParameters() + ) + ) { + throw new OtpAppException( + "Configuration contains unknown parameters (see above for details). " + + "Please fix your configuration or remove --abortOnUnknownConfig from your OTP CLI parameters." + ); + } + } } diff --git a/src/main/java/org/opentripplanner/standalone/config/OtpConfig.java b/src/main/java/org/opentripplanner/standalone/config/OtpConfig.java index 40a659df1ab..2918cbc3884 100644 --- a/src/main/java/org/opentripplanner/standalone/config/OtpConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/OtpConfig.java @@ -71,4 +71,11 @@ public OtpConfig(NodeAdapter nodeAdapter, boolean logUnusedParams) { root.logAllWarnings(LOG::warn); } } + + /** + * Checks if any unknown or invalid parameters were encountered while loading the configuration. + */ + public boolean hasUnknownParameters() { + return root.hasUnknownParameters(); + } } diff --git a/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java b/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java index bf97155b747..4ac0688a759 100644 --- a/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java @@ -150,4 +150,11 @@ public String toString() { // Print ONLY the values set, not default values return root.toPrettyString(); } + + /** + * Checks if any unknown or invalid parameters were encountered while loading the configuration. + */ + public boolean hasUnknownParameters() { + return root.hasUnknownParameters(); + } } 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 69d9937582b..51303ea4fc5 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 @@ -172,6 +172,13 @@ public void logAllWarnings(Consumer logger) { allWarnings().forEach(logger); } + /** + * Checks if any unknown or invalid properties were encountered while loading the configuration. + */ + public boolean hasUnknownParameters() { + return !unusedParams().isEmpty(); + } + /** * Be careful when using this method - this bypasses the NodeAdaptor, and we loose * track of unused parameters and cannot generate documentation for the children. 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 6caf9082cff..70b8e261ee4 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 @@ -10,7 +10,8 @@ public enum OtpVersion { V2_2("2.2"), V2_3("2.3"), V2_4("2.4"), - V2_5("2.5"); + V2_5("2.5"), + V2_6("2.6"); private final String text; diff --git a/src/main/java/org/opentripplanner/standalone/config/framework/json/ParameterBuilder.java b/src/main/java/org/opentripplanner/standalone/config/framework/json/ParameterBuilder.java index 088a7794585..3ecc4d484cb 100644 --- a/src/main/java/org/opentripplanner/standalone/config/framework/json/ParameterBuilder.java +++ b/src/main/java/org/opentripplanner/standalone/config/framework/json/ParameterBuilder.java @@ -120,10 +120,6 @@ public Boolean asBoolean(boolean defaultValue) { return ofOptional(BOOLEAN, defaultValue, JsonNode::asBoolean); } - public Map asBooleanMap() { - return ofOptionalMap(BOOLEAN, JsonNode::asBoolean); - } - /** @throws OtpAppException if parameter is missing. */ public double asDouble() { return ofRequired(DOUBLE).asDouble(); 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 e808ad6905c..c687899009f 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 @@ -2,13 +2,16 @@ import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_2; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_3; +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_6; import java.time.Duration; import java.time.ZoneId; import java.util.ArrayList; import java.util.Set; +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.standalone.config.framework.json.NodeAdapter; import org.opentripplanner.updater.vehicle_parking.VehicleParkingSourceType; @@ -25,7 +28,7 @@ public static VehicleParkingUpdaterParameters create(String updaterRef, NodeAdap var feedId = c .of("feedId") .since(V2_2) - .summary("The name of the data source.") + .summary("The id of the data source, which will be the prefix of the parking lot's id.") .description("This will end up in the API responses as the feed id of of the parking lot.") .asString(); return switch (sourceType) { @@ -50,7 +53,7 @@ public static VehicleParkingUpdaterParameters create(String updaterRef, NodeAdap ); case PARK_API, BICYCLE_PARK_API -> new ParkAPIUpdaterParameters( updaterRef, - c.of("url").since(V2_2).summary("URL of the resource.").asString(null), + c.of("url").since(V2_2).summary("URL of the resource.").asString(), feedId, c .of("frequency") @@ -66,7 +69,7 @@ public static VehicleParkingUpdaterParameters create(String updaterRef, NodeAdap ); case BIKELY -> new BikelyUpdaterParameters( updaterRef, - c.of("url").since(V2_3).summary("URL of the locations endpoint.").asUri(null), + c.of("url").since(V2_3).summary("URL of the locations endpoint.").asUri(), feedId, c .of("frequency") @@ -75,6 +78,28 @@ 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(), + feedId, + c + .of("frequency") + .since(V2_6) + .summary("How often to update the source.") + .asDuration(Duration.ofMinutes(1)), + HttpHeadersConfig.headers(c, V2_6) + ); }; } 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 e44994d1105..35278d8fbd8 100644 --- a/src/main/java/org/opentripplanner/transit/model/basic/Money.java +++ b/src/main/java/org/opentripplanner/transit/model/basic/Money.java @@ -206,4 +206,9 @@ public boolean equals(Object obj) { return false; } } + + @Override + public int hashCode() { + return Objects.hash(currency, amount); + } } 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 e0214fd9d57..a956fda0d87 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingDataSourceFactory.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingDataSourceFactory.java @@ -1,9 +1,13 @@ package org.opentripplanner.updater.vehicle_parking; +import org.opentripplanner.ext.vehicleparking.bikeep.BikeepUpdater; +import org.opentripplanner.ext.vehicleparking.bikeep.BikeepUpdaterParameters; import org.opentripplanner.ext.vehicleparking.bikely.BikelyUpdater; 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; @@ -36,6 +40,8 @@ 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); }; } } 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 583bc9afc99..f6a28177d8e 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingSourceType.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingSourceType.java @@ -5,4 +5,6 @@ public enum VehicleParkingSourceType { BICYCLE_PARK_API, HSL_PARK, BIKELY, + NOI_OPEN_DATA_HUB, + BIKEEP, } diff --git a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java index 3d2e9b21e29..185de6bddb7 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java @@ -8,6 +8,7 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; +import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.linking.DisposableEdgeCollection; import org.opentripplanner.routing.linking.LinkingDirection; @@ -68,7 +69,7 @@ public void setGraphUpdaterManager(WriteToGraphCallback saveResultOnGraph) { } @Override - protected void runPolling() throws Exception { + protected void runPolling() { LOG.debug("Updating vehicle parkings from {}", source); if (!source.update()) { LOG.debug("No updates"); @@ -239,4 +240,9 @@ private void removeVehicleParkingEdgesFromGraph( graph.remove(entranceVertex); } } + + @Override + public String toString() { + return ToStringBuilder.of(this.getClass()).addObj("source", source).toString(); + } } diff --git a/src/test/java/org/opentripplanner/_support/geometry/Coordinates.java b/src/test/java/org/opentripplanner/_support/geometry/Coordinates.java index 3e5bddd9f94..5a4526012c9 100644 --- a/src/test/java/org/opentripplanner/_support/geometry/Coordinates.java +++ b/src/test/java/org/opentripplanner/_support/geometry/Coordinates.java @@ -4,9 +4,18 @@ public class Coordinates { - public static final Coordinate BERLIN = new Coordinate(52.5212, 13.4105); - public static final Coordinate BERLIN_BRANDENBURG_GATE = new Coordinate(52.51627, 13.37770); - public static final Coordinate HAMBURG = new Coordinate(53.5566, 10.0003); - public static final Coordinate KONGSBERG_PLATFORM_1 = new Coordinate(59.67216, 9.65107); - public static final Coordinate BOSTON = new Coordinate(42.36541, -71.06129); + public static final Coordinate BERLIN = of(52.5212, 13.4105); + public static final Coordinate BERLIN_BRANDENBURG_GATE = of(52.51627, 13.37770); + public static final Coordinate HAMBURG = of(53.5566, 10.0003); + public static final Coordinate KONGSBERG_PLATFORM_1 = of(59.67216, 9.65107); + public static final Coordinate BOSTON = of(42.36541, -71.06129); + + /** + * Because it is a frequent mistake to swap x/y and longitude/latitude when + * constructing JTS Coordinates, this static factory method makes is clear + * which is which. + */ + public static Coordinate of(double latitude, double longitude) { + return new Coordinate(longitude, latitude); + } } diff --git a/src/test/java/org/opentripplanner/_support/geometry/Polygons.java b/src/test/java/org/opentripplanner/_support/geometry/Polygons.java index efedd74499c..a386d8a27e1 100644 --- a/src/test/java/org/opentripplanner/_support/geometry/Polygons.java +++ b/src/test/java/org/opentripplanner/_support/geometry/Polygons.java @@ -3,29 +3,65 @@ import java.util.Arrays; import org.geojson.LngLatAlt; import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.Polygon; import org.opentripplanner.framework.geometry.GeometryUtils; public class Polygons { - public static final Polygon BERLIN = GeometryUtils - .getGeometryFactory() - .createPolygon( - new Coordinate[] { - new Coordinate(52.616841, 13.224692810), - new Coordinate(52.6168419, 13.224692810), - new Coordinate(52.3915238, 13.646107734), - new Coordinate(52.616841, 13.646107734), - new Coordinate(52.616841, 13.224692810), - } - ); + private static final GeometryFactory FAC = GeometryUtils.getGeometryFactory(); + public static final Polygon BERLIN = FAC.createPolygon( + new Coordinate[] { + Coordinates.of(52.616841, 13.224692810), + Coordinates.of(52.616841, 13.646107734), + Coordinates.of(52.3915238, 13.646107734), + Coordinates.of(52.396421, 13.2268067), + Coordinates.of(52.616841, 13.224692810), + } + ); + + public static Polygon OSLO = FAC.createPolygon( + new Coordinate[] { + Coordinates.of(59.961055202323195, 10.62535658370308), + Coordinates.of(59.889009435700416, 10.62535658370308), + Coordinates.of(59.889009435700416, 10.849791142928694), + Coordinates.of(59.961055202323195, 10.849791142928694), + Coordinates.of(59.961055202323195, 10.62535658370308), + } + ); + public static Polygon OSLO_FROGNER_PARK = FAC.createPolygon( + new Coordinate[] { + Coordinates.of(59.92939032560119, 10.69770054003061), + Coordinates.of(59.929138466684975, 10.695210909925208), + Coordinates.of(59.92745319808358, 10.692696865071184), + Coordinates.of(59.92709930323093, 10.693774304996225), + Coordinates.of(59.92745914988427, 10.69495957972947), + Coordinates.of(59.92736919590291, 10.697664535925895), + Coordinates.of(59.924837887427856, 10.697927604125255), + Coordinates.of(59.924447953413335, 10.697448767354985), + Coordinates.of(59.92378800804022, 10.697819761729818), + Coordinates.of(59.92329018587293, 10.699196446969069), + Coordinates.of(59.92347619027632, 10.700285749621997), + Coordinates.of(59.92272030268688, 10.704714696822037), + Coordinates.of(59.92597766029715, 10.71001707489603), + Coordinates.of(59.92676341291536, 10.707838597058043), + Coordinates.of(59.92790300889098, 10.708389137506913), + Coordinates.of(59.928376832499424, 10.707060536853078), + Coordinates.of(59.92831087551576, 10.705803789539402), + Coordinates.of(59.92953431964068, 10.706641515204467), + Coordinates.of(59.93046383654274, 10.70484606360543), + Coordinates.of(59.93008590667682, 10.701817874860211), + Coordinates.of(59.93028982601595, 10.700525251174469), + Coordinates.of(59.92939032560119, 10.69770054003061), + } + ); public static org.geojson.Polygon toGeoJson(Polygon polygon) { var ret = new org.geojson.Polygon(); var coordinates = Arrays .stream(polygon.getCoordinates()) - .map(c -> new LngLatAlt(c.y, c.x)) + .map(c -> new LngLatAlt(c.x, c.y)) .toList(); ret.add(coordinates); diff --git a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java index 2f1e57ef4dc..fcc59af845f 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -131,6 +131,7 @@ static void setup() { VehicleParking .builder() .id(id("parking-1")) + .coordinate(WgsCoordinate.GREENWICH) .name(NonLocalizedString.ofNullable("parking")) .build() ), diff --git a/src/test/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapperTest.java b/src/test/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapperTest.java index 160014213d3..b8d08c7d7f5 100644 --- a/src/test/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapperTest.java @@ -13,9 +13,11 @@ class RequestModesMapperTest { void testMapRequestModesEmptyMapReturnsDefaults() { Map inputModes = Map.of(); + RequestModes wantModes = RequestModes.of().withDirectMode(null).build(); + RequestModes mappedModes = RequestModesMapper.mapRequestModes(inputModes); - assertEquals(RequestModes.of().build(), mappedModes); + assertEquals(wantModes, mappedModes); } @Test @@ -26,6 +28,7 @@ void testMapRequestModesAccessSetReturnsDefaultsForOthers() { .of() .withAccessMode(StreetMode.BIKE) .withTransferMode(StreetMode.BIKE) + .withDirectMode(null) .build(); RequestModes mappedModes = RequestModesMapper.mapRequestModes(inputModes); @@ -37,7 +40,11 @@ void testMapRequestModesAccessSetReturnsDefaultsForOthers() { void testMapRequestModesEgressSetReturnsDefaultsForOthers() { Map inputModes = Map.of("egressMode", StreetMode.CAR); - RequestModes wantModes = RequestModes.of().withEgressMode(StreetMode.CAR).build(); + RequestModes wantModes = RequestModes + .of() + .withEgressMode(StreetMode.CAR) + .withDirectMode(null) + .build(); RequestModes mappedModes = RequestModesMapper.mapRequestModes(inputModes); diff --git a/src/test/java/org/opentripplanner/framework/geometry/WgsCoordinateTest.java b/src/test/java/org/opentripplanner/framework/geometry/WgsCoordinateTest.java index 05051ab7dff..5cd7ac66123 100644 --- a/src/test/java/org/opentripplanner/framework/geometry/WgsCoordinateTest.java +++ b/src/test/java/org/opentripplanner/framework/geometry/WgsCoordinateTest.java @@ -109,15 +109,15 @@ void testGreenwich() { void roundingTo10m() { var hamburg = new WgsCoordinate(Coordinates.HAMBURG); var rounded = hamburg.roundToApproximate10m(); - assertEquals(10.0003, rounded.latitude()); - assertEquals(53.5566, rounded.longitude()); + assertEquals(10.0003, rounded.longitude()); + assertEquals(53.5566, rounded.latitude()); } @Test void roundingTo100m() { var hamburg = new WgsCoordinate(Coordinates.HAMBURG); var rounded = hamburg.roundToApproximate100m(); - assertEquals(10, rounded.latitude()); - assertEquals(53.557, rounded.longitude()); + assertEquals(10, rounded.longitude()); + assertEquals(53.557, rounded.latitude()); } } diff --git a/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java index 96e71b9d3ec..4d2141eb38e 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java @@ -102,12 +102,12 @@ private static class TestModel { public TestModel() { var from = StreetModelForTest.intersectionVertex( - KONGSBERG_PLATFORM_1.x - DELTA, - KONGSBERG_PLATFORM_1.y - DELTA + KONGSBERG_PLATFORM_1.y - DELTA, + KONGSBERG_PLATFORM_1.x - DELTA ); var to = StreetModelForTest.intersectionVertex( - KONGSBERG_PLATFORM_1.x + DELTA, - KONGSBERG_PLATFORM_1.y + DELTA + KONGSBERG_PLATFORM_1.y + DELTA, + KONGSBERG_PLATFORM_1.x + DELTA ); Graph graph = new Graph(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/VehicleParkingLinkingTest.java b/src/test/java/org/opentripplanner/graph_builder/module/VehicleParkingLinkingTest.java index 95f4df79b7b..23f4ffc72ca 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/VehicleParkingLinkingTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/VehicleParkingLinkingTest.java @@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingHelper; import org.opentripplanner.routing.vehicle_parking.VehicleParkingTestGraphData; import org.opentripplanner.street.model.StreetTraversalPermission; @@ -46,8 +45,8 @@ public void setup() { @Test public void entranceWithVertexLinkingTest() { - var parking = VehicleParking - .builder() + var parking = StreetModelForTest + .vehicleParking() .entrance(builder -> builder.entranceId(id("1")).coordinate(new WgsCoordinate(A.getCoordinate())).vertex(A) ) @@ -65,8 +64,8 @@ public void entranceWithVertexLinkingTest() { @Test public void entranceWithoutVertexLinkingTest() { - var parking = VehicleParking - .builder() + var parking = StreetModelForTest + .vehicleParking() .entrance(builder -> builder .entranceId(id("1")) @@ -99,8 +98,8 @@ public void carParkingEntranceToAllTraversableStreetLinkingTest() { StreetModelForTest.streetEdge(A, C, StreetTraversalPermission.NONE); - var parking = VehicleParking - .builder() + var parking = StreetModelForTest + .vehicleParking() .entrance(builder -> builder .entranceId(id("1")) @@ -123,9 +122,8 @@ public void carParkingEntranceToAllTraversableStreetLinkingTest() { @Test public void removeEntranceWithNonExistingVertexTest() { - var vehicleParking = VehicleParking - .builder() - .id(id("VP")) + var vehicleParking = StreetModelForTest + .vehicleParking() .bicyclePlaces(true) .entrance(builder -> builder @@ -159,9 +157,8 @@ public void removeEntranceWithNonExistingVertexTest() { @Test public void removeVehicleParkingWithOneEntranceAndNonExistingVertexTest() { - var vehicleParking = VehicleParking - .builder() - .id(id("VP")) + var vehicleParking = StreetModelForTest + .vehicleParking() .bicyclePlaces(true) .entrance(builder -> builder diff --git a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/SubgraphOnlyFerryTest.java b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/SubgraphOnlyFerryTest.java new file mode 100644 index 00000000000..26aaf12896b --- /dev/null +++ b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/SubgraphOnlyFerryTest.java @@ -0,0 +1,112 @@ +package org.opentripplanner.graph_builder.module.islandpruning; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.opentripplanner.street.model.vertex.TransitStopVertex; +import org.opentripplanner.street.model.vertex.TransitStopVertexBuilder; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.basic.TransitMode; +import org.opentripplanner.transit.model.site.RegularStop; + +class SubgraphOnlyFerryTest { + + private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + private static final RegularStop regularStop1 = TEST_MODEL.stop("TEST-1").build(); + private static final RegularStop regularStop2 = TEST_MODEL.stop("TEST-2").build(); + + @Test + void subgraphHasOnlyFerry() { + TransitStopVertex transitStopVertex = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of(TransitMode.FERRY)) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex); + + assertTrue(subgraph.hasOnlyFerryStops()); + } + + @Test + void subgraphHasOnlyNoFerry() { + TransitStopVertex transitStopVertex1 = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of(TransitMode.BUS)) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex1); + + assertFalse(subgraph.hasOnlyFerryStops()); + } + + @Test + void subgraphHasOnlyNoMode() { + TransitStopVertex transitStopVertex1 = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of()) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex1); + + assertFalse(subgraph.hasOnlyFerryStops()); + } + + @Test + void subgraphHasOnlyFerryMoreStops() { + TransitStopVertex transitStopVertex1 = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of(TransitMode.FERRY)) + .build(); + TransitStopVertex transitStopVertex2 = new TransitStopVertexBuilder() + .withStop(regularStop2) + .withModes(Set.of(TransitMode.FERRY)) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex1); + subgraph.addVertex(transitStopVertex2); + + assertTrue(subgraph.hasOnlyFerryStops()); + } + + @Test + void subgraphHasNotOnlyFerryMoreStops() { + TransitStopVertex transitStopVertex1 = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of(TransitMode.FERRY)) + .build(); + TransitStopVertex transitStopVertex2 = new TransitStopVertexBuilder() + .withStop(regularStop2) + .withModes(Set.of(TransitMode.BUS)) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex1); + subgraph.addVertex(transitStopVertex2); + + assertFalse(subgraph.hasOnlyFerryStops()); + } + + @Test + void subgraphHasNoModeMoreStops() { + TransitStopVertex transitStopVertex1 = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of(TransitMode.FERRY)) + .build(); + TransitStopVertex transitStopVertex2 = new TransitStopVertexBuilder() + .withStop(regularStop2) + .withModes(Set.of()) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex1); + subgraph.addVertex(transitStopVertex2); + + assertFalse(subgraph.hasOnlyFerryStops()); + } +} diff --git a/src/test/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializerTest.java b/src/test/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializerTest.java index da295073e01..2d7df788ad3 100644 --- a/src/test/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializerTest.java +++ b/src/test/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializerTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import java.time.LocalDate; import org.junit.jupiter.api.Test; @@ -86,4 +87,19 @@ void testScheduledTransitLegReferenceLegacyV2Deserialize() { assertEquals(FROM_STOP_POS, ref.fromStopPositionInPattern()); assertEquals(TO_STOP_POS, ref.toStopPositionInPattern()); } + + @Test + void testNullSerializedLegReference() { + assertNull(LegReferenceSerializer.decode(null)); + } + + @Test + void testEmptySerializedLegReference() { + assertNull(LegReferenceSerializer.decode("")); + } + + @Test + void testIllegalBase64CharacterInSerializedLegReference() { + assertNull(LegReferenceSerializer.decode("RUT:Line:5")); + } } diff --git a/src/test/java/org/opentripplanner/netex/mapping/NetexTestDataSample.java b/src/test/java/org/opentripplanner/netex/mapping/NetexTestDataSample.java index b8723fdbcf3..b72c9b05277 100644 --- a/src/test/java/org/opentripplanner/netex/mapping/NetexTestDataSample.java +++ b/src/test/java/org/opentripplanner/netex/mapping/NetexTestDataSample.java @@ -40,6 +40,7 @@ import org.rutebanken.netex.model.ScheduledStopPointRefStructure; import org.rutebanken.netex.model.ServiceAlterationEnumeration; import org.rutebanken.netex.model.ServiceJourney; +import org.rutebanken.netex.model.ServiceJourneyRefStructure; import org.rutebanken.netex.model.StopPointInJourneyPattern; import org.rutebanken.netex.model.StopPointInJourneyPatternRefStructure; import org.rutebanken.netex.model.TimetabledPassingTime; @@ -50,9 +51,11 @@ public class NetexTestDataSample { public static final String SERVICE_JOURNEY_ID = "RUT:ServiceJourney:1"; + public static final String DATED_SERVICE_JOURNEY_ID_1 = "RUT:DatedServiceJourney:1"; + public static final String DATED_SERVICE_JOURNEY_ID_2 = "RUT:DatedServiceJourney:2"; public static final List DATED_SERVICE_JOURNEY_ID = List.of( - "RUT:DatedServiceJourney:1", - "RUT:DatedServiceJourney:2" + DATED_SERVICE_JOURNEY_ID_1, + DATED_SERVICE_JOURNEY_ID_2 ); public static final List OPERATING_DAYS = List.of("2022-02-28", "2022-02-29"); private static final DayType EVERYDAY = new DayType() @@ -174,6 +177,11 @@ public NetexTestDataSample() { DatedServiceJourney datedServiceJourney = new DatedServiceJourney() .withId(DATED_SERVICE_JOURNEY_ID.get(i)) + .withJourneyRef( + List.of( + MappingSupport.createWrappedRef(SERVICE_JOURNEY_ID, ServiceJourneyRefStructure.class) + ) + ) .withServiceAlteration(ServiceAlterationEnumeration.PLANNED) .withOperatingDayRef(new OperatingDayRefStructure().withRef(operatingDay.getId())); @@ -218,6 +226,15 @@ public HierarchicalMapById getServiceJourneyById() { return serviceJourneyById; } + public DatedServiceJourney getDatedServiceJourneyById(String id) { + return datedServiceJourneyBySjId + .values() + .stream() + .filter(datedServiceJourney -> datedServiceJourney.getId().equals(id)) + .findFirst() + .orElse(null); + } + public ServiceJourney getServiceJourney() { return serviceJourneyById.lookup(SERVICE_JOURNEY_ID); } diff --git a/src/test/java/org/opentripplanner/netex/mapping/TripPatternMapperTest.java b/src/test/java/org/opentripplanner/netex/mapping/TripPatternMapperTest.java index 74448fb3638..95af8f623e5 100644 --- a/src/test/java/org/opentripplanner/netex/mapping/TripPatternMapperTest.java +++ b/src/test/java/org/opentripplanner/netex/mapping/TripPatternMapperTest.java @@ -1,9 +1,12 @@ package org.opentripplanner.netex.mapping; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.ArrayListMultimap; +import java.util.List; import java.util.Map; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -19,17 +22,19 @@ import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; import org.rutebanken.netex.model.DatedServiceJourney; +import org.rutebanken.netex.model.DatedServiceJourneyRefStructure; import org.rutebanken.netex.model.OperatingDay; +import org.rutebanken.netex.model.ServiceAlterationEnumeration; /** * @author Thomas Gran (Capra) - tgr@capraconsulting.no (29.11.2017) */ -public class TripPatternMapperTest { +class TripPatternMapperTest { private static final FeedScopedId SERVICE_ID = TransitModelForTest.id("S01"); @Test - public void testMapTripPattern() { + void testMapTripPattern() { NetexTestDataSample sample = new NetexTestDataSample(); TripPatternMapper tripPatternMapper = new TripPatternMapper( @@ -88,9 +93,85 @@ public void testMapTripPattern() { } @Test - public void testMapTripPattern_datedServiceJourney() { + void testMapTripPattern_datedServiceJourney() { NetexTestDataSample sample = new NetexTestDataSample(); + Optional res = mapTripPattern(sample); + assertTrue(res.isPresent()); + + var r = res.get(); + + assertEquals(2, r.tripOnServiceDates().size()); + + Trip trip = r.tripPattern().scheduledTripsAsStream().findFirst().get(); + + for (TripOnServiceDate tripOnServiceDate : r.tripOnServiceDates()) { + assertEquals(trip, tripOnServiceDate.getTrip()); + assertEquals(TripAlteration.PLANNED, tripOnServiceDate.getTripAlteration()); + assertEquals( + 1, + sample + .getOperatingDaysById() + .localValues() + .stream() + .map(OperatingDay::getId) + .filter(id -> id.equals(tripOnServiceDate.getServiceDate().toString())) + .count() + ); + } + } + + @Test + void testDatedServiceJourneyReplacement() { + NetexTestDataSample sample = new NetexTestDataSample(); + DatedServiceJourney dsjReplaced = sample.getDatedServiceJourneyById( + NetexTestDataSample.DATED_SERVICE_JOURNEY_ID_1 + ); + dsjReplaced.setServiceAlteration(ServiceAlterationEnumeration.REPLACED); + DatedServiceJourney dsjReplacing = sample.getDatedServiceJourneyById( + NetexTestDataSample.DATED_SERVICE_JOURNEY_ID_2 + ); + dsjReplacing.withJourneyRef( + List.of( + MappingSupport.createWrappedRef(dsjReplaced.getId(), DatedServiceJourneyRefStructure.class) + ) + ); + Optional res = mapTripPattern(sample); + + assertTrue(res.isPresent()); + var r = res.get(); + Optional replacedTripOnServiceDate = r + .tripOnServiceDates() + .stream() + .filter(tripOnServiceDate -> + NetexTestDataSample.DATED_SERVICE_JOURNEY_ID_1.equals(tripOnServiceDate.getId().getId()) + ) + .findFirst(); + + assertTrue(replacedTripOnServiceDate.isPresent()); + assertEquals(TripAlteration.REPLACED, replacedTripOnServiceDate.get().getTripAlteration()); + + Optional replacingTripOnServiceDate = r + .tripOnServiceDates() + .stream() + .filter(tripOnServiceDate -> + NetexTestDataSample.DATED_SERVICE_JOURNEY_ID_2.equals(tripOnServiceDate.getId().getId()) + ) + .findFirst(); + + assertTrue(replacingTripOnServiceDate.isPresent()); + assertEquals(TripAlteration.PLANNED, replacingTripOnServiceDate.get().getTripAlteration()); + assertFalse(replacingTripOnServiceDate.get().getReplacementFor().isEmpty()); + + // the replaced trip should refer to the same object (object identity) whether it is accessed + // directly from the replaced DSJ or indirectly through the replacing DSJ. + assertSame( + replacingTripOnServiceDate.get().getReplacementFor().getFirst().getTrip(), + replacedTripOnServiceDate.get().getTrip() + ); + } + + private static Optional mapTripPattern(NetexTestDataSample sample) { HierarchicalMapById datedServiceJourneys = new HierarchicalMapById<>(); datedServiceJourneys.addAll(sample.getDatedServiceJourneyBySjId().values()); @@ -121,28 +202,6 @@ public void testMapTripPattern_datedServiceJourney() { Optional res = tripPatternMapper.mapTripPattern( sample.getJourneyPattern() ); - - assertTrue(res.isPresent()); - - var r = res.get(); - - assertEquals(2, r.tripOnServiceDates().size()); - - Trip trip = r.tripPattern().scheduledTripsAsStream().findFirst().get(); - - for (TripOnServiceDate tripOnServiceDate : r.tripOnServiceDates()) { - assertEquals(trip, tripOnServiceDate.getTrip()); - assertEquals(TripAlteration.PLANNED, tripOnServiceDate.getTripAlteration()); - assertEquals( - 1, - sample - .getOperatingDaysById() - .localValues() - .stream() - .map(OperatingDay::getId) - .filter(id -> id.equals(tripOnServiceDate.getServiceDate().toString())) - .count() - ); - } + return res; } } diff --git a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java index c316793ad8c..c0af4cf2701 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java @@ -92,21 +92,6 @@ void testIsOneWayBicycle() { assertTrue(way.isOneWayReverseBicycle()); } - @Test - void testIsOneDirectionSidepath() { - OSMWay way = new OSMWay(); - assertFalse(way.isForwardDirectionSidepath()); - assertFalse(way.isReverseDirectionSidepath()); - - way.addTag("bicycle:forward", "use_sidepath"); - assertTrue(way.isForwardDirectionSidepath()); - assertFalse(way.isReverseDirectionSidepath()); - - way.addTag("bicycle:backward", "use_sidepath"); - assertTrue(way.isForwardDirectionSidepath()); - assertTrue(way.isReverseDirectionSidepath()); - } - @Test void testIsOpposableCycleway() { OSMWay way = new OSMWay(); diff --git a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java index ca5db77df12..aefd287cac8 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java @@ -136,7 +136,7 @@ void testBicycleDenied() { assertFalse(tags.isBicycleExplicitlyDenied(), "bicycle=" + allowedValue); } - for (var deniedValue : List.of("no", "dismount", "license", "use_sidepath")) { + for (var deniedValue : List.of("no", "dismount", "license")) { tags.addTag("bicycle", deniedValue); assertTrue(tags.isBicycleExplicitlyDenied(), "bicycle=" + deniedValue); } diff --git a/src/test/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapperTest.java b/src/test/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapperTest.java index 9a16f6a8e2e..2a8988dda61 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapperTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapperTest.java @@ -147,6 +147,48 @@ void bicycleDiscouraged() { assertEquals(2.94, discouragedProps.bicycleSafety().forward(), epsilon); } + @Test + void footUseSidepath() { + var regular = WayTestData.pedestrianTunnel(); + var props = wps.getDataForWay(regular); + assertEquals(PEDESTRIAN_AND_BICYCLE, props.getPermission()); + assertEquals(1, props.walkSafety().forward()); + + var useSidepath = WayTestData.pedestrianTunnel().addTag("foot", "use_sidepath"); + var useSidepathProps = wps.getDataForWay(useSidepath); + assertEquals(PEDESTRIAN_AND_BICYCLE, useSidepathProps.getPermission()); + assertEquals(5, useSidepathProps.walkSafety().forward()); + } + + @Test + void bicycleUseSidepath() { + var regular = WayTestData.southeastLaBonitaWay(); + var props = wps.getDataForWay(regular); + assertEquals(ALL, props.getPermission()); + assertEquals(.98, props.bicycleSafety().forward()); + + var useSidepath = WayTestData.southeastLaBonitaWay().addTag("bicycle", "use_sidepath"); + var useSidepathProps = wps.getDataForWay(useSidepath); + assertEquals(ALL, useSidepathProps.getPermission()); + assertEquals(4.9, useSidepathProps.bicycleSafety().forward(), epsilon); + + var useSidepathForward = WayTestData + .southeastLaBonitaWay() + .addTag("bicycle:forward", "use_sidepath"); + var useSidepathForwardProps = wps.getDataForWay(useSidepathForward); + assertEquals(ALL, useSidepathForwardProps.getPermission()); + assertEquals(4.9, useSidepathForwardProps.bicycleSafety().forward(), epsilon); + assertEquals(0.98, useSidepathForwardProps.bicycleSafety().back(), epsilon); + + var useSidepathBackward = WayTestData + .southeastLaBonitaWay() + .addTag("bicycle:backward", "use_sidepath"); + var useSidepathBackwardProps = wps.getDataForWay(useSidepathBackward); + assertEquals(ALL, useSidepathBackwardProps.getPermission()); + assertEquals(0.98, useSidepathBackwardProps.bicycleSafety().forward(), epsilon); + assertEquals(4.9, useSidepathBackwardProps.bicycleSafety().back(), epsilon); + } + /** * Test that two values are within epsilon of each other. */ diff --git a/src/test/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapperTest.java b/src/test/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapperTest.java index a2f84873f20..4dd52195acb 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapperTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapperTest.java @@ -162,6 +162,13 @@ public void testSafetyWithMixins() { wps.getDataForWay(wayWithMixinsAndCustomSafety).walkSafety().forward(), epsilon ); + + OSMWithTags wayWithBicycleSidePath = new OSMWithTags(); + wayWithBicycleSidePath.addTag("bicycle", "use_sidepath"); + assertEquals(8, wps.getDataForWay(wayWithBicycleSidePath).walkSafety().forward(), epsilon); + OSMWithTags wayWithFootSidePath = new OSMWithTags(); + wayWithFootSidePath.addTag("foot", "use_sidepath"); + assertEquals(8, wps.getDataForWay(wayWithFootSidePath).walkSafety().forward(), epsilon); } @Test diff --git a/src/test/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySetTest.java b/src/test/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySetTest.java index e8f5b6e51d3..24360539b6f 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySetTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySetTest.java @@ -256,65 +256,6 @@ void testMotorVehicleTagDeniedPermissions() { assertTrue(permissionPair.main().allowsNothing());*/ } - @Test - void testSidepathPermissions() { - OSMWay way = new OSMWay(); - way.addTag("bicycle", "use_sidepath"); - way.addTag("highway", "primary"); - way.addTag("lanes", "2"); - way.addTag("maxspeed", "70"); - way.addTag("oneway", "yes"); - var permissionPair = getWayProperties(way); - - assertFalse(permissionPair.main().allows(StreetTraversalPermission.BICYCLE)); - assertFalse(permissionPair.back().allows(StreetTraversalPermission.BICYCLE)); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.CAR)); - assertFalse(permissionPair.back().allows(StreetTraversalPermission.CAR)); - - way = new OSMWay(); - way.addTag("bicycle:forward", "use_sidepath"); - way.addTag("highway", "tertiary"); - permissionPair = getWayProperties(way); - - assertFalse(permissionPair.main().allows(StreetTraversalPermission.BICYCLE)); - assertTrue(permissionPair.back().allows(StreetTraversalPermission.BICYCLE)); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.CAR)); - assertTrue(permissionPair.back().allows(StreetTraversalPermission.CAR)); - - way = new OSMWay(); - way.addTag("bicycle:backward", "use_sidepath"); - way.addTag("highway", "tertiary"); - permissionPair = getWayProperties(way); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.BICYCLE)); - assertFalse(permissionPair.back().allows(StreetTraversalPermission.BICYCLE)); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.CAR)); - assertTrue(permissionPair.back().allows(StreetTraversalPermission.CAR)); - - way = new OSMWay(); - way.addTag("highway", "tertiary"); - way.addTag("oneway", "yes"); - way.addTag("oneway:bicycle", "no"); - permissionPair = getWayProperties(way); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.BICYCLE)); - assertTrue(permissionPair.back().allows(StreetTraversalPermission.BICYCLE)); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.CAR)); - assertFalse(permissionPair.back().allows(StreetTraversalPermission.CAR)); - - way.addTag("bicycle:forward", "use_sidepath"); - permissionPair = getWayProperties(way); - assertFalse(permissionPair.main().allows(StreetTraversalPermission.BICYCLE)); - assertTrue(permissionPair.back().allows(StreetTraversalPermission.BICYCLE)); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.CAR)); - assertFalse(permissionPair.back().allows(StreetTraversalPermission.CAR)); - } - private StreetTraversalPermissionPair getWayProperties(OSMWay way) { WayPropertySet wayPropertySet = new WayPropertySet(); WayProperties wayData = wayPropertySet.getDataForWay(way); diff --git a/src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java b/src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java index 0bca83be8bf..78489508dc4 100644 --- a/src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java +++ b/src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java @@ -60,6 +60,8 @@ public interface RaptorTestConstants { int STOP_L = 12; int STOP_M = 13; + int NUM_STOPS = 14; + // Stop position in pattern int STOP_POS_0 = 0; int STOP_POS_1 = 1; 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 2a77996b47b..037c345bc23 100644 --- a/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java +++ b/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java @@ -66,6 +66,8 @@ public class TestTransitData private final List constrainedTransfers = new ArrayList<>(); private final GeneralizedCostParametersBuilder costParamsBuilder = GeneralizedCostParameters.of(); + private final int[] stopBoardAlightCosts = new int[NUM_STOPS]; + private RaptorSlackProvider slackProvider = SLACK_PROVIDER; @Override @@ -300,6 +302,11 @@ public TestTransitData withConstrainedTransfer( return this; } + public TestTransitData withStopBoardAlightCost(int stop, int boardAlightCost) { + stopBoardAlightCosts[stop] = boardAlightCost; + return this; + } + public GeneralizedCostParametersBuilder mcCostParamsBuilder() { return costParamsBuilder; } @@ -352,7 +359,7 @@ public List getPatterns() { private int[] stopBoardAlightCost() { // Not implemented, no test for this yet. - return null; + return stopBoardAlightCosts; } private void expandNumOfStops(int stopIndex) { diff --git a/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopTransferCostTest.java b/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopTransferCostTest.java new file mode 100644 index 00000000000..21c1dd2a755 --- /dev/null +++ b/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopTransferCostTest.java @@ -0,0 +1,76 @@ +package org.opentripplanner.raptor.moduletests; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.raptor._data.transit.TestRoute.route; +import static org.opentripplanner.raptor._data.transit.TestTripSchedule.schedule; +import static org.opentripplanner.raptor.moduletests.support.RaptorModuleTestConfig.multiCriteria; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.raptor.RaptorService; +import org.opentripplanner.raptor._data.RaptorTestConstants; +import org.opentripplanner.raptor._data.transit.TestAccessEgress; +import org.opentripplanner.raptor._data.transit.TestTransitData; +import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; +import org.opentripplanner.raptor.configure.RaptorConfig; +import org.opentripplanner.raptor.moduletests.support.ModuleTestDebugLogging; +import org.opentripplanner.raptor.moduletests.support.RaptorModuleTestCase; + +/** + * FEATURE UNDER TEST + * + * This verifies that the stopTransferCost is not applied for egress legs. If this is not correctly + * handled by the heuristics optimization, the cheapest journey could be discarded. + */ +public class B05_EgressStopTransferCostTest implements RaptorTestConstants { + + private final TestTransitData data = new TestTransitData(); + private final RaptorRequestBuilder requestBuilder = new RaptorRequestBuilder<>(); + private final RaptorService raptorService = new RaptorService<>( + RaptorConfig.defaultConfigForTest() + ); + + @BeforeEach + void setup() { + data + .withRoute(route("R1", STOP_B, STOP_C).withTimetable(schedule("0:10, 0:14"))) + .withRoute(route("R2", STOP_C, STOP_D).withTimetable(schedule("0:18, 0:20"))); + + data.mcCostParamsBuilder().transferCost(0).boardCost(0); + data.withStopBoardAlightCost(STOP_D, 60000); + + requestBuilder + .searchParams() + .addAccessPaths(TestAccessEgress.free(STOP_B)) + .addEgressPaths( + TestAccessEgress.walk(STOP_C, D5m), // This will be the fastest + TestAccessEgress.walk(STOP_D, D20s) // This will be the cheapest + ) + .earliestDepartureTime(T00_00) + .latestArrivalTime(T00_30); + + ModuleTestDebugLogging.setupDebugLogging(data, requestBuilder); + } + + static List testCases() { + return RaptorModuleTestCase + .of() + .add( + multiCriteria(), + // We should get both the fastest and the c1-cheapest results + // The stopTransferCost should not be applied to the egress leg from STOP_D + "B ~ BUS R1 0:10 0:14 ~ C ~ Walk 5m [0:10 0:19 9m Tₓ0 C₁840]", + "B ~ BUS R1 0:10 0:14 ~ C ~ BUS R2 0:18 0:20 ~ D ~ Walk 20s [0:10 0:20:20 10m20s Tₓ1 C₁640]" + ) + .build(); + } + + @ParameterizedTest + @MethodSource("testCases") + void testRaptor(RaptorModuleTestCase testCase) { + assertEquals(testCase.expected(), testCase.run(raptorService, data, requestBuilder)); + } +} diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayerTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayerTest.java new file mode 100644 index 00000000000..7c674252e6a --- /dev/null +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayerTest.java @@ -0,0 +1,201 @@ +package org.opentripplanner.routing.algorithm.raptoradapter.transit; + +import static java.util.Map.entry; +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 java.time.LocalDate; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.opentripplanner.model.StopTime; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.framework.Deduplicator; +import org.opentripplanner.transit.model.network.RoutingTripPattern; +import org.opentripplanner.transit.model.network.StopPattern; +import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.timetable.TripTimes; +import org.opentripplanner.transit.model.timetable.TripTimesFactory; + +class TransitLayerTest { + + private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + private static final TripTimes TRIP_TIMES; + + private static final RoutingTripPattern TRIP_PATTERN; + + static { + var stop = TEST_MODEL.stop("TEST:STOP", 0, 0).build(); + var stopTime = new StopTime(); + stopTime.setStop(stop); + var stopPattern = new StopPattern(List.of(stopTime)); + var route = TransitModelForTest.route("1").build(); + TRIP_PATTERN = + TripPattern + .of(TransitModelForTest.id("P1")) + .withRoute(route) + .withStopPattern(stopPattern) + .build() + .getRoutingTripPattern(); + TRIP_TIMES = + TripTimesFactory.tripTimes( + TransitModelForTest.trip("1").withRoute(route).build(), + List.of(new StopTime()), + new Deduplicator() + ); + } + + @Test + void testGetTripPatternsRunningOnDateCopy() { + var date = LocalDate.of(2024, 1, 1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + date + ); + var tripPatterns = List.of(tripPatternForDate); + var transitLayer = new TransitLayer( + Map.of(date, tripPatterns), + null, + null, + null, + null, + null, + null, + null, + null + ); + var runningOnDate = transitLayer.getTripPatternsRunningOnDateCopy(date); + assertEquals(1, runningOnDate.size()); + assertEquals(tripPatterns, runningOnDate); + assertFalse(tripPatterns == runningOnDate); + assertEquals(0, transitLayer.getTripPatternsRunningOnDateCopy(date.minusDays(1)).size()); + assertEquals(0, transitLayer.getTripPatternsRunningOnDateCopy(date.plusDays(1)).size()); + } + + @Test + void testGetTripPatternsForRunningDate() { + var date = LocalDate.of(2024, 1, 1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + date + ); + var tripPatterns = List.of(tripPatternForDate); + var transitLayer = new TransitLayer( + Map.of(date, tripPatterns), + null, + null, + null, + null, + null, + null, + null, + null + ); + var runningOnDate = transitLayer.getTripPatternsForRunningDate(date); + assertEquals(1, runningOnDate.size()); + assertEquals(tripPatterns, runningOnDate); + assertTrue(tripPatterns == runningOnDate); + assertEquals(0, transitLayer.getTripPatternsForRunningDate(date.minusDays(1)).size()); + assertEquals(0, transitLayer.getTripPatternsForRunningDate(date.plusDays(1)).size()); + } + + @Test + void testGetTripPatternsOnServiceDateCopyWithSameRunningAndServiceDate() { + var date = LocalDate.of(2024, 1, 1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + date + ); + var transitLayer = new TransitLayer( + Map.of(date, List.of(tripPatternForDate)), + null, + null, + null, + null, + null, + null, + null, + null + ); + var startingOnDate = transitLayer.getTripPatternsOnServiceDateCopy(date); + assertEquals(1, startingOnDate.size()); + assertEquals(tripPatternForDate, startingOnDate.getFirst()); + assertEquals(0, transitLayer.getTripPatternsOnServiceDateCopy(date.minusDays(1)).size()); + assertEquals(0, transitLayer.getTripPatternsOnServiceDateCopy(date.plusDays(1)).size()); + } + + @Test + void testGetTripPatternsOnServiceDateCopyWithServiceRunningAfterMidnight() { + var runningDate = LocalDate.of(2024, 1, 1); + var serviceDate = runningDate.minusDays(1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + serviceDate + ); + var transitLayer = new TransitLayer( + Map.of(runningDate, List.of(tripPatternForDate)), + null, + null, + null, + null, + null, + null, + null, + null + ); + var startingOnDate = transitLayer.getTripPatternsOnServiceDateCopy(serviceDate); + // starting date should be determined by service date, not running date which refers to the + // normal calendar date that the trip pattern is running on + assertEquals(1, startingOnDate.size()); + assertEquals(tripPatternForDate, startingOnDate.getFirst()); + assertEquals(0, transitLayer.getTripPatternsOnServiceDateCopy(runningDate).size()); + } + + @Test + void testGetTripPatternsOnServiceDateCopyWithServiceRunningBeforeAndAfterMidnight() { + // This is same as the service date + var firstRunningDate = LocalDate.of(2024, 1, 1); + var secondRunningDate = firstRunningDate.plusDays(1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + firstRunningDate + ); + var transitLayer = new TransitLayer( + Map.ofEntries( + entry(firstRunningDate, List.of(tripPatternForDate)), + entry(secondRunningDate, List.of(tripPatternForDate)) + ), + null, + null, + null, + null, + null, + null, + null, + null + ); + var startingOnDate = transitLayer.getTripPatternsOnServiceDateCopy(firstRunningDate); + // Transit layer indexes trip patterns by running date and to get trip patterns for certain + // service date, we need to look up the trip patterns for the next running date as well, but + // we don't want to return duplicates + assertEquals(1, startingOnDate.size()); + assertEquals(tripPatternForDate, startingOnDate.getFirst()); + assertEquals(0, transitLayer.getTripPatternsOnServiceDateCopy(secondRunningDate).size()); + } +} diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java index 7b7ba23499c..3570d92bdbe 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java @@ -76,22 +76,28 @@ public void calculateMinCost() { // - Transit factor: 80 (min of 80 and 100) // Board cost is 500: - assertEquals(500, subject.calculateMinCost(0, 0)); + assertEquals(500, subject.calculateRemainingMinCost(0, 0, 0)); // The transfer 1s * 80 = 80 + board cost 500 - assertEquals(580, subject.calculateMinCost(1, 0)); + assertEquals(580, subject.calculateRemainingMinCost(1, 0, 0)); // Board 2 times and transfer 1: 2 * 500 + 200 - assertEquals(1200, subject.calculateMinCost(0, 1)); + assertEquals(1200, subject.calculateRemainingMinCost(0, 1, 0)); // Transit 200s * 80 + Board 4 * 500 + Transfer 3 * 200 - assertEquals(18_600, subject.calculateMinCost(200, 3)); + assertEquals(18_600, subject.calculateRemainingMinCost(200, 3, 0)); + + // Cost of egress should subtract the stop transfer cost 25 + assertEquals(-25, subject.calculateRemainingMinCost(0, -1, 1)); } @Test public void testConvertBetweenRaptorAndMainOtpDomainModel() { - assertEquals(RaptorCostConverter.toRaptorCost(BOARD_COST_SEC), subject.calculateMinCost(0, 0)); + assertEquals( + RaptorCostConverter.toRaptorCost(BOARD_COST_SEC), + subject.calculateRemainingMinCost(0, 0, 0) + ); assertEquals( RaptorCostConverter.toRaptorCost(0.8 * 20 + BOARD_COST_SEC), - subject.calculateMinCost(20, 0) + subject.calculateRemainingMinCost(20, 0, 0) ); } diff --git a/src/test/java/org/opentripplanner/routing/core/MoneyTest.java b/src/test/java/org/opentripplanner/routing/core/MoneyTest.java index 5f197708f1d..01392e64493 100644 --- a/src/test/java/org/opentripplanner/routing/core/MoneyTest.java +++ b/src/test/java/org/opentripplanner/routing/core/MoneyTest.java @@ -102,4 +102,9 @@ void greaterThan() { void serializable() { assertInstanceOf(Serializable.class, oneDollar); } + + @Test + void equalHashCode() { + assertEquals(Money.usDollars(5).hashCode(), Money.usDollars(5).hashCode()); + } } diff --git a/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java b/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java index 49dc42e6f4c..c01558338a4 100644 --- a/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java +++ b/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java @@ -39,8 +39,8 @@ public class TemporaryVerticesContainerTest { private final Graph g = new Graph(new Deduplicator()); private final StreetVertex a = StreetModelForTest.intersectionVertex("A", 1.0, 1.0); - private final StreetVertex b = StreetModelForTest.intersectionVertex("B", 0.0, 1.0); - private final StreetVertex c = StreetModelForTest.intersectionVertex("C", 1.0, 0.0); + private final StreetVertex b = StreetModelForTest.intersectionVertex("B", 1.0, 0.0); + private final StreetVertex c = StreetModelForTest.intersectionVertex("C", 0.0, 1.0); private final List permanentVertexes = Arrays.asList(a, b, c); // - And travel *origin* is 0,4 degrees on the road from B to A private final GenericLocation from = new GenericLocation(1.0, 0.4); diff --git a/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelperTest.java b/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelperTest.java index 9c80cb9cda9..90bdeb015a4 100644 --- a/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelperTest.java +++ b/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelperTest.java @@ -10,6 +10,7 @@ import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.vehicle_parking.VehicleParking.VehicleParkingEntranceCreator; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.edge.VehicleParkingEdge; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -41,8 +42,8 @@ void linkThreeVerticesTest() { @Test void linkSkippingEdgesTest() { Graph graph = new Graph(); - var vehicleParking = VehicleParking - .builder() + var vehicleParking = StreetModelForTest + .vehicleParking() .entrances( IntStream .rangeClosed(1, 3) @@ -67,8 +68,8 @@ void linkSkippingEdgesTest() { } private VehicleParking createParingWithEntrances(int entranceNumber) { - return VehicleParking - .builder() + return StreetModelForTest + .vehicleParking() .bicyclePlaces(true) .entrances( IntStream diff --git a/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingTestUtil.java b/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingTestUtil.java index 078d7350dcc..fca20322ebe 100644 --- a/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingTestUtil.java +++ b/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingTestUtil.java @@ -2,6 +2,7 @@ import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.NonLocalizedString; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.transit.model.framework.FeedScopedId; public class VehicleParkingTestUtil { @@ -25,8 +26,8 @@ public static VehicleParking createParkingWithEntrances( .coordinate(new WgsCoordinate(y, x)) .walkAccessible(true); - return VehicleParking - .builder() + return StreetModelForTest + .vehicleParking() .id(new FeedScopedId(TEST_FEED_ID, id)) .bicyclePlaces(true) .capacity(vehiclePlaces) diff --git a/src/test/java/org/opentripplanner/standalone/config/framework/json/NodeAdapterTest.java b/src/test/java/org/opentripplanner/standalone/config/framework/json/NodeAdapterTest.java index be44d1ea86f..9d3719c2285 100644 --- a/src/test/java/org/opentripplanner/standalone/config/framework/json/NodeAdapterTest.java +++ b/src/test/java/org/opentripplanner/standalone/config/framework/json/NodeAdapterTest.java @@ -34,7 +34,7 @@ public class NodeAdapterTest { public static final String NON_UNUSED_PARAMETERS = "EXPECTED_NONE"; @Test - public void testAsRawNode() { + void testAsRawNode() { NodeAdapter subject = newNodeAdapterForTest("{ child : { foo : 'bar' } }"); // Define child @@ -53,7 +53,7 @@ public void testAsRawNode() { } @Test - public void isEmpty() { + void isEmpty() { NodeAdapter subject = newNodeAdapterForTest(""); assertTrue(subject.of("alf").asObject().isEmpty()); @@ -64,7 +64,7 @@ public void isEmpty() { } @Test - public void path() { + void path() { NodeAdapter subject = newNodeAdapterForTest("{ foo : 'bar' }"); assertFalse(subject.of("foo").asObject().isEmpty()); assertTrue(subject.of("missingObject").asObject().isEmpty()); @@ -72,7 +72,7 @@ public void path() { } @Test - public void docInfo() { + void docInfo() { NodeAdapter subject = newNodeAdapterForTest("{ bool: false }"); subject.of("bool").since(V2_0).summary("B Summary").description("Ddd").asBoolean(); subject.of("en").since(V2_1).summary("EN Summary").asEnum(SECONDS); @@ -95,7 +95,7 @@ public void docInfo() { } @Test - public void asBoolean() { + void asBoolean() { NodeAdapter subject = newNodeAdapterForTest("{ aBoolean : true }"); assertTrue(subject.of("aBoolean").asBoolean()); assertTrue(subject.of("aBoolean").asBoolean(false)); @@ -103,7 +103,7 @@ public void asBoolean() { } @Test - public void asDouble() { + void asDouble() { NodeAdapter subject = newNodeAdapterForTest("{ aDouble : 7.0 }"); assertEquals(7.0, subject.of("aDouble").asDouble(-1d), 0.01); assertEquals(7.0, subject.of("aDouble").asDouble(), 0.01); @@ -111,28 +111,28 @@ public void asDouble() { } @Test - public void asDoubles() { + void asDoubles() { NodeAdapter subject = newNodeAdapterForTest("{ key : [ 2.0, 3.0, 5.0 ] }"); assertEquals(List.of(2d, 3d, 5d), subject.of("key").asDoubles(null)); assertEquals(NON_UNUSED_PARAMETERS, unusedParams(subject)); } @Test - public void asInt() { + void asInt() { NodeAdapter subject = newNodeAdapterForTest("{ aInt : 5 }"); assertEquals(5, subject.of("aInt").asInt(-1)); assertEquals(-1, subject.of("missingField").asInt(-1)); } @Test - public void asLong() { + void asLong() { NodeAdapter subject = newNodeAdapterForTest("{ key : 5 }"); assertEquals(5, subject.of("key").asLong(-1)); assertEquals(-1, subject.of("missingField").asLong(-1)); } @Test - public void asText() { + void asText() { NodeAdapter subject = newNodeAdapterForTest("{ aText : 'TEXT' }"); assertEquals("TEXT", subject.of("aText").asString("DEFAULT")); assertEquals("DEFAULT", subject.of("missingField").asString("DEFAULT")); @@ -142,19 +142,19 @@ public void asText() { } @Test - public void requiredAsText() { + void requiredAsText() { NodeAdapter subject = newNodeAdapterForTest("{ }"); assertThrows(OtpAppException.class, () -> subject.of("missingField").asString()); } @Test - public void rawAsText() { + void rawAsText() { NodeAdapter subject = newNodeAdapterForTest("{ aText : 'TEXT' }"); assertEquals("TEXT", subject.of("aText").asObject().asText()); } @Test - public void asEnum() { + void asEnum() { // Given NodeAdapter subject = newNodeAdapterForTest("{ a : 'A', abc : 'a-b-c' }"); @@ -169,7 +169,7 @@ public void asEnum() { } @Test - public void asEnumWithIllegalPropertySet() { + void asEnumWithIllegalPropertySet() { // Given NodeAdapter subject = newNodeAdapterForTest( """ @@ -205,7 +205,7 @@ public void asEnumWithIllegalPropertySet() { } @Test - public void asEnumMap() { + void asEnumMap() { // With optional enum values in map NodeAdapter subject = newNodeAdapterForTest("{ key : { A: true, B: false } }"); assertEquals( @@ -220,7 +220,7 @@ public void asEnumMap() { } @Test - public void asEnumMapWithCustomType() { + void asEnumMapWithCustomType() { // With optional enum values in map NodeAdapter subject = newNodeAdapterForTest("{ key : { A: {a:'Foo'} } }"); assertEquals( @@ -235,7 +235,7 @@ public void asEnumMapWithCustomType() { } @Test - public void asEnumMapWithDefaultValue() { + void asEnumMapWithDefaultValue() { var subject = newNodeAdapterForTest("{}"); final Map dflt = Map.of(AnEnum.A, new ARecord("Foo")); assertEquals(dflt, subject.of("key").asEnumMap(AnEnum.class, ARecord::fromJson, dflt)); @@ -243,7 +243,7 @@ public void asEnumMapWithDefaultValue() { } @Test - public void asEnumMapWithUnknownKey() { + void asEnumMapWithUnknownKey() { NodeAdapter subject = newNodeAdapterForTest("{ enumMap : { unknown : 7 } }"); subject.of("enumMap").asEnumMap(AnEnum.class, Double.class); @@ -261,7 +261,7 @@ public void asEnumMapWithUnknownKey() { } @Test - public void asEnumMapAllKeysRequired() { + void asEnumMapAllKeysRequired() { var subject = newNodeAdapterForTest("{ key : { A: true, b: false, a_B_c: true } }"); assertEquals( Map.of(AnEnum.A, true, AnEnum.B, false, AnEnum.A_B_C, true), @@ -280,7 +280,7 @@ public void asEnumMapAllKeysRequired() { } @Test - public void asEnumMapWithRequiredMissingValue() { + void asEnumMapWithRequiredMissingValue() { // A value for C is missing in map NodeAdapter subject = newNodeAdapterForTest("{ key : { A: true, B: false } }"); @@ -291,7 +291,7 @@ public void asEnumMapWithRequiredMissingValue() { } @Test - public void asEnumSet() { + void asEnumSet() { NodeAdapter subject = newNodeAdapterForTest("{ key : [ 'A', 'B' ] }"); assertEquals(Set.of(AnEnum.A, AnEnum.B), subject.of("key").asEnumSet(AnEnum.class)); assertEquals(Set.of(), subject.of("missing-key").asEnumSet(AnEnum.class)); @@ -299,13 +299,13 @@ public void asEnumSet() { } @Test - public void asEnumSetFailsUsingWrongFormat() { + void asEnumSetFailsUsingWrongFormat() { NodeAdapter subject = newNodeAdapterForTest("{ key : 'A,B' }"); assertThrows(OtpAppException.class, () -> subject.of("key").asEnumSet(AnEnum.class)); } @Test - public void asFeedScopedId() { + void asFeedScopedId() { NodeAdapter subject = newNodeAdapterForTest("{ key1: 'A:23', key2: 'B:12' }"); assertEquals("A:23", subject.of("key1").asFeedScopedId(null).toString()); assertEquals("B:12", subject.of("key2").asFeedScopedId(null).toString()); @@ -316,7 +316,7 @@ public void asFeedScopedId() { } @Test - public void asFeedScopedIds() { + void asFeedScopedIds() { NodeAdapter subject = newNodeAdapterForTest("{ routes: ['A:23', 'B:12']}"); assertEquals("[A:23, B:12]", subject.of("routes").asFeedScopedIds(List.of()).toString()); assertEquals("[]", subject.of("missing-key").asFeedScopedIds(List.of()).toString()); @@ -328,7 +328,7 @@ public void asFeedScopedIds() { } @Test - public void asFeedScopedIdSet() { + void asFeedScopedIdSet() { NodeAdapter subject = newNodeAdapterForTest("{ routes: ['A:23', 'B:12', 'A:23']}"); assertEquals( List.of( @@ -342,7 +342,7 @@ public void asFeedScopedIdSet() { } @Test - public void asDateOrRelativePeriod() { + void asDateOrRelativePeriod() { // Given var subject = newNodeAdapterForTest("{ 'a' : '2020-02-28', 'b' : '-P3Y' }"); var utc = ZoneIds.UTC; @@ -362,7 +362,7 @@ public void asDateOrRelativePeriod() { } @Test - public void testParsePeriodDateThrowsException() { + void testParsePeriodDateThrowsException() { // Given NodeAdapter subject = newNodeAdapterForTest("{ 'foo' : 'bar' }"); @@ -374,7 +374,7 @@ public void testParsePeriodDateThrowsException() { } @Test - public void asDuration() { + void asDuration() { NodeAdapter subject = newNodeAdapterForTest("{ k1:'PT1s', k2:'3h2m1s', k3:7 }"); // as required duration @@ -390,13 +390,13 @@ public void asDuration() { } @Test - public void requiredAsDuration() { + void requiredAsDuration() { NodeAdapter subject = newNodeAdapterForTest("{ }"); assertThrows(OtpAppException.class, () -> subject.of("missingField").asDuration()); } @Test - public void asDurations() { + void asDurations() { NodeAdapter subject = newNodeAdapterForTest("{ key1 : ['PT1s', '2h'] }"); assertEquals("[PT1S, PT2H]", subject.of("key1").asDurations(List.of()).toString()); assertEquals("[PT3H]", subject.of("missing-key").asDurations(List.of(D3h)).toString()); @@ -404,7 +404,7 @@ public void asDurations() { } @Test - public void asLocale() { + void asLocale() { NodeAdapter subject = newNodeAdapterForTest( "{ key1 : 'no', key2 : 'no_NO', key3 : 'no_NO_NY' }" ); @@ -415,14 +415,14 @@ public void asLocale() { } @Test - public void asPattern() { + void asPattern() { NodeAdapter subject = newNodeAdapterForTest("{ key : 'Ab*a' }"); assertEquals("Ab*a", subject.of("key").asPattern("ABC").toString()); assertEquals("ABC", subject.of("missingField").asPattern("ABC").toString()); } @Test - public void uri() { + void uri() { var URL = "gs://bucket/a.obj"; NodeAdapter subject = newNodeAdapterForTest("{ aUri : '" + URL + "' }"); @@ -433,14 +433,14 @@ public void uri() { } @Test - public void uriSyntaxException() { + void uriSyntaxException() { NodeAdapter subject = newNodeAdapterForTest("{ aUri : 'error$%uri' }"); assertThrows(OtpAppException.class, () -> subject.of("aUri").asUri(null), "error$%uri"); } @Test - public void uriRequiredValueMissing() { + void uriRequiredValueMissing() { NodeAdapter subject = newNodeAdapterForTest("{ }"); assertThrows( @@ -451,7 +451,7 @@ public void uriRequiredValueMissing() { } @Test - public void uris() { + void uris() { NodeAdapter subject = newNodeAdapterForTest("{ foo : ['gs://a/b', 'gs://c/d'] }"); assertEquals("[gs://a/b, gs://c/d]", subject.of("foo").asUris().toString()); @@ -461,7 +461,7 @@ public void uris() { } @Test - public void urisNotAnArrayException() { + void urisNotAnArrayException() { NodeAdapter subject = newNodeAdapterForTest("{ 'uris': 'no array' }"); assertThrows( @@ -472,7 +472,7 @@ public void urisNotAnArrayException() { } @Test - public void objectAsList() { + void objectAsList() { NodeAdapter subject = newNodeAdapterForTest("{ key : [{ a: 'I' }, { a: '2' } ] }"); List result = subject @@ -487,21 +487,21 @@ public void objectAsList() { } @Test - public void asCostLinearFunction() { + void asCostLinearFunction() { NodeAdapter subject = newNodeAdapterForTest("{ key : '400+8x' }"); assertEquals("6m40s + 8.0 t", subject.of("key").asCostLinearFunction(null).toString()); assertNull(subject.of("no-key").asCostLinearFunction(null)); } @Test - public void asTimePenalty() { + void asTimePenalty() { NodeAdapter subject = newNodeAdapterForTest("{ key : '400+8x' }"); assertEquals("6m40s + 8.0 t", subject.of("key").asTimePenalty(null).toString()); assertNull(subject.of("no-key").asTimePenalty(null)); } @Test - public void asZoneId() { + void asZoneId() { NodeAdapter subject = newNodeAdapterForTest( "{ key1 : 'UTC', key2 : 'Europe/Oslo', key3 : '+02:00', key4: 'invalid' }" ); @@ -515,7 +515,7 @@ public void asZoneId() { } @Test - public void asTextSet() { + void asTextSet() { NodeAdapter subject = newNodeAdapterForTest("{ ids : ['A', 'C', 'F'] }"); assertEquals( Set.of("A", "C", "F"), @@ -528,7 +528,7 @@ public void asTextSet() { } @Test - public void isNonEmptyArray() { + void isNonEmptyArray() { NodeAdapter subject = newNodeAdapterForTest("{ foo : ['A'], bar: [], foobar: true }"); assertTrue(subject.of("foo").asObject().isNonEmptyArray()); assertFalse(subject.of("bar").asObject().isNonEmptyArray()); @@ -538,13 +538,13 @@ public void isNonEmptyArray() { } @Test - public void deduplicateChildren() { + void deduplicateChildren() { NodeAdapter subject = newNodeAdapterForTest("{ foo : { enabled: true } }"); assertSame(subject.of("foo").asObject(), subject.of("foo").asObject()); } @Test - public void unusedParams() { + void unusedParams() { // Given: two parameters a and b NodeAdapter subject = newNodeAdapterForTest("{ foo : { a: true, b: false } }"); var buf = new StringBuilder(); @@ -557,6 +557,29 @@ public void unusedParams() { assertEquals("Unexpected config parameter: 'foo.b:false' in 'Test'", buf.toString()); } + @Test + void unknownParameters() { + // Given: two parameters a and b + var subject = newNodeAdapterForTest("{ foo : { a: true, b: false } }"); + + // When: Access ONLY parameter 'a', but not 'b' + subject.of("foo").asObject().of("a").asBoolean(); + + assertTrue(subject.hasUnknownParameters()); + } + + @Test + void allParametersAreKnown() { + // Given: two parameters a and b + var subject = newNodeAdapterForTest("{ foo : { a: true, b: false } }"); + + var object = subject.of("foo").asObject(); + object.of("a").asBoolean(); + object.of("b").asBoolean(); + + assertFalse(subject.hasUnknownParameters()); + } + private static String unusedParams(NodeAdapter subject) { var buf = new StringBuilder(); subject.logAllWarnings(m -> buf.append('\n').append(m)); 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 d50c8a74dfc..4a0a0d1b848 100644 --- a/src/test/java/org/opentripplanner/street/model/_data/StreetModelForTest.java +++ b/src/test/java/org/opentripplanner/street/model/_data/StreetModelForTest.java @@ -9,6 +9,7 @@ import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.service.vehiclerental.model.TestFreeFloatingRentalVehicleBuilder; import org.opentripplanner.service.vehiclerental.street.VehicleRentalPlaceVertex; import org.opentripplanner.street.model.RentalFormFactor; @@ -29,16 +30,16 @@ public class StreetModelForTest { public static StreetVertex V4 = intersectionVertex("V4", 3, 3); public static IntersectionVertex intersectionVertex(Coordinate c) { - return intersectionVertex(c.x, c.y); + return intersectionVertex(c.y, c.x); } public static IntersectionVertex intersectionVertex(double lat, double lon) { var label = "%s_%s".formatted(lat, lon); - return new LabelledIntersectionVertex(label, lat, lon, false, false); + return new LabelledIntersectionVertex(label, lon, lat, false, false); } public static IntersectionVertex intersectionVertex(String label, double lat, double lon) { - return new LabelledIntersectionVertex(label, lat, lon, false, false); + return new LabelledIntersectionVertex(label, lon, lat, false, false); } @Nonnull @@ -111,4 +112,8 @@ public static VehicleRentalPlaceVertex rentalVertex(RentalFormFactor formFactor) } return new VehicleRentalPlaceVertex(rentalVehicleBuilder.build()); } + + public static VehicleParking.VehicleParkingBuilder vehicleParking() { + return VehicleParking.builder().id(id("vehicle-parking-1")).coordinate(WgsCoordinate.GREENWICH); + } } diff --git a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java index 318f133f15e..7de7b0c7ce6 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java @@ -46,7 +46,7 @@ public class StreetEdgeTest { public void before() { v0 = intersectionVertex("maple_0th", 0.0, 0.0); // label, X, Y v1 = intersectionVertex("maple_1st", 2.0, 2.0); - v2 = intersectionVertex("maple_2nd", 1.0, 2.0); + v2 = intersectionVertex("maple_2nd", 2.0, 1.0); this.proto = StreetSearchRequest @@ -73,7 +73,7 @@ public void testInAndOutAngles() { assertEquals(90, e1.getOutAngle()); // 2 new ones - StreetVertex u = intersectionVertex("test1", 2.0, 1.0); + StreetVertex u = intersectionVertex("test1", 1.0, 2.0); StreetVertex v = intersectionVertex("test2", 2.0, 2.0); // Second edge, heading straight North diff --git a/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingEdgeTest.java b/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingEdgeTest.java index 7dad61fbd6b..81bee23ad54 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingEdgeTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingEdgeTest.java @@ -10,6 +10,7 @@ import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.street.search.request.StreetSearchRequest; import org.opentripplanner.street.search.state.State; @@ -130,8 +131,8 @@ private VehicleParking createVehicleParking( boolean carPlaces, VehicleParkingSpaces availability ) { - return VehicleParking - .builder() + return StreetModelForTest + .vehicleParking() .id(TransitModelForTest.id("VehicleParking")) .bicyclePlaces(bicyclePlaces) .carPlaces(carPlaces) diff --git a/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingPreferredTagsTest.java b/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingPreferredTagsTest.java index 5969121b6d6..579ae4e964d 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingPreferredTagsTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingPreferredTagsTest.java @@ -11,9 +11,9 @@ import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.routing.api.request.StreetMode; -import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingEntrance; import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.search.request.StreetSearchRequest; @@ -62,9 +62,8 @@ private void runTest( double expectedCost, boolean arriveBy ) { - var parking = VehicleParking - .builder() - .coordinate(COORDINATE) + var parking = StreetModelForTest + .vehicleParking() .tags(parkingTags) .availability(VehicleParkingSpaces.builder().bicycleSpaces(100).build()) .bicyclePlaces(true) diff --git a/src/test/java/org/opentripplanner/transit/speed_test/support/AssertSpeedTestSetup.java b/src/test/java/org/opentripplanner/transit/speed_test/support/AssertSpeedTestSetup.java index 031df343a9a..4113d5979b1 100644 --- a/src/test/java/org/opentripplanner/transit/speed_test/support/AssertSpeedTestSetup.java +++ b/src/test/java/org/opentripplanner/transit/speed_test/support/AssertSpeedTestSetup.java @@ -15,7 +15,7 @@ public static void assertTestDateHasData( ) { int numberOfPatternForTestDate = transitModel .getTransitLayer() - .getTripPatternsForDate(config.testDate) + .getTripPatternsForRunningDate(config.testDate) .size(); if (numberOfPatternForTestDate < 10) { diff --git a/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java b/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java index 2bcb49defae..a31b5cfb387 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java @@ -16,6 +16,7 @@ import org.opentripplanner.routing.vehicle_parking.VehicleParkingState; import org.opentripplanner.routing.vehicle_parking.VehicleParkingTestGraphData; import org.opentripplanner.routing.vehicle_parking.VehicleParkingTestUtil; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.edge.StreetVehicleParkingLink; import org.opentripplanner.street.model.edge.VehicleParkingEdge; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; @@ -142,7 +143,10 @@ void deleteVehicleParkingTest() { @Test void addNotOperatingVehicleParkingTest() { - var vehicleParking = VehicleParking.builder().state(VehicleParkingState.CLOSED).build(); + var vehicleParking = StreetModelForTest + .vehicleParking() + .state(VehicleParkingState.CLOSED) + .build(); when(dataSource.getUpdates()).thenReturn(List.of(vehicleParking)); runUpdaterOnce(); @@ -155,8 +159,8 @@ void addNotOperatingVehicleParkingTest() { void updateNotOperatingVehicleParkingTest() { var vehiclePlaces = VehicleParkingSpaces.builder().bicycleSpaces(1).build(); - var vehicleParking = VehicleParking - .builder() + var vehicleParking = StreetModelForTest + .vehicleParking() .availability(vehiclePlaces) .state(VehicleParkingState.CLOSED) .build(); @@ -175,8 +179,8 @@ void updateNotOperatingVehicleParkingTest() { vehiclePlaces = VehicleParkingSpaces.builder().bicycleSpaces(2).build(); vehicleParking = - VehicleParking - .builder() + StreetModelForTest + .vehicleParking() .availability(vehiclePlaces) .state(VehicleParkingState.CLOSED) .build(); @@ -194,7 +198,10 @@ void updateNotOperatingVehicleParkingTest() { @Test void deleteNotOperatingVehicleParkingTest() { - var vehicleParking = VehicleParking.builder().state(VehicleParkingState.CLOSED).build(); + var vehicleParking = StreetModelForTest + .vehicleParking() + .state(VehicleParkingState.CLOSED) + .build(); when(dataSource.getUpdates()).thenReturn(List.of(vehicleParking)); runUpdaterOnce(); diff --git a/src/test/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdaterTest.java b/src/test/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdaterTest.java index 09dd1cf1cb9..99910e04f7c 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdaterTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdaterTest.java @@ -8,10 +8,10 @@ import java.util.List; import org.junit.jupiter.api.Test; -import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.MultiPolygon; import org.locationtech.jts.geom.Polygon; +import org.opentripplanner._support.geometry.Polygons; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.service.vehiclerental.model.GeofencingZone; import org.opentripplanner.service.vehiclerental.street.BusinessAreaBorder; @@ -35,30 +35,16 @@ class GeofencingVertexUpdaterTest { final GeofencingVertexUpdater updater = new GeofencingVertexUpdater(ignored -> List.of(insideFrognerPark, halfInHalfOutFrognerPark, businessBorder) ); - StreetEdge outsideFrognerPark = streetEdge(outsideFrognerPark1, outsideFrognerPark2); - - GeometryFactory fac = GeometryUtils.getGeometryFactory(); - Polygon frognerPark = fac.createPolygon( - new Coordinate[] { - new Coordinate(59.93112978539807, 10.691099320272173), - new Coordinate(59.92231848097069, 10.691099320272173), - new Coordinate(59.92231848097069, 10.711758464910503), - new Coordinate(59.92231848097069, 10.691099320272173), - new Coordinate(59.93112978539807, 10.691099320272173), - } - ); - final GeofencingZone zone = new GeofencingZone(id("frogner-park"), frognerPark, true, false); - Polygon osloPolygon = fac.createPolygon( - new Coordinate[] { - new Coordinate(59.961055202323195, 10.62535658370308), - new Coordinate(59.889009435700416, 10.62535658370308), - new Coordinate(59.889009435700416, 10.849791142928694), - new Coordinate(59.961055202323195, 10.849791142928694), - new Coordinate(59.961055202323195, 10.62535658370308), - } + + static GeometryFactory fac = GeometryUtils.getGeometryFactory(); + final GeofencingZone zone = new GeofencingZone( + id("frogner-park"), + Polygons.OSLO_FROGNER_PARK, + true, + false ); - MultiPolygon osloMultiPolygon = fac.createMultiPolygon(new Polygon[] { osloPolygon }); + MultiPolygon osloMultiPolygon = fac.createMultiPolygon(new Polygon[] { Polygons.OSLO }); final GeofencingZone businessArea = new GeofencingZone( id("oslo"), osloMultiPolygon, diff --git a/src/test/resources/standalone/config/router-config.json b/src/test/resources/standalone/config/router-config.json index 3a07cddfeb8..3b43ef1c5d2 100644 --- a/src/test/resources/standalone/config/router-config.json +++ b/src/test/resources/standalone/config/router-config.json @@ -334,6 +334,12 @@ "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", @@ -414,6 +420,12 @@ "fromDateTime": "-P1D", "timeout": 300000 } + }, + { + "type": "vehicle-parking", + "feedId": "bikeep", + "sourceType": "bikeep", + "url": "https://services.bikeep.com/location/v1/public-areas/no-baia-mobility/locations" } ], "rideHailingServices": [
- * bicycle is denied if bicycle:no, bicycle:dismount, bicycle:license or bicycle:use_sidepath + * bicycle is denied if bicycle:no, bicycle:dismount or bicycle:license. */ public boolean isBicycleExplicitlyDenied() { - return ( - isTagDeniedAccess("bicycle") || - "dismount".equals(getTag("bicycle")) || - "use_sidepath".equals(getTag("bicycle")) - ); + return (isTagDeniedAccess("bicycle") || "dismount".equals(getTag("bicycle"))); } /** diff --git a/src/main/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapper.java b/src/main/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapper.java index 1463313203d..9989c102030 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapper.java +++ b/src/main/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapper.java @@ -624,6 +624,9 @@ public void populateProperties(WayPropertySet props) { props.setMixinProperties("foot=discouraged", ofWalkSafety(3)); props.setMixinProperties("bicycle=discouraged", ofBicycleSafety(3)); + props.setMixinProperties("foot=use_sidepath", ofWalkSafety(5)); + props.setMixinProperties("bicycle=use_sidepath", ofBicycleSafety(5)); + populateNotesAndNames(props); // slope overrides diff --git a/src/main/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapper.java b/src/main/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapper.java index 9bafe133f2a..5ea949e8d5c 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapper.java +++ b/src/main/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapper.java @@ -1,5 +1,6 @@ package org.opentripplanner.openstreetmap.tagmapping; +import static org.opentripplanner.openstreetmap.wayproperty.MixinPropertiesBuilder.ofWalkSafety; import static org.opentripplanner.openstreetmap.wayproperty.WayPropertiesBuilder.withModes; import static org.opentripplanner.street.model.StreetTraversalPermission.ALL; import static org.opentripplanner.street.model.StreetTraversalPermission.CAR; @@ -163,6 +164,10 @@ else if (speedLimit <= 16.65f) { props.setProperties("highway=service;tunnel=yes;access=destination", withModes(NONE)); props.setProperties("highway=service;access=destination", withModes(ALL).bicycleSafety(1.1)); + // Typically if this tag is used on a way, there is also a better option for walking. + // We don't need to set bicycle safety as cycling is not currently allowed on these ways. + props.setMixinProperties("bicycle=use_sidepath", ofWalkSafety(5)); + // Automobile speeds in Finland. // General speed limit is 80kph unless signs says otherwise. props.defaultCarSpeed = 22.22f; diff --git a/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java b/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java index 107d41208f4..3dbff516347 100644 --- a/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java +++ b/src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java @@ -140,7 +140,13 @@ private int bestNumOfTransfers(int stop) { } private int bestGeneralizedCost(int stop) { - return costCalculator.calculateMinCost(bestTravelDuration(stop), bestNumOfTransfers(stop)); + return ( + costCalculator.calculateRemainingMinCost( + bestTravelDuration(stop), + bestNumOfTransfers(stop), + stop + ) + ); } /** diff --git a/src/main/java/org/opentripplanner/raptor/spi/RaptorCostCalculator.java b/src/main/java/org/opentripplanner/raptor/spi/RaptorCostCalculator.java index d575a0e4d8c..7a9928e51aa 100644 --- a/src/main/java/org/opentripplanner/raptor/spi/RaptorCostCalculator.java +++ b/src/main/java/org/opentripplanner/raptor/spi/RaptorCostCalculator.java @@ -52,12 +52,12 @@ int boardingCost( /** * Used for estimating the remaining value for a criteria at a given stop arrival. The calculated - * value should be a an optimistic estimate for the heuristics to work properly. So, to calculate - * the generalized cost for given the {@code minTravelTime} and {@code minNumTransfers} retuning + * value should be an optimistic estimate for the heuristics to work properly. So, to calculate + * the generalized cost for given the {@code minTravelTime} and {@code minNumTransfers} returning * the greatest value, which is guaranteed to be less than the - * real value would be correct and a good choose. + * real value would be correct and a good choice. */ - int calculateMinCost(int minTravelTime, int minNumTransfers); + int calculateRemainingMinCost(int minTravelTime, int minNumTransfers, int fromStop); /** * This method allows the cost calculator to add cost in addition to the generalized-cost of the diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayer.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayer.java index 60cfb72ef7d..288f429a861 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayer.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayer.java @@ -22,6 +22,8 @@ public class TransitLayer { /** * Transit data required for routing, indexed by each local date(Graph TimeZone) it runs through. * A Trip "runs through" a date if any of its arrivals or departures is happening on that date. + * The same trip pattern can therefore have multiple running dates and trip pattern is not + * required to "run" on its service date. */ private final HashMap> tripPatternsRunningOnDate; @@ -94,7 +96,12 @@ public StopLocation getStopByIndex(int stop) { return stop == -1 ? null : this.stopModel.stopByIndex(stop); } - public Collection getTripPatternsForDate(LocalDate date) { + /** + * Returns trip patterns for the given running date. Running date is not necessarily the same + * as the service date. A Trip "runs through" a date if any of its arrivals or departures is + * happening on that date. Trip pattern can have multiple running dates. + */ + public Collection getTripPatternsForRunningDate(LocalDate date) { return tripPatternsRunningOnDate.getOrDefault(date, List.of()); } @@ -112,16 +119,29 @@ public int getStopCount() { return stopModel.stopIndexSize(); } + /** + * Returns a copy of the list of trip patterns for the given running date. Running date is not + * necessarily the same as the service date. A Trip "runs through" a date if any of its arrivals + * or departures is happening on that date. Trip pattern can have multiple running dates. + */ public List getTripPatternsRunningOnDateCopy(LocalDate runningPeriodDate) { List tripPatternForDate = tripPatternsRunningOnDate.get(runningPeriodDate); return tripPatternForDate != null ? new ArrayList<>(tripPatternForDate) : new ArrayList<>(); } - public List getTripPatternsStartingOnDateCopy(LocalDate date) { - List tripPatternsRunningOnDate = getTripPatternsRunningOnDateCopy(date); - return tripPatternsRunningOnDate + /** + * Returns a copy of the list of trip patterns for the given service date. Service date is not + * necessarily the same as any of the trip pattern's running dates. + */ + public List getTripPatternsOnServiceDateCopy(LocalDate date) { + List tripPatternsRunningOnDates = getTripPatternsRunningOnDateCopy(date); + // Trip pattern can run only after midnight. Therefore, we need to get the trip pattern's for + // the next running date as well and filter out duplicates. + tripPatternsRunningOnDates.addAll(getTripPatternsRunningOnDateCopy(date.plusDays(1))); + return tripPatternsRunningOnDates .stream() - .filter(t -> t.getLocalDate().equals(date)) + .filter(t -> t.getServiceDate().equals(date)) + .distinct() .collect(Collectors.toList()); } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TripPatternForDate.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TripPatternForDate.java index b86314aa42c..1411424dfc6 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TripPatternForDate.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TripPatternForDate.java @@ -43,16 +43,16 @@ public class TripPatternForDate implements Comparable { */ private final FrequencyEntry[] frequencies; - /** The date for which the filtering was performed. */ - private final LocalDate localDate; + /** The service date of the trip pattern. */ + private final LocalDate serviceDate; /** - * The date on which the first trip departs. + * The running date on which the first trip departs. Not necessarily the same as the service date. */ private final LocalDate startOfRunningPeriod; /** - * The date on which the last trip arrives. + * The running date on which the last trip arrives. */ private final LocalDate endOfRunningPeriod; @@ -60,19 +60,19 @@ public TripPatternForDate( RoutingTripPattern tripPattern, List tripTimes, List frequencies, - LocalDate localDate + LocalDate serviceDate ) { this.tripPattern = tripPattern; this.tripTimes = tripTimes.toArray(new TripTimes[0]); this.frequencies = frequencies.toArray(new FrequencyEntry[0]); - this.localDate = localDate; + this.serviceDate = serviceDate; // TODO: We expect a pattern only containing trips or frequencies, fix ability to merge if (hasFrequencies()) { this.startOfRunningPeriod = ServiceDateUtils .asDateTime( - localDate, + serviceDate, frequencies .stream() .mapToInt(frequencyEntry -> frequencyEntry.startTime) @@ -84,7 +84,7 @@ public TripPatternForDate( this.endOfRunningPeriod = ServiceDateUtils .asDateTime( - localDate, + serviceDate, frequencies .stream() .mapToInt(frequencyEntry -> frequencyEntry.endTime) @@ -96,11 +96,11 @@ public TripPatternForDate( // These depend on the tripTimes array being sorted var first = tripTimes.get(0); this.startOfRunningPeriod = - ServiceDateUtils.asDateTime(localDate, first.getDepartureTime(0)).toLocalDate(); + ServiceDateUtils.asDateTime(serviceDate, first.getDepartureTime(0)).toLocalDate(); var last = tripTimes.get(tripTimes.size() - 1); this.endOfRunningPeriod = ServiceDateUtils - .asDateTime(localDate, last.getArrivalTime(last.getNumStops() - 1)) + .asDateTime(serviceDate, last.getArrivalTime(last.getNumStops() - 1)) .toLocalDate(); assertValidRunningPeriod(startOfRunningPeriod, endOfRunningPeriod, first, last); } @@ -126,18 +126,31 @@ public TripTimes getTripTimes(int i) { return tripTimes[i]; } - public LocalDate getLocalDate() { - return localDate; + /** + * The service date for which the trip pattern belongs to. Not necessarily the same as the start + * of the running period in cases where the trip pattern only runs after midnight. + */ + public LocalDate getServiceDate() { + return serviceDate; } public int numberOfTripSchedules() { return tripTimes.length; } + /** + * The start of the running period. This is determined by the first departure time for this + * pattern. Not necessarily the same as the service date if the pattern runs after midnight. + */ public LocalDate getStartOfRunningPeriod() { return startOfRunningPeriod; } + /** + * Returns the running dates. A Trip "runs through" a date if any of its arrivals or departures is + * happening on that date. The same trip pattern can therefore have multiple running dates and + * trip pattern is not required to "run" on its service date. + */ public List getRunningPeriodDates() { // Add one day to ensure last day is included return startOfRunningPeriod @@ -151,14 +164,14 @@ public boolean hasFrequencies() { @Override public int compareTo(TripPatternForDate other) { - return localDate.compareTo(other.localDate); + return serviceDate.compareTo(other.serviceDate); } @Override public int hashCode() { return Objects.hash( tripPattern, - localDate, + serviceDate, Arrays.hashCode(tripTimes), Arrays.hashCode(frequencies) ); @@ -176,7 +189,7 @@ public boolean equals(Object o) { return ( tripPattern.equals(that.tripPattern) && - localDate.equals(that.localDate) && + serviceDate.equals(that.serviceDate) && Arrays.equals(tripTimes, that.tripTimes) && Arrays.equals(frequencies, that.frequencies) ); @@ -184,7 +197,9 @@ public boolean equals(Object o) { @Override public String toString() { - return "TripPatternForDate{" + "tripPattern=" + tripPattern + ", localDate=" + localDate + '}'; + return ( + "TripPatternForDate{" + "tripPattern=" + tripPattern + ", serviceDate=" + serviceDate + '}' + ); } @Nullable @@ -214,7 +229,7 @@ public TripPatternForDate newWithFilteredTripTimes(Predicate filter) return this; } - return new TripPatternForDate(tripPattern, filteredTripTimes, filteredFrequencies, localDate); + return new TripPatternForDate(tripPattern, filteredTripTimes, filteredFrequencies, serviceDate); } private static void assertValidRunningPeriod( diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java index 26a1286d10d..ee2f1282625 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java @@ -121,14 +121,21 @@ public int waitCost(int waitTimeInSeconds) { } @Override - public int calculateMinCost(int minTravelTime, int minNumTransfers) { - return ( - boardCostOnly + - boardAndTransferCost * - minNumTransfers + - transitFactors.minFactor() * - minTravelTime - ); + public int calculateRemainingMinCost(int minTravelTime, int minNumTransfers, int fromStop) { + if (minNumTransfers > -1) { + return ( + boardCostOnly + + boardAndTransferCost * + minNumTransfers + + transitFactors.minFactor() * + minTravelTime + ); + } else { + // Remove cost that was added during alighting similar as we do in the costEgress() method + int fixedCost = transitFactors.minFactor() * minTravelTime; + + return stopTransferCost == null ? fixedCost : fixedCost - stopTransferCost[fromStop]; + } } @Override 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 ecd0704bbc2..734355f0ac5 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 @@ -77,8 +77,8 @@ public int waitCost(int waitTimeInSeconds) { } @Override - public int calculateMinCost(int minTravelTime, int minNumTransfers) { - return delegate.calculateMinCost(minTravelTime, minNumTransfers); + public int calculateRemainingMinCost(int minTravelTime, int minNumTransfers, int fromStop) { + return delegate.calculateRemainingMinCost(minTravelTime, minNumTransfers, fromStop); } @Override 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 78014bd245d..d0697f78692 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 @@ -66,8 +66,8 @@ public int waitCost(int waitTimeInSeconds) { } @Override - public int calculateMinCost(int minTravelTime, int minNumTransfers) { - return delegate.calculateMinCost(minTravelTime, minNumTransfers); + public int calculateRemainingMinCost(int minTravelTime, int minNumTransfers, int fromStop) { + return delegate.calculateRemainingMinCost(minTravelTime, minNumTransfers, fromStop); } @Override diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/TripFrequencyAlightSearch.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/TripFrequencyAlightSearch.java index 451f51b2aa5..2f020e22cf5 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/TripFrequencyAlightSearch.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/TripFrequencyAlightSearch.java @@ -55,7 +55,7 @@ public RaptorBoardOrAlightEvent search( arrivalTime + headway, headway, offset, - pattern.getLocalDate() + pattern.getServiceDate() ); } } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/TripFrequencyBoardSearch.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/TripFrequencyBoardSearch.java index 897ce370a93..ea58e870547 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/TripFrequencyBoardSearch.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/frequency/TripFrequencyBoardSearch.java @@ -54,7 +54,7 @@ public RaptorBoardOrAlightEvent search( departureTime - headway, headway, offset, - pattern.getLocalDate() + pattern.getServiceDate() ); } } diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerUpdater.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerUpdater.java index bed27497587..fad5de83de0 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerUpdater.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/mappers/TransitLayerUpdater.java @@ -96,7 +96,7 @@ public void update( if (!tripPatternsStartingOnDateMapCache.containsKey(date)) { Map map = realtimeTransitLayer - .getTripPatternsStartingOnDateCopy(date) + .getTripPatternsOnServiceDateCopy(date) .stream() .collect(Collectors.toMap(t -> t.getTripPattern().getPattern(), t -> t)); tripPatternsStartingOnDateMapCache.put(date, map); @@ -146,7 +146,7 @@ public void update( } else { LOG.debug( "NEW TripPatternForDate: {} - {}", - newTripPatternForDate.getLocalDate(), + newTripPatternForDate.getServiceDate(), newTripPatternForDate.getTripPattern().debugInfo() ); } @@ -179,7 +179,7 @@ public void update( } for (TripPatternForDate tripPatternForDate : previouslyUsedPatterns) { - if (tripPatternForDate.getLocalDate().equals(date)) { + if (tripPatternForDate.getServiceDate().equals(date)) { TripPattern pattern = tripPatternForDate.getTripPattern().getPattern(); if (!pattern.isCreatedByRealtimeUpdater()) { continue; diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java index 863a4ca9ae8..f987e4f7a21 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/RaptorRoutingRequestTransitDataCreator.java @@ -120,7 +120,7 @@ static List merge( // Calculate offsets per date int[] offsets = new int[patternsSorted.length]; for (int i = 0; i < patternsSorted.length; i++) { - LocalDate serviceDate = patternsSorted[i].getLocalDate(); + LocalDate serviceDate = patternsSorted[i].getServiceDate(); if (offsetCache.containsKey(serviceDate)) { offsets[i] = offsetCache.get(serviceDate); } else { @@ -185,7 +185,9 @@ private static List filterActiveTripPatterns( filter.tripTimesPredicate(tripTimes, filter.hasSubModeFilters()); Predicate tripTimesWithoutSubmodesPredicate = tripTimes -> filter.tripTimesPredicate(tripTimes, false); - Collection tripPatternsForDate = transitLayer.getTripPatternsForDate(date); + Collection tripPatternsForDate = transitLayer.getTripPatternsForRunningDate( + date + ); List result = new ArrayList<>(tripPatternsForDate.size()); for (TripPatternForDate p : tripPatternsForDate) { if (firstDay || p.getStartOfRunningPeriod().equals(date)) { diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleWithOffset.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleWithOffset.java index 642dbd2d6e5..aca998b24ca 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleWithOffset.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/request/TripScheduleWithOffset.java @@ -124,7 +124,7 @@ private void findTripTimes() { if (index < numSchedules) { this.tripTimes = tripPatternForDate.getTripTimes(index); - this.serviceDate = tripPatternForDate.getLocalDate(); + this.serviceDate = tripPatternForDate.getServiceDate(); this.secondsOffset = pattern.tripPatternForDateOffsets(i); return; } diff --git a/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParking.java b/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParking.java index e5b0fd083dc..6ebd34e1287 100644 --- a/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParking.java +++ b/src/main/java/org/opentripplanner/routing/vehicle_parking/VehicleParking.java @@ -120,9 +120,10 @@ public class VehicleParking implements Serializable { VehicleParkingSpaces availability, VehicleParkingGroup vehicleParkingGroup ) { - this.id = id; + this.id = + Objects.requireNonNull(id, "%s must have an ID".formatted(this.getClass().getSimpleName())); this.name = name; - this.coordinate = coordinate; + this.coordinate = Objects.requireNonNull(coordinate); this.detailsUrl = detailsUrl; this.imageUrl = imageUrl; this.tags = tags; diff --git a/src/main/java/org/opentripplanner/standalone/OTPMain.java b/src/main/java/org/opentripplanner/standalone/OTPMain.java index 51172d5d90a..d865f8fa28c 100644 --- a/src/main/java/org/opentripplanner/standalone/OTPMain.java +++ b/src/main/java/org/opentripplanner/standalone/OTPMain.java @@ -13,6 +13,7 @@ import org.opentripplanner.raptor.configure.RaptorConfig; import org.opentripplanner.routing.graph.SerializedGraphObject; import org.opentripplanner.standalone.config.CommandLineParameters; +import org.opentripplanner.standalone.config.ConfigModel; import org.opentripplanner.standalone.configure.ConstructApplication; import org.opentripplanner.standalone.configure.LoadApplication; import org.opentripplanner.standalone.server.GrizzlyServer; @@ -113,6 +114,8 @@ private static void startOTPServer(CommandLineParameters cli) { var loadApp = new LoadApplication(cli); var config = loadApp.config(); + detectUnusedConfigParams(cli, config); + // Validate data sources, command line arguments and config before loading and // processing input data to fail early loadApp.validateConfigAndDataSources(); @@ -173,6 +176,15 @@ private static void startOTPServer(CommandLineParameters cli) { } } + /** + * Optionally, check if the config is valid and if not abort the startup process. + */ + private static void detectUnusedConfigParams(CommandLineParameters cli, ConfigModel config) { + if (cli.abortOnUnknownConfig) { + config.abortOnUnknownParameters(); + } + } + private static void startOtpWebServer(CommandLineParameters params, ConstructApplication app) { // Index graph for travel search app.transitModel().index(); diff --git a/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java b/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java index a542c330afe..a3c6f313799 100644 --- a/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java @@ -726,4 +726,11 @@ public int getSubwayAccessTimeSeconds() { public NodeAdapter asNodeAdapter() { return root; } + + /** + * Checks if any unknown or invalid parameters were encountered while loading the configuration. + */ + public boolean hasUnknownParameters() { + return root.hasUnknownParameters(); + } } diff --git a/src/main/java/org/opentripplanner/standalone/config/CommandLineParameters.java b/src/main/java/org/opentripplanner/standalone/config/CommandLineParameters.java index fb26db4877d..3513ec252ea 100644 --- a/src/main/java/org/opentripplanner/standalone/config/CommandLineParameters.java +++ b/src/main/java/org/opentripplanner/standalone/config/CommandLineParameters.java @@ -133,6 +133,12 @@ public class CommandLineParameters { ) public boolean visualize; + @Parameter( + names = { "--abortOnUnknownConfig" }, + description = "Abort the startup if configuration files are found to contain unknown parameters." + ) + public boolean abortOnUnknownConfig = false; + /** * The remaining single parameter after the switches is the directory with the configuration * files. This directory may contain other files like the graph, input data and report files. diff --git a/src/main/java/org/opentripplanner/standalone/config/ConfigModel.java b/src/main/java/org/opentripplanner/standalone/config/ConfigModel.java index 48966aefc7b..c82dd33ddbf 100644 --- a/src/main/java/org/opentripplanner/standalone/config/ConfigModel.java +++ b/src/main/java/org/opentripplanner/standalone/config/ConfigModel.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.node.MissingNode; import org.opentripplanner.framework.application.OTPFeature; +import org.opentripplanner.framework.application.OtpAppException; import org.opentripplanner.framework.application.OtpFileNames; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -105,4 +106,24 @@ public static void initializeOtpFeatures(OtpConfig otpConfig) { OTPFeature.enableFeatures(otpConfig.otpFeatures); OTPFeature.logFeatureSetup(); } + + /** + * Checks if any unknown or invalid parameters were encountered while loading the configuration. + * + * If so it throws an exception. + */ + public void abortOnUnknownParameters() { + if ( + ( + otpConfig.hasUnknownParameters() || + buildConfig.hasUnknownParameters() || + routerConfig.hasUnknownParameters() + ) + ) { + throw new OtpAppException( + "Configuration contains unknown parameters (see above for details). " + + "Please fix your configuration or remove --abortOnUnknownConfig from your OTP CLI parameters." + ); + } + } } diff --git a/src/main/java/org/opentripplanner/standalone/config/OtpConfig.java b/src/main/java/org/opentripplanner/standalone/config/OtpConfig.java index 40a659df1ab..2918cbc3884 100644 --- a/src/main/java/org/opentripplanner/standalone/config/OtpConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/OtpConfig.java @@ -71,4 +71,11 @@ public OtpConfig(NodeAdapter nodeAdapter, boolean logUnusedParams) { root.logAllWarnings(LOG::warn); } } + + /** + * Checks if any unknown or invalid parameters were encountered while loading the configuration. + */ + public boolean hasUnknownParameters() { + return root.hasUnknownParameters(); + } } diff --git a/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java b/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java index bf97155b747..4ac0688a759 100644 --- a/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java @@ -150,4 +150,11 @@ public String toString() { // Print ONLY the values set, not default values return root.toPrettyString(); } + + /** + * Checks if any unknown or invalid parameters were encountered while loading the configuration. + */ + public boolean hasUnknownParameters() { + return root.hasUnknownParameters(); + } } 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 69d9937582b..51303ea4fc5 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 @@ -172,6 +172,13 @@ public void logAllWarnings(Consumer logger) { allWarnings().forEach(logger); } + /** + * Checks if any unknown or invalid properties were encountered while loading the configuration. + */ + public boolean hasUnknownParameters() { + return !unusedParams().isEmpty(); + } + /** * Be careful when using this method - this bypasses the NodeAdaptor, and we loose * track of unused parameters and cannot generate documentation for the children. 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 6caf9082cff..70b8e261ee4 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 @@ -10,7 +10,8 @@ public enum OtpVersion { V2_2("2.2"), V2_3("2.3"), V2_4("2.4"), - V2_5("2.5"); + V2_5("2.5"), + V2_6("2.6"); private final String text; diff --git a/src/main/java/org/opentripplanner/standalone/config/framework/json/ParameterBuilder.java b/src/main/java/org/opentripplanner/standalone/config/framework/json/ParameterBuilder.java index 088a7794585..3ecc4d484cb 100644 --- a/src/main/java/org/opentripplanner/standalone/config/framework/json/ParameterBuilder.java +++ b/src/main/java/org/opentripplanner/standalone/config/framework/json/ParameterBuilder.java @@ -120,10 +120,6 @@ public Boolean asBoolean(boolean defaultValue) { return ofOptional(BOOLEAN, defaultValue, JsonNode::asBoolean); } - public Map asBooleanMap() { - return ofOptionalMap(BOOLEAN, JsonNode::asBoolean); - } - /** @throws OtpAppException if parameter is missing. */ public double asDouble() { return ofRequired(DOUBLE).asDouble(); 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 e808ad6905c..c687899009f 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 @@ -2,13 +2,16 @@ import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_2; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_3; +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_6; import java.time.Duration; import java.time.ZoneId; import java.util.ArrayList; import java.util.Set; +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.standalone.config.framework.json.NodeAdapter; import org.opentripplanner.updater.vehicle_parking.VehicleParkingSourceType; @@ -25,7 +28,7 @@ public static VehicleParkingUpdaterParameters create(String updaterRef, NodeAdap var feedId = c .of("feedId") .since(V2_2) - .summary("The name of the data source.") + .summary("The id of the data source, which will be the prefix of the parking lot's id.") .description("This will end up in the API responses as the feed id of of the parking lot.") .asString(); return switch (sourceType) { @@ -50,7 +53,7 @@ public static VehicleParkingUpdaterParameters create(String updaterRef, NodeAdap ); case PARK_API, BICYCLE_PARK_API -> new ParkAPIUpdaterParameters( updaterRef, - c.of("url").since(V2_2).summary("URL of the resource.").asString(null), + c.of("url").since(V2_2).summary("URL of the resource.").asString(), feedId, c .of("frequency") @@ -66,7 +69,7 @@ public static VehicleParkingUpdaterParameters create(String updaterRef, NodeAdap ); case BIKELY -> new BikelyUpdaterParameters( updaterRef, - c.of("url").since(V2_3).summary("URL of the locations endpoint.").asUri(null), + c.of("url").since(V2_3).summary("URL of the locations endpoint.").asUri(), feedId, c .of("frequency") @@ -75,6 +78,28 @@ 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(), + feedId, + c + .of("frequency") + .since(V2_6) + .summary("How often to update the source.") + .asDuration(Duration.ofMinutes(1)), + HttpHeadersConfig.headers(c, V2_6) + ); }; } 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 e44994d1105..35278d8fbd8 100644 --- a/src/main/java/org/opentripplanner/transit/model/basic/Money.java +++ b/src/main/java/org/opentripplanner/transit/model/basic/Money.java @@ -206,4 +206,9 @@ public boolean equals(Object obj) { return false; } } + + @Override + public int hashCode() { + return Objects.hash(currency, amount); + } } 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 e0214fd9d57..a956fda0d87 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingDataSourceFactory.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingDataSourceFactory.java @@ -1,9 +1,13 @@ package org.opentripplanner.updater.vehicle_parking; +import org.opentripplanner.ext.vehicleparking.bikeep.BikeepUpdater; +import org.opentripplanner.ext.vehicleparking.bikeep.BikeepUpdaterParameters; import org.opentripplanner.ext.vehicleparking.bikely.BikelyUpdater; 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; @@ -36,6 +40,8 @@ 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); }; } } 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 583bc9afc99..f6a28177d8e 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingSourceType.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingSourceType.java @@ -5,4 +5,6 @@ public enum VehicleParkingSourceType { BICYCLE_PARK_API, HSL_PARK, BIKELY, + NOI_OPEN_DATA_HUB, + BIKEEP, } diff --git a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java index 3d2e9b21e29..185de6bddb7 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java @@ -8,6 +8,7 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; +import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.linking.DisposableEdgeCollection; import org.opentripplanner.routing.linking.LinkingDirection; @@ -68,7 +69,7 @@ public void setGraphUpdaterManager(WriteToGraphCallback saveResultOnGraph) { } @Override - protected void runPolling() throws Exception { + protected void runPolling() { LOG.debug("Updating vehicle parkings from {}", source); if (!source.update()) { LOG.debug("No updates"); @@ -239,4 +240,9 @@ private void removeVehicleParkingEdgesFromGraph( graph.remove(entranceVertex); } } + + @Override + public String toString() { + return ToStringBuilder.of(this.getClass()).addObj("source", source).toString(); + } } diff --git a/src/test/java/org/opentripplanner/_support/geometry/Coordinates.java b/src/test/java/org/opentripplanner/_support/geometry/Coordinates.java index 3e5bddd9f94..5a4526012c9 100644 --- a/src/test/java/org/opentripplanner/_support/geometry/Coordinates.java +++ b/src/test/java/org/opentripplanner/_support/geometry/Coordinates.java @@ -4,9 +4,18 @@ public class Coordinates { - public static final Coordinate BERLIN = new Coordinate(52.5212, 13.4105); - public static final Coordinate BERLIN_BRANDENBURG_GATE = new Coordinate(52.51627, 13.37770); - public static final Coordinate HAMBURG = new Coordinate(53.5566, 10.0003); - public static final Coordinate KONGSBERG_PLATFORM_1 = new Coordinate(59.67216, 9.65107); - public static final Coordinate BOSTON = new Coordinate(42.36541, -71.06129); + public static final Coordinate BERLIN = of(52.5212, 13.4105); + public static final Coordinate BERLIN_BRANDENBURG_GATE = of(52.51627, 13.37770); + public static final Coordinate HAMBURG = of(53.5566, 10.0003); + public static final Coordinate KONGSBERG_PLATFORM_1 = of(59.67216, 9.65107); + public static final Coordinate BOSTON = of(42.36541, -71.06129); + + /** + * Because it is a frequent mistake to swap x/y and longitude/latitude when + * constructing JTS Coordinates, this static factory method makes is clear + * which is which. + */ + public static Coordinate of(double latitude, double longitude) { + return new Coordinate(longitude, latitude); + } } diff --git a/src/test/java/org/opentripplanner/_support/geometry/Polygons.java b/src/test/java/org/opentripplanner/_support/geometry/Polygons.java index efedd74499c..a386d8a27e1 100644 --- a/src/test/java/org/opentripplanner/_support/geometry/Polygons.java +++ b/src/test/java/org/opentripplanner/_support/geometry/Polygons.java @@ -3,29 +3,65 @@ import java.util.Arrays; import org.geojson.LngLatAlt; import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.Polygon; import org.opentripplanner.framework.geometry.GeometryUtils; public class Polygons { - public static final Polygon BERLIN = GeometryUtils - .getGeometryFactory() - .createPolygon( - new Coordinate[] { - new Coordinate(52.616841, 13.224692810), - new Coordinate(52.6168419, 13.224692810), - new Coordinate(52.3915238, 13.646107734), - new Coordinate(52.616841, 13.646107734), - new Coordinate(52.616841, 13.224692810), - } - ); + private static final GeometryFactory FAC = GeometryUtils.getGeometryFactory(); + public static final Polygon BERLIN = FAC.createPolygon( + new Coordinate[] { + Coordinates.of(52.616841, 13.224692810), + Coordinates.of(52.616841, 13.646107734), + Coordinates.of(52.3915238, 13.646107734), + Coordinates.of(52.396421, 13.2268067), + Coordinates.of(52.616841, 13.224692810), + } + ); + + public static Polygon OSLO = FAC.createPolygon( + new Coordinate[] { + Coordinates.of(59.961055202323195, 10.62535658370308), + Coordinates.of(59.889009435700416, 10.62535658370308), + Coordinates.of(59.889009435700416, 10.849791142928694), + Coordinates.of(59.961055202323195, 10.849791142928694), + Coordinates.of(59.961055202323195, 10.62535658370308), + } + ); + public static Polygon OSLO_FROGNER_PARK = FAC.createPolygon( + new Coordinate[] { + Coordinates.of(59.92939032560119, 10.69770054003061), + Coordinates.of(59.929138466684975, 10.695210909925208), + Coordinates.of(59.92745319808358, 10.692696865071184), + Coordinates.of(59.92709930323093, 10.693774304996225), + Coordinates.of(59.92745914988427, 10.69495957972947), + Coordinates.of(59.92736919590291, 10.697664535925895), + Coordinates.of(59.924837887427856, 10.697927604125255), + Coordinates.of(59.924447953413335, 10.697448767354985), + Coordinates.of(59.92378800804022, 10.697819761729818), + Coordinates.of(59.92329018587293, 10.699196446969069), + Coordinates.of(59.92347619027632, 10.700285749621997), + Coordinates.of(59.92272030268688, 10.704714696822037), + Coordinates.of(59.92597766029715, 10.71001707489603), + Coordinates.of(59.92676341291536, 10.707838597058043), + Coordinates.of(59.92790300889098, 10.708389137506913), + Coordinates.of(59.928376832499424, 10.707060536853078), + Coordinates.of(59.92831087551576, 10.705803789539402), + Coordinates.of(59.92953431964068, 10.706641515204467), + Coordinates.of(59.93046383654274, 10.70484606360543), + Coordinates.of(59.93008590667682, 10.701817874860211), + Coordinates.of(59.93028982601595, 10.700525251174469), + Coordinates.of(59.92939032560119, 10.69770054003061), + } + ); public static org.geojson.Polygon toGeoJson(Polygon polygon) { var ret = new org.geojson.Polygon(); var coordinates = Arrays .stream(polygon.getCoordinates()) - .map(c -> new LngLatAlt(c.y, c.x)) + .map(c -> new LngLatAlt(c.x, c.y)) .toList(); ret.add(coordinates); diff --git a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java index 2f1e57ef4dc..fcc59af845f 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -131,6 +131,7 @@ static void setup() { VehicleParking .builder() .id(id("parking-1")) + .coordinate(WgsCoordinate.GREENWICH) .name(NonLocalizedString.ofNullable("parking")) .build() ), diff --git a/src/test/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapperTest.java b/src/test/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapperTest.java index 160014213d3..b8d08c7d7f5 100644 --- a/src/test/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapperTest.java @@ -13,9 +13,11 @@ class RequestModesMapperTest { void testMapRequestModesEmptyMapReturnsDefaults() { Map inputModes = Map.of(); + RequestModes wantModes = RequestModes.of().withDirectMode(null).build(); + RequestModes mappedModes = RequestModesMapper.mapRequestModes(inputModes); - assertEquals(RequestModes.of().build(), mappedModes); + assertEquals(wantModes, mappedModes); } @Test @@ -26,6 +28,7 @@ void testMapRequestModesAccessSetReturnsDefaultsForOthers() { .of() .withAccessMode(StreetMode.BIKE) .withTransferMode(StreetMode.BIKE) + .withDirectMode(null) .build(); RequestModes mappedModes = RequestModesMapper.mapRequestModes(inputModes); @@ -37,7 +40,11 @@ void testMapRequestModesAccessSetReturnsDefaultsForOthers() { void testMapRequestModesEgressSetReturnsDefaultsForOthers() { Map inputModes = Map.of("egressMode", StreetMode.CAR); - RequestModes wantModes = RequestModes.of().withEgressMode(StreetMode.CAR).build(); + RequestModes wantModes = RequestModes + .of() + .withEgressMode(StreetMode.CAR) + .withDirectMode(null) + .build(); RequestModes mappedModes = RequestModesMapper.mapRequestModes(inputModes); diff --git a/src/test/java/org/opentripplanner/framework/geometry/WgsCoordinateTest.java b/src/test/java/org/opentripplanner/framework/geometry/WgsCoordinateTest.java index 05051ab7dff..5cd7ac66123 100644 --- a/src/test/java/org/opentripplanner/framework/geometry/WgsCoordinateTest.java +++ b/src/test/java/org/opentripplanner/framework/geometry/WgsCoordinateTest.java @@ -109,15 +109,15 @@ void testGreenwich() { void roundingTo10m() { var hamburg = new WgsCoordinate(Coordinates.HAMBURG); var rounded = hamburg.roundToApproximate10m(); - assertEquals(10.0003, rounded.latitude()); - assertEquals(53.5566, rounded.longitude()); + assertEquals(10.0003, rounded.longitude()); + assertEquals(53.5566, rounded.latitude()); } @Test void roundingTo100m() { var hamburg = new WgsCoordinate(Coordinates.HAMBURG); var rounded = hamburg.roundToApproximate100m(); - assertEquals(10, rounded.latitude()); - assertEquals(53.557, rounded.longitude()); + assertEquals(10, rounded.longitude()); + assertEquals(53.557, rounded.latitude()); } } diff --git a/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java index 96e71b9d3ec..4d2141eb38e 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java @@ -102,12 +102,12 @@ private static class TestModel { public TestModel() { var from = StreetModelForTest.intersectionVertex( - KONGSBERG_PLATFORM_1.x - DELTA, - KONGSBERG_PLATFORM_1.y - DELTA + KONGSBERG_PLATFORM_1.y - DELTA, + KONGSBERG_PLATFORM_1.x - DELTA ); var to = StreetModelForTest.intersectionVertex( - KONGSBERG_PLATFORM_1.x + DELTA, - KONGSBERG_PLATFORM_1.y + DELTA + KONGSBERG_PLATFORM_1.y + DELTA, + KONGSBERG_PLATFORM_1.x + DELTA ); Graph graph = new Graph(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/VehicleParkingLinkingTest.java b/src/test/java/org/opentripplanner/graph_builder/module/VehicleParkingLinkingTest.java index 95f4df79b7b..23f4ffc72ca 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/VehicleParkingLinkingTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/VehicleParkingLinkingTest.java @@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingHelper; import org.opentripplanner.routing.vehicle_parking.VehicleParkingTestGraphData; import org.opentripplanner.street.model.StreetTraversalPermission; @@ -46,8 +45,8 @@ public void setup() { @Test public void entranceWithVertexLinkingTest() { - var parking = VehicleParking - .builder() + var parking = StreetModelForTest + .vehicleParking() .entrance(builder -> builder.entranceId(id("1")).coordinate(new WgsCoordinate(A.getCoordinate())).vertex(A) ) @@ -65,8 +64,8 @@ public void entranceWithVertexLinkingTest() { @Test public void entranceWithoutVertexLinkingTest() { - var parking = VehicleParking - .builder() + var parking = StreetModelForTest + .vehicleParking() .entrance(builder -> builder .entranceId(id("1")) @@ -99,8 +98,8 @@ public void carParkingEntranceToAllTraversableStreetLinkingTest() { StreetModelForTest.streetEdge(A, C, StreetTraversalPermission.NONE); - var parking = VehicleParking - .builder() + var parking = StreetModelForTest + .vehicleParking() .entrance(builder -> builder .entranceId(id("1")) @@ -123,9 +122,8 @@ public void carParkingEntranceToAllTraversableStreetLinkingTest() { @Test public void removeEntranceWithNonExistingVertexTest() { - var vehicleParking = VehicleParking - .builder() - .id(id("VP")) + var vehicleParking = StreetModelForTest + .vehicleParking() .bicyclePlaces(true) .entrance(builder -> builder @@ -159,9 +157,8 @@ public void removeEntranceWithNonExistingVertexTest() { @Test public void removeVehicleParkingWithOneEntranceAndNonExistingVertexTest() { - var vehicleParking = VehicleParking - .builder() - .id(id("VP")) + var vehicleParking = StreetModelForTest + .vehicleParking() .bicyclePlaces(true) .entrance(builder -> builder diff --git a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/SubgraphOnlyFerryTest.java b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/SubgraphOnlyFerryTest.java new file mode 100644 index 00000000000..26aaf12896b --- /dev/null +++ b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/SubgraphOnlyFerryTest.java @@ -0,0 +1,112 @@ +package org.opentripplanner.graph_builder.module.islandpruning; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.opentripplanner.street.model.vertex.TransitStopVertex; +import org.opentripplanner.street.model.vertex.TransitStopVertexBuilder; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.basic.TransitMode; +import org.opentripplanner.transit.model.site.RegularStop; + +class SubgraphOnlyFerryTest { + + private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + private static final RegularStop regularStop1 = TEST_MODEL.stop("TEST-1").build(); + private static final RegularStop regularStop2 = TEST_MODEL.stop("TEST-2").build(); + + @Test + void subgraphHasOnlyFerry() { + TransitStopVertex transitStopVertex = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of(TransitMode.FERRY)) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex); + + assertTrue(subgraph.hasOnlyFerryStops()); + } + + @Test + void subgraphHasOnlyNoFerry() { + TransitStopVertex transitStopVertex1 = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of(TransitMode.BUS)) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex1); + + assertFalse(subgraph.hasOnlyFerryStops()); + } + + @Test + void subgraphHasOnlyNoMode() { + TransitStopVertex transitStopVertex1 = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of()) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex1); + + assertFalse(subgraph.hasOnlyFerryStops()); + } + + @Test + void subgraphHasOnlyFerryMoreStops() { + TransitStopVertex transitStopVertex1 = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of(TransitMode.FERRY)) + .build(); + TransitStopVertex transitStopVertex2 = new TransitStopVertexBuilder() + .withStop(regularStop2) + .withModes(Set.of(TransitMode.FERRY)) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex1); + subgraph.addVertex(transitStopVertex2); + + assertTrue(subgraph.hasOnlyFerryStops()); + } + + @Test + void subgraphHasNotOnlyFerryMoreStops() { + TransitStopVertex transitStopVertex1 = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of(TransitMode.FERRY)) + .build(); + TransitStopVertex transitStopVertex2 = new TransitStopVertexBuilder() + .withStop(regularStop2) + .withModes(Set.of(TransitMode.BUS)) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex1); + subgraph.addVertex(transitStopVertex2); + + assertFalse(subgraph.hasOnlyFerryStops()); + } + + @Test + void subgraphHasNoModeMoreStops() { + TransitStopVertex transitStopVertex1 = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of(TransitMode.FERRY)) + .build(); + TransitStopVertex transitStopVertex2 = new TransitStopVertexBuilder() + .withStop(regularStop2) + .withModes(Set.of()) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex1); + subgraph.addVertex(transitStopVertex2); + + assertFalse(subgraph.hasOnlyFerryStops()); + } +} diff --git a/src/test/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializerTest.java b/src/test/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializerTest.java index da295073e01..2d7df788ad3 100644 --- a/src/test/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializerTest.java +++ b/src/test/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializerTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import java.time.LocalDate; import org.junit.jupiter.api.Test; @@ -86,4 +87,19 @@ void testScheduledTransitLegReferenceLegacyV2Deserialize() { assertEquals(FROM_STOP_POS, ref.fromStopPositionInPattern()); assertEquals(TO_STOP_POS, ref.toStopPositionInPattern()); } + + @Test + void testNullSerializedLegReference() { + assertNull(LegReferenceSerializer.decode(null)); + } + + @Test + void testEmptySerializedLegReference() { + assertNull(LegReferenceSerializer.decode("")); + } + + @Test + void testIllegalBase64CharacterInSerializedLegReference() { + assertNull(LegReferenceSerializer.decode("RUT:Line:5")); + } } diff --git a/src/test/java/org/opentripplanner/netex/mapping/NetexTestDataSample.java b/src/test/java/org/opentripplanner/netex/mapping/NetexTestDataSample.java index b8723fdbcf3..b72c9b05277 100644 --- a/src/test/java/org/opentripplanner/netex/mapping/NetexTestDataSample.java +++ b/src/test/java/org/opentripplanner/netex/mapping/NetexTestDataSample.java @@ -40,6 +40,7 @@ import org.rutebanken.netex.model.ScheduledStopPointRefStructure; import org.rutebanken.netex.model.ServiceAlterationEnumeration; import org.rutebanken.netex.model.ServiceJourney; +import org.rutebanken.netex.model.ServiceJourneyRefStructure; import org.rutebanken.netex.model.StopPointInJourneyPattern; import org.rutebanken.netex.model.StopPointInJourneyPatternRefStructure; import org.rutebanken.netex.model.TimetabledPassingTime; @@ -50,9 +51,11 @@ public class NetexTestDataSample { public static final String SERVICE_JOURNEY_ID = "RUT:ServiceJourney:1"; + public static final String DATED_SERVICE_JOURNEY_ID_1 = "RUT:DatedServiceJourney:1"; + public static final String DATED_SERVICE_JOURNEY_ID_2 = "RUT:DatedServiceJourney:2"; public static final List DATED_SERVICE_JOURNEY_ID = List.of( - "RUT:DatedServiceJourney:1", - "RUT:DatedServiceJourney:2" + DATED_SERVICE_JOURNEY_ID_1, + DATED_SERVICE_JOURNEY_ID_2 ); public static final List OPERATING_DAYS = List.of("2022-02-28", "2022-02-29"); private static final DayType EVERYDAY = new DayType() @@ -174,6 +177,11 @@ public NetexTestDataSample() { DatedServiceJourney datedServiceJourney = new DatedServiceJourney() .withId(DATED_SERVICE_JOURNEY_ID.get(i)) + .withJourneyRef( + List.of( + MappingSupport.createWrappedRef(SERVICE_JOURNEY_ID, ServiceJourneyRefStructure.class) + ) + ) .withServiceAlteration(ServiceAlterationEnumeration.PLANNED) .withOperatingDayRef(new OperatingDayRefStructure().withRef(operatingDay.getId())); @@ -218,6 +226,15 @@ public HierarchicalMapById getServiceJourneyById() { return serviceJourneyById; } + public DatedServiceJourney getDatedServiceJourneyById(String id) { + return datedServiceJourneyBySjId + .values() + .stream() + .filter(datedServiceJourney -> datedServiceJourney.getId().equals(id)) + .findFirst() + .orElse(null); + } + public ServiceJourney getServiceJourney() { return serviceJourneyById.lookup(SERVICE_JOURNEY_ID); } diff --git a/src/test/java/org/opentripplanner/netex/mapping/TripPatternMapperTest.java b/src/test/java/org/opentripplanner/netex/mapping/TripPatternMapperTest.java index 74448fb3638..95af8f623e5 100644 --- a/src/test/java/org/opentripplanner/netex/mapping/TripPatternMapperTest.java +++ b/src/test/java/org/opentripplanner/netex/mapping/TripPatternMapperTest.java @@ -1,9 +1,12 @@ package org.opentripplanner.netex.mapping; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.ArrayListMultimap; +import java.util.List; import java.util.Map; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -19,17 +22,19 @@ import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; import org.rutebanken.netex.model.DatedServiceJourney; +import org.rutebanken.netex.model.DatedServiceJourneyRefStructure; import org.rutebanken.netex.model.OperatingDay; +import org.rutebanken.netex.model.ServiceAlterationEnumeration; /** * @author Thomas Gran (Capra) - tgr@capraconsulting.no (29.11.2017) */ -public class TripPatternMapperTest { +class TripPatternMapperTest { private static final FeedScopedId SERVICE_ID = TransitModelForTest.id("S01"); @Test - public void testMapTripPattern() { + void testMapTripPattern() { NetexTestDataSample sample = new NetexTestDataSample(); TripPatternMapper tripPatternMapper = new TripPatternMapper( @@ -88,9 +93,85 @@ public void testMapTripPattern() { } @Test - public void testMapTripPattern_datedServiceJourney() { + void testMapTripPattern_datedServiceJourney() { NetexTestDataSample sample = new NetexTestDataSample(); + Optional res = mapTripPattern(sample); + assertTrue(res.isPresent()); + + var r = res.get(); + + assertEquals(2, r.tripOnServiceDates().size()); + + Trip trip = r.tripPattern().scheduledTripsAsStream().findFirst().get(); + + for (TripOnServiceDate tripOnServiceDate : r.tripOnServiceDates()) { + assertEquals(trip, tripOnServiceDate.getTrip()); + assertEquals(TripAlteration.PLANNED, tripOnServiceDate.getTripAlteration()); + assertEquals( + 1, + sample + .getOperatingDaysById() + .localValues() + .stream() + .map(OperatingDay::getId) + .filter(id -> id.equals(tripOnServiceDate.getServiceDate().toString())) + .count() + ); + } + } + + @Test + void testDatedServiceJourneyReplacement() { + NetexTestDataSample sample = new NetexTestDataSample(); + DatedServiceJourney dsjReplaced = sample.getDatedServiceJourneyById( + NetexTestDataSample.DATED_SERVICE_JOURNEY_ID_1 + ); + dsjReplaced.setServiceAlteration(ServiceAlterationEnumeration.REPLACED); + DatedServiceJourney dsjReplacing = sample.getDatedServiceJourneyById( + NetexTestDataSample.DATED_SERVICE_JOURNEY_ID_2 + ); + dsjReplacing.withJourneyRef( + List.of( + MappingSupport.createWrappedRef(dsjReplaced.getId(), DatedServiceJourneyRefStructure.class) + ) + ); + Optional res = mapTripPattern(sample); + + assertTrue(res.isPresent()); + var r = res.get(); + Optional replacedTripOnServiceDate = r + .tripOnServiceDates() + .stream() + .filter(tripOnServiceDate -> + NetexTestDataSample.DATED_SERVICE_JOURNEY_ID_1.equals(tripOnServiceDate.getId().getId()) + ) + .findFirst(); + + assertTrue(replacedTripOnServiceDate.isPresent()); + assertEquals(TripAlteration.REPLACED, replacedTripOnServiceDate.get().getTripAlteration()); + + Optional replacingTripOnServiceDate = r + .tripOnServiceDates() + .stream() + .filter(tripOnServiceDate -> + NetexTestDataSample.DATED_SERVICE_JOURNEY_ID_2.equals(tripOnServiceDate.getId().getId()) + ) + .findFirst(); + + assertTrue(replacingTripOnServiceDate.isPresent()); + assertEquals(TripAlteration.PLANNED, replacingTripOnServiceDate.get().getTripAlteration()); + assertFalse(replacingTripOnServiceDate.get().getReplacementFor().isEmpty()); + + // the replaced trip should refer to the same object (object identity) whether it is accessed + // directly from the replaced DSJ or indirectly through the replacing DSJ. + assertSame( + replacingTripOnServiceDate.get().getReplacementFor().getFirst().getTrip(), + replacedTripOnServiceDate.get().getTrip() + ); + } + + private static Optional mapTripPattern(NetexTestDataSample sample) { HierarchicalMapById datedServiceJourneys = new HierarchicalMapById<>(); datedServiceJourneys.addAll(sample.getDatedServiceJourneyBySjId().values()); @@ -121,28 +202,6 @@ public void testMapTripPattern_datedServiceJourney() { Optional res = tripPatternMapper.mapTripPattern( sample.getJourneyPattern() ); - - assertTrue(res.isPresent()); - - var r = res.get(); - - assertEquals(2, r.tripOnServiceDates().size()); - - Trip trip = r.tripPattern().scheduledTripsAsStream().findFirst().get(); - - for (TripOnServiceDate tripOnServiceDate : r.tripOnServiceDates()) { - assertEquals(trip, tripOnServiceDate.getTrip()); - assertEquals(TripAlteration.PLANNED, tripOnServiceDate.getTripAlteration()); - assertEquals( - 1, - sample - .getOperatingDaysById() - .localValues() - .stream() - .map(OperatingDay::getId) - .filter(id -> id.equals(tripOnServiceDate.getServiceDate().toString())) - .count() - ); - } + return res; } } diff --git a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java index c316793ad8c..c0af4cf2701 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java @@ -92,21 +92,6 @@ void testIsOneWayBicycle() { assertTrue(way.isOneWayReverseBicycle()); } - @Test - void testIsOneDirectionSidepath() { - OSMWay way = new OSMWay(); - assertFalse(way.isForwardDirectionSidepath()); - assertFalse(way.isReverseDirectionSidepath()); - - way.addTag("bicycle:forward", "use_sidepath"); - assertTrue(way.isForwardDirectionSidepath()); - assertFalse(way.isReverseDirectionSidepath()); - - way.addTag("bicycle:backward", "use_sidepath"); - assertTrue(way.isForwardDirectionSidepath()); - assertTrue(way.isReverseDirectionSidepath()); - } - @Test void testIsOpposableCycleway() { OSMWay way = new OSMWay(); diff --git a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java index ca5db77df12..aefd287cac8 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java @@ -136,7 +136,7 @@ void testBicycleDenied() { assertFalse(tags.isBicycleExplicitlyDenied(), "bicycle=" + allowedValue); } - for (var deniedValue : List.of("no", "dismount", "license", "use_sidepath")) { + for (var deniedValue : List.of("no", "dismount", "license")) { tags.addTag("bicycle", deniedValue); assertTrue(tags.isBicycleExplicitlyDenied(), "bicycle=" + deniedValue); } diff --git a/src/test/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapperTest.java b/src/test/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapperTest.java index 9a16f6a8e2e..2a8988dda61 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapperTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapperTest.java @@ -147,6 +147,48 @@ void bicycleDiscouraged() { assertEquals(2.94, discouragedProps.bicycleSafety().forward(), epsilon); } + @Test + void footUseSidepath() { + var regular = WayTestData.pedestrianTunnel(); + var props = wps.getDataForWay(regular); + assertEquals(PEDESTRIAN_AND_BICYCLE, props.getPermission()); + assertEquals(1, props.walkSafety().forward()); + + var useSidepath = WayTestData.pedestrianTunnel().addTag("foot", "use_sidepath"); + var useSidepathProps = wps.getDataForWay(useSidepath); + assertEquals(PEDESTRIAN_AND_BICYCLE, useSidepathProps.getPermission()); + assertEquals(5, useSidepathProps.walkSafety().forward()); + } + + @Test + void bicycleUseSidepath() { + var regular = WayTestData.southeastLaBonitaWay(); + var props = wps.getDataForWay(regular); + assertEquals(ALL, props.getPermission()); + assertEquals(.98, props.bicycleSafety().forward()); + + var useSidepath = WayTestData.southeastLaBonitaWay().addTag("bicycle", "use_sidepath"); + var useSidepathProps = wps.getDataForWay(useSidepath); + assertEquals(ALL, useSidepathProps.getPermission()); + assertEquals(4.9, useSidepathProps.bicycleSafety().forward(), epsilon); + + var useSidepathForward = WayTestData + .southeastLaBonitaWay() + .addTag("bicycle:forward", "use_sidepath"); + var useSidepathForwardProps = wps.getDataForWay(useSidepathForward); + assertEquals(ALL, useSidepathForwardProps.getPermission()); + assertEquals(4.9, useSidepathForwardProps.bicycleSafety().forward(), epsilon); + assertEquals(0.98, useSidepathForwardProps.bicycleSafety().back(), epsilon); + + var useSidepathBackward = WayTestData + .southeastLaBonitaWay() + .addTag("bicycle:backward", "use_sidepath"); + var useSidepathBackwardProps = wps.getDataForWay(useSidepathBackward); + assertEquals(ALL, useSidepathBackwardProps.getPermission()); + assertEquals(0.98, useSidepathBackwardProps.bicycleSafety().forward(), epsilon); + assertEquals(4.9, useSidepathBackwardProps.bicycleSafety().back(), epsilon); + } + /** * Test that two values are within epsilon of each other. */ diff --git a/src/test/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapperTest.java b/src/test/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapperTest.java index a2f84873f20..4dd52195acb 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapperTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapperTest.java @@ -162,6 +162,13 @@ public void testSafetyWithMixins() { wps.getDataForWay(wayWithMixinsAndCustomSafety).walkSafety().forward(), epsilon ); + + OSMWithTags wayWithBicycleSidePath = new OSMWithTags(); + wayWithBicycleSidePath.addTag("bicycle", "use_sidepath"); + assertEquals(8, wps.getDataForWay(wayWithBicycleSidePath).walkSafety().forward(), epsilon); + OSMWithTags wayWithFootSidePath = new OSMWithTags(); + wayWithFootSidePath.addTag("foot", "use_sidepath"); + assertEquals(8, wps.getDataForWay(wayWithFootSidePath).walkSafety().forward(), epsilon); } @Test diff --git a/src/test/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySetTest.java b/src/test/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySetTest.java index e8f5b6e51d3..24360539b6f 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySetTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySetTest.java @@ -256,65 +256,6 @@ void testMotorVehicleTagDeniedPermissions() { assertTrue(permissionPair.main().allowsNothing());*/ } - @Test - void testSidepathPermissions() { - OSMWay way = new OSMWay(); - way.addTag("bicycle", "use_sidepath"); - way.addTag("highway", "primary"); - way.addTag("lanes", "2"); - way.addTag("maxspeed", "70"); - way.addTag("oneway", "yes"); - var permissionPair = getWayProperties(way); - - assertFalse(permissionPair.main().allows(StreetTraversalPermission.BICYCLE)); - assertFalse(permissionPair.back().allows(StreetTraversalPermission.BICYCLE)); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.CAR)); - assertFalse(permissionPair.back().allows(StreetTraversalPermission.CAR)); - - way = new OSMWay(); - way.addTag("bicycle:forward", "use_sidepath"); - way.addTag("highway", "tertiary"); - permissionPair = getWayProperties(way); - - assertFalse(permissionPair.main().allows(StreetTraversalPermission.BICYCLE)); - assertTrue(permissionPair.back().allows(StreetTraversalPermission.BICYCLE)); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.CAR)); - assertTrue(permissionPair.back().allows(StreetTraversalPermission.CAR)); - - way = new OSMWay(); - way.addTag("bicycle:backward", "use_sidepath"); - way.addTag("highway", "tertiary"); - permissionPair = getWayProperties(way); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.BICYCLE)); - assertFalse(permissionPair.back().allows(StreetTraversalPermission.BICYCLE)); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.CAR)); - assertTrue(permissionPair.back().allows(StreetTraversalPermission.CAR)); - - way = new OSMWay(); - way.addTag("highway", "tertiary"); - way.addTag("oneway", "yes"); - way.addTag("oneway:bicycle", "no"); - permissionPair = getWayProperties(way); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.BICYCLE)); - assertTrue(permissionPair.back().allows(StreetTraversalPermission.BICYCLE)); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.CAR)); - assertFalse(permissionPair.back().allows(StreetTraversalPermission.CAR)); - - way.addTag("bicycle:forward", "use_sidepath"); - permissionPair = getWayProperties(way); - assertFalse(permissionPair.main().allows(StreetTraversalPermission.BICYCLE)); - assertTrue(permissionPair.back().allows(StreetTraversalPermission.BICYCLE)); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.CAR)); - assertFalse(permissionPair.back().allows(StreetTraversalPermission.CAR)); - } - private StreetTraversalPermissionPair getWayProperties(OSMWay way) { WayPropertySet wayPropertySet = new WayPropertySet(); WayProperties wayData = wayPropertySet.getDataForWay(way); diff --git a/src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java b/src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java index 0bca83be8bf..78489508dc4 100644 --- a/src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java +++ b/src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java @@ -60,6 +60,8 @@ public interface RaptorTestConstants { int STOP_L = 12; int STOP_M = 13; + int NUM_STOPS = 14; + // Stop position in pattern int STOP_POS_0 = 0; int STOP_POS_1 = 1; 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 2a77996b47b..037c345bc23 100644 --- a/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java +++ b/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java @@ -66,6 +66,8 @@ public class TestTransitData private final List constrainedTransfers = new ArrayList<>(); private final GeneralizedCostParametersBuilder costParamsBuilder = GeneralizedCostParameters.of(); + private final int[] stopBoardAlightCosts = new int[NUM_STOPS]; + private RaptorSlackProvider slackProvider = SLACK_PROVIDER; @Override @@ -300,6 +302,11 @@ public TestTransitData withConstrainedTransfer( return this; } + public TestTransitData withStopBoardAlightCost(int stop, int boardAlightCost) { + stopBoardAlightCosts[stop] = boardAlightCost; + return this; + } + public GeneralizedCostParametersBuilder mcCostParamsBuilder() { return costParamsBuilder; } @@ -352,7 +359,7 @@ public List getPatterns() { private int[] stopBoardAlightCost() { // Not implemented, no test for this yet. - return null; + return stopBoardAlightCosts; } private void expandNumOfStops(int stopIndex) { diff --git a/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopTransferCostTest.java b/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopTransferCostTest.java new file mode 100644 index 00000000000..21c1dd2a755 --- /dev/null +++ b/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopTransferCostTest.java @@ -0,0 +1,76 @@ +package org.opentripplanner.raptor.moduletests; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.raptor._data.transit.TestRoute.route; +import static org.opentripplanner.raptor._data.transit.TestTripSchedule.schedule; +import static org.opentripplanner.raptor.moduletests.support.RaptorModuleTestConfig.multiCriteria; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.raptor.RaptorService; +import org.opentripplanner.raptor._data.RaptorTestConstants; +import org.opentripplanner.raptor._data.transit.TestAccessEgress; +import org.opentripplanner.raptor._data.transit.TestTransitData; +import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; +import org.opentripplanner.raptor.configure.RaptorConfig; +import org.opentripplanner.raptor.moduletests.support.ModuleTestDebugLogging; +import org.opentripplanner.raptor.moduletests.support.RaptorModuleTestCase; + +/** + * FEATURE UNDER TEST + * + * This verifies that the stopTransferCost is not applied for egress legs. If this is not correctly + * handled by the heuristics optimization, the cheapest journey could be discarded. + */ +public class B05_EgressStopTransferCostTest implements RaptorTestConstants { + + private final TestTransitData data = new TestTransitData(); + private final RaptorRequestBuilder requestBuilder = new RaptorRequestBuilder<>(); + private final RaptorService raptorService = new RaptorService<>( + RaptorConfig.defaultConfigForTest() + ); + + @BeforeEach + void setup() { + data + .withRoute(route("R1", STOP_B, STOP_C).withTimetable(schedule("0:10, 0:14"))) + .withRoute(route("R2", STOP_C, STOP_D).withTimetable(schedule("0:18, 0:20"))); + + data.mcCostParamsBuilder().transferCost(0).boardCost(0); + data.withStopBoardAlightCost(STOP_D, 60000); + + requestBuilder + .searchParams() + .addAccessPaths(TestAccessEgress.free(STOP_B)) + .addEgressPaths( + TestAccessEgress.walk(STOP_C, D5m), // This will be the fastest + TestAccessEgress.walk(STOP_D, D20s) // This will be the cheapest + ) + .earliestDepartureTime(T00_00) + .latestArrivalTime(T00_30); + + ModuleTestDebugLogging.setupDebugLogging(data, requestBuilder); + } + + static List testCases() { + return RaptorModuleTestCase + .of() + .add( + multiCriteria(), + // We should get both the fastest and the c1-cheapest results + // The stopTransferCost should not be applied to the egress leg from STOP_D + "B ~ BUS R1 0:10 0:14 ~ C ~ Walk 5m [0:10 0:19 9m Tₓ0 C₁840]", + "B ~ BUS R1 0:10 0:14 ~ C ~ BUS R2 0:18 0:20 ~ D ~ Walk 20s [0:10 0:20:20 10m20s Tₓ1 C₁640]" + ) + .build(); + } + + @ParameterizedTest + @MethodSource("testCases") + void testRaptor(RaptorModuleTestCase testCase) { + assertEquals(testCase.expected(), testCase.run(raptorService, data, requestBuilder)); + } +} diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayerTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayerTest.java new file mode 100644 index 00000000000..7c674252e6a --- /dev/null +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayerTest.java @@ -0,0 +1,201 @@ +package org.opentripplanner.routing.algorithm.raptoradapter.transit; + +import static java.util.Map.entry; +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 java.time.LocalDate; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.opentripplanner.model.StopTime; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.framework.Deduplicator; +import org.opentripplanner.transit.model.network.RoutingTripPattern; +import org.opentripplanner.transit.model.network.StopPattern; +import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.timetable.TripTimes; +import org.opentripplanner.transit.model.timetable.TripTimesFactory; + +class TransitLayerTest { + + private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + private static final TripTimes TRIP_TIMES; + + private static final RoutingTripPattern TRIP_PATTERN; + + static { + var stop = TEST_MODEL.stop("TEST:STOP", 0, 0).build(); + var stopTime = new StopTime(); + stopTime.setStop(stop); + var stopPattern = new StopPattern(List.of(stopTime)); + var route = TransitModelForTest.route("1").build(); + TRIP_PATTERN = + TripPattern + .of(TransitModelForTest.id("P1")) + .withRoute(route) + .withStopPattern(stopPattern) + .build() + .getRoutingTripPattern(); + TRIP_TIMES = + TripTimesFactory.tripTimes( + TransitModelForTest.trip("1").withRoute(route).build(), + List.of(new StopTime()), + new Deduplicator() + ); + } + + @Test + void testGetTripPatternsRunningOnDateCopy() { + var date = LocalDate.of(2024, 1, 1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + date + ); + var tripPatterns = List.of(tripPatternForDate); + var transitLayer = new TransitLayer( + Map.of(date, tripPatterns), + null, + null, + null, + null, + null, + null, + null, + null + ); + var runningOnDate = transitLayer.getTripPatternsRunningOnDateCopy(date); + assertEquals(1, runningOnDate.size()); + assertEquals(tripPatterns, runningOnDate); + assertFalse(tripPatterns == runningOnDate); + assertEquals(0, transitLayer.getTripPatternsRunningOnDateCopy(date.minusDays(1)).size()); + assertEquals(0, transitLayer.getTripPatternsRunningOnDateCopy(date.plusDays(1)).size()); + } + + @Test + void testGetTripPatternsForRunningDate() { + var date = LocalDate.of(2024, 1, 1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + date + ); + var tripPatterns = List.of(tripPatternForDate); + var transitLayer = new TransitLayer( + Map.of(date, tripPatterns), + null, + null, + null, + null, + null, + null, + null, + null + ); + var runningOnDate = transitLayer.getTripPatternsForRunningDate(date); + assertEquals(1, runningOnDate.size()); + assertEquals(tripPatterns, runningOnDate); + assertTrue(tripPatterns == runningOnDate); + assertEquals(0, transitLayer.getTripPatternsForRunningDate(date.minusDays(1)).size()); + assertEquals(0, transitLayer.getTripPatternsForRunningDate(date.plusDays(1)).size()); + } + + @Test + void testGetTripPatternsOnServiceDateCopyWithSameRunningAndServiceDate() { + var date = LocalDate.of(2024, 1, 1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + date + ); + var transitLayer = new TransitLayer( + Map.of(date, List.of(tripPatternForDate)), + null, + null, + null, + null, + null, + null, + null, + null + ); + var startingOnDate = transitLayer.getTripPatternsOnServiceDateCopy(date); + assertEquals(1, startingOnDate.size()); + assertEquals(tripPatternForDate, startingOnDate.getFirst()); + assertEquals(0, transitLayer.getTripPatternsOnServiceDateCopy(date.minusDays(1)).size()); + assertEquals(0, transitLayer.getTripPatternsOnServiceDateCopy(date.plusDays(1)).size()); + } + + @Test + void testGetTripPatternsOnServiceDateCopyWithServiceRunningAfterMidnight() { + var runningDate = LocalDate.of(2024, 1, 1); + var serviceDate = runningDate.minusDays(1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + serviceDate + ); + var transitLayer = new TransitLayer( + Map.of(runningDate, List.of(tripPatternForDate)), + null, + null, + null, + null, + null, + null, + null, + null + ); + var startingOnDate = transitLayer.getTripPatternsOnServiceDateCopy(serviceDate); + // starting date should be determined by service date, not running date which refers to the + // normal calendar date that the trip pattern is running on + assertEquals(1, startingOnDate.size()); + assertEquals(tripPatternForDate, startingOnDate.getFirst()); + assertEquals(0, transitLayer.getTripPatternsOnServiceDateCopy(runningDate).size()); + } + + @Test + void testGetTripPatternsOnServiceDateCopyWithServiceRunningBeforeAndAfterMidnight() { + // This is same as the service date + var firstRunningDate = LocalDate.of(2024, 1, 1); + var secondRunningDate = firstRunningDate.plusDays(1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + firstRunningDate + ); + var transitLayer = new TransitLayer( + Map.ofEntries( + entry(firstRunningDate, List.of(tripPatternForDate)), + entry(secondRunningDate, List.of(tripPatternForDate)) + ), + null, + null, + null, + null, + null, + null, + null, + null + ); + var startingOnDate = transitLayer.getTripPatternsOnServiceDateCopy(firstRunningDate); + // Transit layer indexes trip patterns by running date and to get trip patterns for certain + // service date, we need to look up the trip patterns for the next running date as well, but + // we don't want to return duplicates + assertEquals(1, startingOnDate.size()); + assertEquals(tripPatternForDate, startingOnDate.getFirst()); + assertEquals(0, transitLayer.getTripPatternsOnServiceDateCopy(secondRunningDate).size()); + } +} diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java index 7b7ba23499c..3570d92bdbe 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java @@ -76,22 +76,28 @@ public void calculateMinCost() { // - Transit factor: 80 (min of 80 and 100) // Board cost is 500: - assertEquals(500, subject.calculateMinCost(0, 0)); + assertEquals(500, subject.calculateRemainingMinCost(0, 0, 0)); // The transfer 1s * 80 = 80 + board cost 500 - assertEquals(580, subject.calculateMinCost(1, 0)); + assertEquals(580, subject.calculateRemainingMinCost(1, 0, 0)); // Board 2 times and transfer 1: 2 * 500 + 200 - assertEquals(1200, subject.calculateMinCost(0, 1)); + assertEquals(1200, subject.calculateRemainingMinCost(0, 1, 0)); // Transit 200s * 80 + Board 4 * 500 + Transfer 3 * 200 - assertEquals(18_600, subject.calculateMinCost(200, 3)); + assertEquals(18_600, subject.calculateRemainingMinCost(200, 3, 0)); + + // Cost of egress should subtract the stop transfer cost 25 + assertEquals(-25, subject.calculateRemainingMinCost(0, -1, 1)); } @Test public void testConvertBetweenRaptorAndMainOtpDomainModel() { - assertEquals(RaptorCostConverter.toRaptorCost(BOARD_COST_SEC), subject.calculateMinCost(0, 0)); + assertEquals( + RaptorCostConverter.toRaptorCost(BOARD_COST_SEC), + subject.calculateRemainingMinCost(0, 0, 0) + ); assertEquals( RaptorCostConverter.toRaptorCost(0.8 * 20 + BOARD_COST_SEC), - subject.calculateMinCost(20, 0) + subject.calculateRemainingMinCost(20, 0, 0) ); } diff --git a/src/test/java/org/opentripplanner/routing/core/MoneyTest.java b/src/test/java/org/opentripplanner/routing/core/MoneyTest.java index 5f197708f1d..01392e64493 100644 --- a/src/test/java/org/opentripplanner/routing/core/MoneyTest.java +++ b/src/test/java/org/opentripplanner/routing/core/MoneyTest.java @@ -102,4 +102,9 @@ void greaterThan() { void serializable() { assertInstanceOf(Serializable.class, oneDollar); } + + @Test + void equalHashCode() { + assertEquals(Money.usDollars(5).hashCode(), Money.usDollars(5).hashCode()); + } } diff --git a/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java b/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java index 49dc42e6f4c..c01558338a4 100644 --- a/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java +++ b/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java @@ -39,8 +39,8 @@ public class TemporaryVerticesContainerTest { private final Graph g = new Graph(new Deduplicator()); private final StreetVertex a = StreetModelForTest.intersectionVertex("A", 1.0, 1.0); - private final StreetVertex b = StreetModelForTest.intersectionVertex("B", 0.0, 1.0); - private final StreetVertex c = StreetModelForTest.intersectionVertex("C", 1.0, 0.0); + private final StreetVertex b = StreetModelForTest.intersectionVertex("B", 1.0, 0.0); + private final StreetVertex c = StreetModelForTest.intersectionVertex("C", 0.0, 1.0); private final List permanentVertexes = Arrays.asList(a, b, c); // - And travel *origin* is 0,4 degrees on the road from B to A private final GenericLocation from = new GenericLocation(1.0, 0.4); diff --git a/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelperTest.java b/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelperTest.java index 9c80cb9cda9..90bdeb015a4 100644 --- a/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelperTest.java +++ b/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelperTest.java @@ -10,6 +10,7 @@ import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.vehicle_parking.VehicleParking.VehicleParkingEntranceCreator; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.edge.VehicleParkingEdge; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -41,8 +42,8 @@ void linkThreeVerticesTest() { @Test void linkSkippingEdgesTest() { Graph graph = new Graph(); - var vehicleParking = VehicleParking - .builder() + var vehicleParking = StreetModelForTest + .vehicleParking() .entrances( IntStream .rangeClosed(1, 3) @@ -67,8 +68,8 @@ void linkSkippingEdgesTest() { } private VehicleParking createParingWithEntrances(int entranceNumber) { - return VehicleParking - .builder() + return StreetModelForTest + .vehicleParking() .bicyclePlaces(true) .entrances( IntStream diff --git a/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingTestUtil.java b/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingTestUtil.java index 078d7350dcc..fca20322ebe 100644 --- a/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingTestUtil.java +++ b/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingTestUtil.java @@ -2,6 +2,7 @@ import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.NonLocalizedString; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.transit.model.framework.FeedScopedId; public class VehicleParkingTestUtil { @@ -25,8 +26,8 @@ public static VehicleParking createParkingWithEntrances( .coordinate(new WgsCoordinate(y, x)) .walkAccessible(true); - return VehicleParking - .builder() + return StreetModelForTest + .vehicleParking() .id(new FeedScopedId(TEST_FEED_ID, id)) .bicyclePlaces(true) .capacity(vehiclePlaces) diff --git a/src/test/java/org/opentripplanner/standalone/config/framework/json/NodeAdapterTest.java b/src/test/java/org/opentripplanner/standalone/config/framework/json/NodeAdapterTest.java index be44d1ea86f..9d3719c2285 100644 --- a/src/test/java/org/opentripplanner/standalone/config/framework/json/NodeAdapterTest.java +++ b/src/test/java/org/opentripplanner/standalone/config/framework/json/NodeAdapterTest.java @@ -34,7 +34,7 @@ public class NodeAdapterTest { public static final String NON_UNUSED_PARAMETERS = "EXPECTED_NONE"; @Test - public void testAsRawNode() { + void testAsRawNode() { NodeAdapter subject = newNodeAdapterForTest("{ child : { foo : 'bar' } }"); // Define child @@ -53,7 +53,7 @@ public void testAsRawNode() { } @Test - public void isEmpty() { + void isEmpty() { NodeAdapter subject = newNodeAdapterForTest(""); assertTrue(subject.of("alf").asObject().isEmpty()); @@ -64,7 +64,7 @@ public void isEmpty() { } @Test - public void path() { + void path() { NodeAdapter subject = newNodeAdapterForTest("{ foo : 'bar' }"); assertFalse(subject.of("foo").asObject().isEmpty()); assertTrue(subject.of("missingObject").asObject().isEmpty()); @@ -72,7 +72,7 @@ public void path() { } @Test - public void docInfo() { + void docInfo() { NodeAdapter subject = newNodeAdapterForTest("{ bool: false }"); subject.of("bool").since(V2_0).summary("B Summary").description("Ddd").asBoolean(); subject.of("en").since(V2_1).summary("EN Summary").asEnum(SECONDS); @@ -95,7 +95,7 @@ public void docInfo() { } @Test - public void asBoolean() { + void asBoolean() { NodeAdapter subject = newNodeAdapterForTest("{ aBoolean : true }"); assertTrue(subject.of("aBoolean").asBoolean()); assertTrue(subject.of("aBoolean").asBoolean(false)); @@ -103,7 +103,7 @@ public void asBoolean() { } @Test - public void asDouble() { + void asDouble() { NodeAdapter subject = newNodeAdapterForTest("{ aDouble : 7.0 }"); assertEquals(7.0, subject.of("aDouble").asDouble(-1d), 0.01); assertEquals(7.0, subject.of("aDouble").asDouble(), 0.01); @@ -111,28 +111,28 @@ public void asDouble() { } @Test - public void asDoubles() { + void asDoubles() { NodeAdapter subject = newNodeAdapterForTest("{ key : [ 2.0, 3.0, 5.0 ] }"); assertEquals(List.of(2d, 3d, 5d), subject.of("key").asDoubles(null)); assertEquals(NON_UNUSED_PARAMETERS, unusedParams(subject)); } @Test - public void asInt() { + void asInt() { NodeAdapter subject = newNodeAdapterForTest("{ aInt : 5 }"); assertEquals(5, subject.of("aInt").asInt(-1)); assertEquals(-1, subject.of("missingField").asInt(-1)); } @Test - public void asLong() { + void asLong() { NodeAdapter subject = newNodeAdapterForTest("{ key : 5 }"); assertEquals(5, subject.of("key").asLong(-1)); assertEquals(-1, subject.of("missingField").asLong(-1)); } @Test - public void asText() { + void asText() { NodeAdapter subject = newNodeAdapterForTest("{ aText : 'TEXT' }"); assertEquals("TEXT", subject.of("aText").asString("DEFAULT")); assertEquals("DEFAULT", subject.of("missingField").asString("DEFAULT")); @@ -142,19 +142,19 @@ public void asText() { } @Test - public void requiredAsText() { + void requiredAsText() { NodeAdapter subject = newNodeAdapterForTest("{ }"); assertThrows(OtpAppException.class, () -> subject.of("missingField").asString()); } @Test - public void rawAsText() { + void rawAsText() { NodeAdapter subject = newNodeAdapterForTest("{ aText : 'TEXT' }"); assertEquals("TEXT", subject.of("aText").asObject().asText()); } @Test - public void asEnum() { + void asEnum() { // Given NodeAdapter subject = newNodeAdapterForTest("{ a : 'A', abc : 'a-b-c' }"); @@ -169,7 +169,7 @@ public void asEnum() { } @Test - public void asEnumWithIllegalPropertySet() { + void asEnumWithIllegalPropertySet() { // Given NodeAdapter subject = newNodeAdapterForTest( """ @@ -205,7 +205,7 @@ public void asEnumWithIllegalPropertySet() { } @Test - public void asEnumMap() { + void asEnumMap() { // With optional enum values in map NodeAdapter subject = newNodeAdapterForTest("{ key : { A: true, B: false } }"); assertEquals( @@ -220,7 +220,7 @@ public void asEnumMap() { } @Test - public void asEnumMapWithCustomType() { + void asEnumMapWithCustomType() { // With optional enum values in map NodeAdapter subject = newNodeAdapterForTest("{ key : { A: {a:'Foo'} } }"); assertEquals( @@ -235,7 +235,7 @@ public void asEnumMapWithCustomType() { } @Test - public void asEnumMapWithDefaultValue() { + void asEnumMapWithDefaultValue() { var subject = newNodeAdapterForTest("{}"); final Map dflt = Map.of(AnEnum.A, new ARecord("Foo")); assertEquals(dflt, subject.of("key").asEnumMap(AnEnum.class, ARecord::fromJson, dflt)); @@ -243,7 +243,7 @@ public void asEnumMapWithDefaultValue() { } @Test - public void asEnumMapWithUnknownKey() { + void asEnumMapWithUnknownKey() { NodeAdapter subject = newNodeAdapterForTest("{ enumMap : { unknown : 7 } }"); subject.of("enumMap").asEnumMap(AnEnum.class, Double.class); @@ -261,7 +261,7 @@ public void asEnumMapWithUnknownKey() { } @Test - public void asEnumMapAllKeysRequired() { + void asEnumMapAllKeysRequired() { var subject = newNodeAdapterForTest("{ key : { A: true, b: false, a_B_c: true } }"); assertEquals( Map.of(AnEnum.A, true, AnEnum.B, false, AnEnum.A_B_C, true), @@ -280,7 +280,7 @@ public void asEnumMapAllKeysRequired() { } @Test - public void asEnumMapWithRequiredMissingValue() { + void asEnumMapWithRequiredMissingValue() { // A value for C is missing in map NodeAdapter subject = newNodeAdapterForTest("{ key : { A: true, B: false } }"); @@ -291,7 +291,7 @@ public void asEnumMapWithRequiredMissingValue() { } @Test - public void asEnumSet() { + void asEnumSet() { NodeAdapter subject = newNodeAdapterForTest("{ key : [ 'A', 'B' ] }"); assertEquals(Set.of(AnEnum.A, AnEnum.B), subject.of("key").asEnumSet(AnEnum.class)); assertEquals(Set.of(), subject.of("missing-key").asEnumSet(AnEnum.class)); @@ -299,13 +299,13 @@ public void asEnumSet() { } @Test - public void asEnumSetFailsUsingWrongFormat() { + void asEnumSetFailsUsingWrongFormat() { NodeAdapter subject = newNodeAdapterForTest("{ key : 'A,B' }"); assertThrows(OtpAppException.class, () -> subject.of("key").asEnumSet(AnEnum.class)); } @Test - public void asFeedScopedId() { + void asFeedScopedId() { NodeAdapter subject = newNodeAdapterForTest("{ key1: 'A:23', key2: 'B:12' }"); assertEquals("A:23", subject.of("key1").asFeedScopedId(null).toString()); assertEquals("B:12", subject.of("key2").asFeedScopedId(null).toString()); @@ -316,7 +316,7 @@ public void asFeedScopedId() { } @Test - public void asFeedScopedIds() { + void asFeedScopedIds() { NodeAdapter subject = newNodeAdapterForTest("{ routes: ['A:23', 'B:12']}"); assertEquals("[A:23, B:12]", subject.of("routes").asFeedScopedIds(List.of()).toString()); assertEquals("[]", subject.of("missing-key").asFeedScopedIds(List.of()).toString()); @@ -328,7 +328,7 @@ public void asFeedScopedIds() { } @Test - public void asFeedScopedIdSet() { + void asFeedScopedIdSet() { NodeAdapter subject = newNodeAdapterForTest("{ routes: ['A:23', 'B:12', 'A:23']}"); assertEquals( List.of( @@ -342,7 +342,7 @@ public void asFeedScopedIdSet() { } @Test - public void asDateOrRelativePeriod() { + void asDateOrRelativePeriod() { // Given var subject = newNodeAdapterForTest("{ 'a' : '2020-02-28', 'b' : '-P3Y' }"); var utc = ZoneIds.UTC; @@ -362,7 +362,7 @@ public void asDateOrRelativePeriod() { } @Test - public void testParsePeriodDateThrowsException() { + void testParsePeriodDateThrowsException() { // Given NodeAdapter subject = newNodeAdapterForTest("{ 'foo' : 'bar' }"); @@ -374,7 +374,7 @@ public void testParsePeriodDateThrowsException() { } @Test - public void asDuration() { + void asDuration() { NodeAdapter subject = newNodeAdapterForTest("{ k1:'PT1s', k2:'3h2m1s', k3:7 }"); // as required duration @@ -390,13 +390,13 @@ public void asDuration() { } @Test - public void requiredAsDuration() { + void requiredAsDuration() { NodeAdapter subject = newNodeAdapterForTest("{ }"); assertThrows(OtpAppException.class, () -> subject.of("missingField").asDuration()); } @Test - public void asDurations() { + void asDurations() { NodeAdapter subject = newNodeAdapterForTest("{ key1 : ['PT1s', '2h'] }"); assertEquals("[PT1S, PT2H]", subject.of("key1").asDurations(List.of()).toString()); assertEquals("[PT3H]", subject.of("missing-key").asDurations(List.of(D3h)).toString()); @@ -404,7 +404,7 @@ public void asDurations() { } @Test - public void asLocale() { + void asLocale() { NodeAdapter subject = newNodeAdapterForTest( "{ key1 : 'no', key2 : 'no_NO', key3 : 'no_NO_NY' }" ); @@ -415,14 +415,14 @@ public void asLocale() { } @Test - public void asPattern() { + void asPattern() { NodeAdapter subject = newNodeAdapterForTest("{ key : 'Ab*a' }"); assertEquals("Ab*a", subject.of("key").asPattern("ABC").toString()); assertEquals("ABC", subject.of("missingField").asPattern("ABC").toString()); } @Test - public void uri() { + void uri() { var URL = "gs://bucket/a.obj"; NodeAdapter subject = newNodeAdapterForTest("{ aUri : '" + URL + "' }"); @@ -433,14 +433,14 @@ public void uri() { } @Test - public void uriSyntaxException() { + void uriSyntaxException() { NodeAdapter subject = newNodeAdapterForTest("{ aUri : 'error$%uri' }"); assertThrows(OtpAppException.class, () -> subject.of("aUri").asUri(null), "error$%uri"); } @Test - public void uriRequiredValueMissing() { + void uriRequiredValueMissing() { NodeAdapter subject = newNodeAdapterForTest("{ }"); assertThrows( @@ -451,7 +451,7 @@ public void uriRequiredValueMissing() { } @Test - public void uris() { + void uris() { NodeAdapter subject = newNodeAdapterForTest("{ foo : ['gs://a/b', 'gs://c/d'] }"); assertEquals("[gs://a/b, gs://c/d]", subject.of("foo").asUris().toString()); @@ -461,7 +461,7 @@ public void uris() { } @Test - public void urisNotAnArrayException() { + void urisNotAnArrayException() { NodeAdapter subject = newNodeAdapterForTest("{ 'uris': 'no array' }"); assertThrows( @@ -472,7 +472,7 @@ public void urisNotAnArrayException() { } @Test - public void objectAsList() { + void objectAsList() { NodeAdapter subject = newNodeAdapterForTest("{ key : [{ a: 'I' }, { a: '2' } ] }"); List result = subject @@ -487,21 +487,21 @@ public void objectAsList() { } @Test - public void asCostLinearFunction() { + void asCostLinearFunction() { NodeAdapter subject = newNodeAdapterForTest("{ key : '400+8x' }"); assertEquals("6m40s + 8.0 t", subject.of("key").asCostLinearFunction(null).toString()); assertNull(subject.of("no-key").asCostLinearFunction(null)); } @Test - public void asTimePenalty() { + void asTimePenalty() { NodeAdapter subject = newNodeAdapterForTest("{ key : '400+8x' }"); assertEquals("6m40s + 8.0 t", subject.of("key").asTimePenalty(null).toString()); assertNull(subject.of("no-key").asTimePenalty(null)); } @Test - public void asZoneId() { + void asZoneId() { NodeAdapter subject = newNodeAdapterForTest( "{ key1 : 'UTC', key2 : 'Europe/Oslo', key3 : '+02:00', key4: 'invalid' }" ); @@ -515,7 +515,7 @@ public void asZoneId() { } @Test - public void asTextSet() { + void asTextSet() { NodeAdapter subject = newNodeAdapterForTest("{ ids : ['A', 'C', 'F'] }"); assertEquals( Set.of("A", "C", "F"), @@ -528,7 +528,7 @@ public void asTextSet() { } @Test - public void isNonEmptyArray() { + void isNonEmptyArray() { NodeAdapter subject = newNodeAdapterForTest("{ foo : ['A'], bar: [], foobar: true }"); assertTrue(subject.of("foo").asObject().isNonEmptyArray()); assertFalse(subject.of("bar").asObject().isNonEmptyArray()); @@ -538,13 +538,13 @@ public void isNonEmptyArray() { } @Test - public void deduplicateChildren() { + void deduplicateChildren() { NodeAdapter subject = newNodeAdapterForTest("{ foo : { enabled: true } }"); assertSame(subject.of("foo").asObject(), subject.of("foo").asObject()); } @Test - public void unusedParams() { + void unusedParams() { // Given: two parameters a and b NodeAdapter subject = newNodeAdapterForTest("{ foo : { a: true, b: false } }"); var buf = new StringBuilder(); @@ -557,6 +557,29 @@ public void unusedParams() { assertEquals("Unexpected config parameter: 'foo.b:false' in 'Test'", buf.toString()); } + @Test + void unknownParameters() { + // Given: two parameters a and b + var subject = newNodeAdapterForTest("{ foo : { a: true, b: false } }"); + + // When: Access ONLY parameter 'a', but not 'b' + subject.of("foo").asObject().of("a").asBoolean(); + + assertTrue(subject.hasUnknownParameters()); + } + + @Test + void allParametersAreKnown() { + // Given: two parameters a and b + var subject = newNodeAdapterForTest("{ foo : { a: true, b: false } }"); + + var object = subject.of("foo").asObject(); + object.of("a").asBoolean(); + object.of("b").asBoolean(); + + assertFalse(subject.hasUnknownParameters()); + } + private static String unusedParams(NodeAdapter subject) { var buf = new StringBuilder(); subject.logAllWarnings(m -> buf.append('\n').append(m)); 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 d50c8a74dfc..4a0a0d1b848 100644 --- a/src/test/java/org/opentripplanner/street/model/_data/StreetModelForTest.java +++ b/src/test/java/org/opentripplanner/street/model/_data/StreetModelForTest.java @@ -9,6 +9,7 @@ import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.service.vehiclerental.model.TestFreeFloatingRentalVehicleBuilder; import org.opentripplanner.service.vehiclerental.street.VehicleRentalPlaceVertex; import org.opentripplanner.street.model.RentalFormFactor; @@ -29,16 +30,16 @@ public class StreetModelForTest { public static StreetVertex V4 = intersectionVertex("V4", 3, 3); public static IntersectionVertex intersectionVertex(Coordinate c) { - return intersectionVertex(c.x, c.y); + return intersectionVertex(c.y, c.x); } public static IntersectionVertex intersectionVertex(double lat, double lon) { var label = "%s_%s".formatted(lat, lon); - return new LabelledIntersectionVertex(label, lat, lon, false, false); + return new LabelledIntersectionVertex(label, lon, lat, false, false); } public static IntersectionVertex intersectionVertex(String label, double lat, double lon) { - return new LabelledIntersectionVertex(label, lat, lon, false, false); + return new LabelledIntersectionVertex(label, lon, lat, false, false); } @Nonnull @@ -111,4 +112,8 @@ public static VehicleRentalPlaceVertex rentalVertex(RentalFormFactor formFactor) } return new VehicleRentalPlaceVertex(rentalVehicleBuilder.build()); } + + public static VehicleParking.VehicleParkingBuilder vehicleParking() { + return VehicleParking.builder().id(id("vehicle-parking-1")).coordinate(WgsCoordinate.GREENWICH); + } } diff --git a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java index 318f133f15e..7de7b0c7ce6 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java @@ -46,7 +46,7 @@ public class StreetEdgeTest { public void before() { v0 = intersectionVertex("maple_0th", 0.0, 0.0); // label, X, Y v1 = intersectionVertex("maple_1st", 2.0, 2.0); - v2 = intersectionVertex("maple_2nd", 1.0, 2.0); + v2 = intersectionVertex("maple_2nd", 2.0, 1.0); this.proto = StreetSearchRequest @@ -73,7 +73,7 @@ public void testInAndOutAngles() { assertEquals(90, e1.getOutAngle()); // 2 new ones - StreetVertex u = intersectionVertex("test1", 2.0, 1.0); + StreetVertex u = intersectionVertex("test1", 1.0, 2.0); StreetVertex v = intersectionVertex("test2", 2.0, 2.0); // Second edge, heading straight North diff --git a/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingEdgeTest.java b/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingEdgeTest.java index 7dad61fbd6b..81bee23ad54 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingEdgeTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingEdgeTest.java @@ -10,6 +10,7 @@ import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.street.search.request.StreetSearchRequest; import org.opentripplanner.street.search.state.State; @@ -130,8 +131,8 @@ private VehicleParking createVehicleParking( boolean carPlaces, VehicleParkingSpaces availability ) { - return VehicleParking - .builder() + return StreetModelForTest + .vehicleParking() .id(TransitModelForTest.id("VehicleParking")) .bicyclePlaces(bicyclePlaces) .carPlaces(carPlaces) diff --git a/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingPreferredTagsTest.java b/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingPreferredTagsTest.java index 5969121b6d6..579ae4e964d 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingPreferredTagsTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingPreferredTagsTest.java @@ -11,9 +11,9 @@ import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.routing.api.request.StreetMode; -import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingEntrance; import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.search.request.StreetSearchRequest; @@ -62,9 +62,8 @@ private void runTest( double expectedCost, boolean arriveBy ) { - var parking = VehicleParking - .builder() - .coordinate(COORDINATE) + var parking = StreetModelForTest + .vehicleParking() .tags(parkingTags) .availability(VehicleParkingSpaces.builder().bicycleSpaces(100).build()) .bicyclePlaces(true) diff --git a/src/test/java/org/opentripplanner/transit/speed_test/support/AssertSpeedTestSetup.java b/src/test/java/org/opentripplanner/transit/speed_test/support/AssertSpeedTestSetup.java index 031df343a9a..4113d5979b1 100644 --- a/src/test/java/org/opentripplanner/transit/speed_test/support/AssertSpeedTestSetup.java +++ b/src/test/java/org/opentripplanner/transit/speed_test/support/AssertSpeedTestSetup.java @@ -15,7 +15,7 @@ public static void assertTestDateHasData( ) { int numberOfPatternForTestDate = transitModel .getTransitLayer() - .getTripPatternsForDate(config.testDate) + .getTripPatternsForRunningDate(config.testDate) .size(); if (numberOfPatternForTestDate < 10) { diff --git a/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java b/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java index 2bcb49defae..a31b5cfb387 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java @@ -16,6 +16,7 @@ import org.opentripplanner.routing.vehicle_parking.VehicleParkingState; import org.opentripplanner.routing.vehicle_parking.VehicleParkingTestGraphData; import org.opentripplanner.routing.vehicle_parking.VehicleParkingTestUtil; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.edge.StreetVehicleParkingLink; import org.opentripplanner.street.model.edge.VehicleParkingEdge; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; @@ -142,7 +143,10 @@ void deleteVehicleParkingTest() { @Test void addNotOperatingVehicleParkingTest() { - var vehicleParking = VehicleParking.builder().state(VehicleParkingState.CLOSED).build(); + var vehicleParking = StreetModelForTest + .vehicleParking() + .state(VehicleParkingState.CLOSED) + .build(); when(dataSource.getUpdates()).thenReturn(List.of(vehicleParking)); runUpdaterOnce(); @@ -155,8 +159,8 @@ void addNotOperatingVehicleParkingTest() { void updateNotOperatingVehicleParkingTest() { var vehiclePlaces = VehicleParkingSpaces.builder().bicycleSpaces(1).build(); - var vehicleParking = VehicleParking - .builder() + var vehicleParking = StreetModelForTest + .vehicleParking() .availability(vehiclePlaces) .state(VehicleParkingState.CLOSED) .build(); @@ -175,8 +179,8 @@ void updateNotOperatingVehicleParkingTest() { vehiclePlaces = VehicleParkingSpaces.builder().bicycleSpaces(2).build(); vehicleParking = - VehicleParking - .builder() + StreetModelForTest + .vehicleParking() .availability(vehiclePlaces) .state(VehicleParkingState.CLOSED) .build(); @@ -194,7 +198,10 @@ void updateNotOperatingVehicleParkingTest() { @Test void deleteNotOperatingVehicleParkingTest() { - var vehicleParking = VehicleParking.builder().state(VehicleParkingState.CLOSED).build(); + var vehicleParking = StreetModelForTest + .vehicleParking() + .state(VehicleParkingState.CLOSED) + .build(); when(dataSource.getUpdates()).thenReturn(List.of(vehicleParking)); runUpdaterOnce(); diff --git a/src/test/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdaterTest.java b/src/test/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdaterTest.java index 09dd1cf1cb9..99910e04f7c 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdaterTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdaterTest.java @@ -8,10 +8,10 @@ import java.util.List; import org.junit.jupiter.api.Test; -import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.MultiPolygon; import org.locationtech.jts.geom.Polygon; +import org.opentripplanner._support.geometry.Polygons; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.service.vehiclerental.model.GeofencingZone; import org.opentripplanner.service.vehiclerental.street.BusinessAreaBorder; @@ -35,30 +35,16 @@ class GeofencingVertexUpdaterTest { final GeofencingVertexUpdater updater = new GeofencingVertexUpdater(ignored -> List.of(insideFrognerPark, halfInHalfOutFrognerPark, businessBorder) ); - StreetEdge outsideFrognerPark = streetEdge(outsideFrognerPark1, outsideFrognerPark2); - - GeometryFactory fac = GeometryUtils.getGeometryFactory(); - Polygon frognerPark = fac.createPolygon( - new Coordinate[] { - new Coordinate(59.93112978539807, 10.691099320272173), - new Coordinate(59.92231848097069, 10.691099320272173), - new Coordinate(59.92231848097069, 10.711758464910503), - new Coordinate(59.92231848097069, 10.691099320272173), - new Coordinate(59.93112978539807, 10.691099320272173), - } - ); - final GeofencingZone zone = new GeofencingZone(id("frogner-park"), frognerPark, true, false); - Polygon osloPolygon = fac.createPolygon( - new Coordinate[] { - new Coordinate(59.961055202323195, 10.62535658370308), - new Coordinate(59.889009435700416, 10.62535658370308), - new Coordinate(59.889009435700416, 10.849791142928694), - new Coordinate(59.961055202323195, 10.849791142928694), - new Coordinate(59.961055202323195, 10.62535658370308), - } + + static GeometryFactory fac = GeometryUtils.getGeometryFactory(); + final GeofencingZone zone = new GeofencingZone( + id("frogner-park"), + Polygons.OSLO_FROGNER_PARK, + true, + false ); - MultiPolygon osloMultiPolygon = fac.createMultiPolygon(new Polygon[] { osloPolygon }); + MultiPolygon osloMultiPolygon = fac.createMultiPolygon(new Polygon[] { Polygons.OSLO }); final GeofencingZone businessArea = new GeofencingZone( id("oslo"), osloMultiPolygon, diff --git a/src/test/resources/standalone/config/router-config.json b/src/test/resources/standalone/config/router-config.json index 3a07cddfeb8..3b43ef1c5d2 100644 --- a/src/test/resources/standalone/config/router-config.json +++ b/src/test/resources/standalone/config/router-config.json @@ -334,6 +334,12 @@ "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", @@ -414,6 +420,12 @@ "fromDateTime": "-P1D", "timeout": 300000 } + }, + { + "type": "vehicle-parking", + "feedId": "bikeep", + "sourceType": "bikeep", + "url": "https://services.bikeep.com/location/v1/public-areas/no-baia-mobility/locations" } ], "rideHailingServices": [
+ * If so it throws an exception. + */ + public void abortOnUnknownParameters() { + if ( + ( + otpConfig.hasUnknownParameters() || + buildConfig.hasUnknownParameters() || + routerConfig.hasUnknownParameters() + ) + ) { + throw new OtpAppException( + "Configuration contains unknown parameters (see above for details). " + + "Please fix your configuration or remove --abortOnUnknownConfig from your OTP CLI parameters." + ); + } + } } diff --git a/src/main/java/org/opentripplanner/standalone/config/OtpConfig.java b/src/main/java/org/opentripplanner/standalone/config/OtpConfig.java index 40a659df1ab..2918cbc3884 100644 --- a/src/main/java/org/opentripplanner/standalone/config/OtpConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/OtpConfig.java @@ -71,4 +71,11 @@ public OtpConfig(NodeAdapter nodeAdapter, boolean logUnusedParams) { root.logAllWarnings(LOG::warn); } } + + /** + * Checks if any unknown or invalid parameters were encountered while loading the configuration. + */ + public boolean hasUnknownParameters() { + return root.hasUnknownParameters(); + } } diff --git a/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java b/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java index bf97155b747..4ac0688a759 100644 --- a/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/RouterConfig.java @@ -150,4 +150,11 @@ public String toString() { // Print ONLY the values set, not default values return root.toPrettyString(); } + + /** + * Checks if any unknown or invalid parameters were encountered while loading the configuration. + */ + public boolean hasUnknownParameters() { + return root.hasUnknownParameters(); + } } 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 69d9937582b..51303ea4fc5 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 @@ -172,6 +172,13 @@ public void logAllWarnings(Consumer logger) { allWarnings().forEach(logger); } + /** + * Checks if any unknown or invalid properties were encountered while loading the configuration. + */ + public boolean hasUnknownParameters() { + return !unusedParams().isEmpty(); + } + /** * Be careful when using this method - this bypasses the NodeAdaptor, and we loose * track of unused parameters and cannot generate documentation for the children. 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 6caf9082cff..70b8e261ee4 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 @@ -10,7 +10,8 @@ public enum OtpVersion { V2_2("2.2"), V2_3("2.3"), V2_4("2.4"), - V2_5("2.5"); + V2_5("2.5"), + V2_6("2.6"); private final String text; diff --git a/src/main/java/org/opentripplanner/standalone/config/framework/json/ParameterBuilder.java b/src/main/java/org/opentripplanner/standalone/config/framework/json/ParameterBuilder.java index 088a7794585..3ecc4d484cb 100644 --- a/src/main/java/org/opentripplanner/standalone/config/framework/json/ParameterBuilder.java +++ b/src/main/java/org/opentripplanner/standalone/config/framework/json/ParameterBuilder.java @@ -120,10 +120,6 @@ public Boolean asBoolean(boolean defaultValue) { return ofOptional(BOOLEAN, defaultValue, JsonNode::asBoolean); } - public Map asBooleanMap() { - return ofOptionalMap(BOOLEAN, JsonNode::asBoolean); - } - /** @throws OtpAppException if parameter is missing. */ public double asDouble() { return ofRequired(DOUBLE).asDouble(); 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 e808ad6905c..c687899009f 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 @@ -2,13 +2,16 @@ import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_2; import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_3; +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_6; import java.time.Duration; import java.time.ZoneId; import java.util.ArrayList; import java.util.Set; +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.standalone.config.framework.json.NodeAdapter; import org.opentripplanner.updater.vehicle_parking.VehicleParkingSourceType; @@ -25,7 +28,7 @@ public static VehicleParkingUpdaterParameters create(String updaterRef, NodeAdap var feedId = c .of("feedId") .since(V2_2) - .summary("The name of the data source.") + .summary("The id of the data source, which will be the prefix of the parking lot's id.") .description("This will end up in the API responses as the feed id of of the parking lot.") .asString(); return switch (sourceType) { @@ -50,7 +53,7 @@ public static VehicleParkingUpdaterParameters create(String updaterRef, NodeAdap ); case PARK_API, BICYCLE_PARK_API -> new ParkAPIUpdaterParameters( updaterRef, - c.of("url").since(V2_2).summary("URL of the resource.").asString(null), + c.of("url").since(V2_2).summary("URL of the resource.").asString(), feedId, c .of("frequency") @@ -66,7 +69,7 @@ public static VehicleParkingUpdaterParameters create(String updaterRef, NodeAdap ); case BIKELY -> new BikelyUpdaterParameters( updaterRef, - c.of("url").since(V2_3).summary("URL of the locations endpoint.").asUri(null), + c.of("url").since(V2_3).summary("URL of the locations endpoint.").asUri(), feedId, c .of("frequency") @@ -75,6 +78,28 @@ 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(), + feedId, + c + .of("frequency") + .since(V2_6) + .summary("How often to update the source.") + .asDuration(Duration.ofMinutes(1)), + HttpHeadersConfig.headers(c, V2_6) + ); }; } 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 e44994d1105..35278d8fbd8 100644 --- a/src/main/java/org/opentripplanner/transit/model/basic/Money.java +++ b/src/main/java/org/opentripplanner/transit/model/basic/Money.java @@ -206,4 +206,9 @@ public boolean equals(Object obj) { return false; } } + + @Override + public int hashCode() { + return Objects.hash(currency, amount); + } } 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 e0214fd9d57..a956fda0d87 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingDataSourceFactory.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingDataSourceFactory.java @@ -1,9 +1,13 @@ package org.opentripplanner.updater.vehicle_parking; +import org.opentripplanner.ext.vehicleparking.bikeep.BikeepUpdater; +import org.opentripplanner.ext.vehicleparking.bikeep.BikeepUpdaterParameters; import org.opentripplanner.ext.vehicleparking.bikely.BikelyUpdater; 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; @@ -36,6 +40,8 @@ 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); }; } } 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 583bc9afc99..f6a28177d8e 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingSourceType.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingSourceType.java @@ -5,4 +5,6 @@ public enum VehicleParkingSourceType { BICYCLE_PARK_API, HSL_PARK, BIKELY, + NOI_OPEN_DATA_HUB, + BIKEEP, } diff --git a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java index 3d2e9b21e29..185de6bddb7 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java @@ -8,6 +8,7 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; +import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.linking.DisposableEdgeCollection; import org.opentripplanner.routing.linking.LinkingDirection; @@ -68,7 +69,7 @@ public void setGraphUpdaterManager(WriteToGraphCallback saveResultOnGraph) { } @Override - protected void runPolling() throws Exception { + protected void runPolling() { LOG.debug("Updating vehicle parkings from {}", source); if (!source.update()) { LOG.debug("No updates"); @@ -239,4 +240,9 @@ private void removeVehicleParkingEdgesFromGraph( graph.remove(entranceVertex); } } + + @Override + public String toString() { + return ToStringBuilder.of(this.getClass()).addObj("source", source).toString(); + } } diff --git a/src/test/java/org/opentripplanner/_support/geometry/Coordinates.java b/src/test/java/org/opentripplanner/_support/geometry/Coordinates.java index 3e5bddd9f94..5a4526012c9 100644 --- a/src/test/java/org/opentripplanner/_support/geometry/Coordinates.java +++ b/src/test/java/org/opentripplanner/_support/geometry/Coordinates.java @@ -4,9 +4,18 @@ public class Coordinates { - public static final Coordinate BERLIN = new Coordinate(52.5212, 13.4105); - public static final Coordinate BERLIN_BRANDENBURG_GATE = new Coordinate(52.51627, 13.37770); - public static final Coordinate HAMBURG = new Coordinate(53.5566, 10.0003); - public static final Coordinate KONGSBERG_PLATFORM_1 = new Coordinate(59.67216, 9.65107); - public static final Coordinate BOSTON = new Coordinate(42.36541, -71.06129); + public static final Coordinate BERLIN = of(52.5212, 13.4105); + public static final Coordinate BERLIN_BRANDENBURG_GATE = of(52.51627, 13.37770); + public static final Coordinate HAMBURG = of(53.5566, 10.0003); + public static final Coordinate KONGSBERG_PLATFORM_1 = of(59.67216, 9.65107); + public static final Coordinate BOSTON = of(42.36541, -71.06129); + + /** + * Because it is a frequent mistake to swap x/y and longitude/latitude when + * constructing JTS Coordinates, this static factory method makes is clear + * which is which. + */ + public static Coordinate of(double latitude, double longitude) { + return new Coordinate(longitude, latitude); + } } diff --git a/src/test/java/org/opentripplanner/_support/geometry/Polygons.java b/src/test/java/org/opentripplanner/_support/geometry/Polygons.java index efedd74499c..a386d8a27e1 100644 --- a/src/test/java/org/opentripplanner/_support/geometry/Polygons.java +++ b/src/test/java/org/opentripplanner/_support/geometry/Polygons.java @@ -3,29 +3,65 @@ import java.util.Arrays; import org.geojson.LngLatAlt; import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.Polygon; import org.opentripplanner.framework.geometry.GeometryUtils; public class Polygons { - public static final Polygon BERLIN = GeometryUtils - .getGeometryFactory() - .createPolygon( - new Coordinate[] { - new Coordinate(52.616841, 13.224692810), - new Coordinate(52.6168419, 13.224692810), - new Coordinate(52.3915238, 13.646107734), - new Coordinate(52.616841, 13.646107734), - new Coordinate(52.616841, 13.224692810), - } - ); + private static final GeometryFactory FAC = GeometryUtils.getGeometryFactory(); + public static final Polygon BERLIN = FAC.createPolygon( + new Coordinate[] { + Coordinates.of(52.616841, 13.224692810), + Coordinates.of(52.616841, 13.646107734), + Coordinates.of(52.3915238, 13.646107734), + Coordinates.of(52.396421, 13.2268067), + Coordinates.of(52.616841, 13.224692810), + } + ); + + public static Polygon OSLO = FAC.createPolygon( + new Coordinate[] { + Coordinates.of(59.961055202323195, 10.62535658370308), + Coordinates.of(59.889009435700416, 10.62535658370308), + Coordinates.of(59.889009435700416, 10.849791142928694), + Coordinates.of(59.961055202323195, 10.849791142928694), + Coordinates.of(59.961055202323195, 10.62535658370308), + } + ); + public static Polygon OSLO_FROGNER_PARK = FAC.createPolygon( + new Coordinate[] { + Coordinates.of(59.92939032560119, 10.69770054003061), + Coordinates.of(59.929138466684975, 10.695210909925208), + Coordinates.of(59.92745319808358, 10.692696865071184), + Coordinates.of(59.92709930323093, 10.693774304996225), + Coordinates.of(59.92745914988427, 10.69495957972947), + Coordinates.of(59.92736919590291, 10.697664535925895), + Coordinates.of(59.924837887427856, 10.697927604125255), + Coordinates.of(59.924447953413335, 10.697448767354985), + Coordinates.of(59.92378800804022, 10.697819761729818), + Coordinates.of(59.92329018587293, 10.699196446969069), + Coordinates.of(59.92347619027632, 10.700285749621997), + Coordinates.of(59.92272030268688, 10.704714696822037), + Coordinates.of(59.92597766029715, 10.71001707489603), + Coordinates.of(59.92676341291536, 10.707838597058043), + Coordinates.of(59.92790300889098, 10.708389137506913), + Coordinates.of(59.928376832499424, 10.707060536853078), + Coordinates.of(59.92831087551576, 10.705803789539402), + Coordinates.of(59.92953431964068, 10.706641515204467), + Coordinates.of(59.93046383654274, 10.70484606360543), + Coordinates.of(59.93008590667682, 10.701817874860211), + Coordinates.of(59.93028982601595, 10.700525251174469), + Coordinates.of(59.92939032560119, 10.69770054003061), + } + ); public static org.geojson.Polygon toGeoJson(Polygon polygon) { var ret = new org.geojson.Polygon(); var coordinates = Arrays .stream(polygon.getCoordinates()) - .map(c -> new LngLatAlt(c.y, c.x)) + .map(c -> new LngLatAlt(c.x, c.y)) .toList(); ret.add(coordinates); diff --git a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java index 2f1e57ef4dc..fcc59af845f 100644 --- a/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java +++ b/src/test/java/org/opentripplanner/apis/gtfs/GraphQLIntegrationTest.java @@ -131,6 +131,7 @@ static void setup() { VehicleParking .builder() .id(id("parking-1")) + .coordinate(WgsCoordinate.GREENWICH) .name(NonLocalizedString.ofNullable("parking")) .build() ), diff --git a/src/test/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapperTest.java b/src/test/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapperTest.java index 160014213d3..b8d08c7d7f5 100644 --- a/src/test/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapperTest.java +++ b/src/test/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapperTest.java @@ -13,9 +13,11 @@ class RequestModesMapperTest { void testMapRequestModesEmptyMapReturnsDefaults() { Map inputModes = Map.of(); + RequestModes wantModes = RequestModes.of().withDirectMode(null).build(); + RequestModes mappedModes = RequestModesMapper.mapRequestModes(inputModes); - assertEquals(RequestModes.of().build(), mappedModes); + assertEquals(wantModes, mappedModes); } @Test @@ -26,6 +28,7 @@ void testMapRequestModesAccessSetReturnsDefaultsForOthers() { .of() .withAccessMode(StreetMode.BIKE) .withTransferMode(StreetMode.BIKE) + .withDirectMode(null) .build(); RequestModes mappedModes = RequestModesMapper.mapRequestModes(inputModes); @@ -37,7 +40,11 @@ void testMapRequestModesAccessSetReturnsDefaultsForOthers() { void testMapRequestModesEgressSetReturnsDefaultsForOthers() { Map inputModes = Map.of("egressMode", StreetMode.CAR); - RequestModes wantModes = RequestModes.of().withEgressMode(StreetMode.CAR).build(); + RequestModes wantModes = RequestModes + .of() + .withEgressMode(StreetMode.CAR) + .withDirectMode(null) + .build(); RequestModes mappedModes = RequestModesMapper.mapRequestModes(inputModes); diff --git a/src/test/java/org/opentripplanner/framework/geometry/WgsCoordinateTest.java b/src/test/java/org/opentripplanner/framework/geometry/WgsCoordinateTest.java index 05051ab7dff..5cd7ac66123 100644 --- a/src/test/java/org/opentripplanner/framework/geometry/WgsCoordinateTest.java +++ b/src/test/java/org/opentripplanner/framework/geometry/WgsCoordinateTest.java @@ -109,15 +109,15 @@ void testGreenwich() { void roundingTo10m() { var hamburg = new WgsCoordinate(Coordinates.HAMBURG); var rounded = hamburg.roundToApproximate10m(); - assertEquals(10.0003, rounded.latitude()); - assertEquals(53.5566, rounded.longitude()); + assertEquals(10.0003, rounded.longitude()); + assertEquals(53.5566, rounded.latitude()); } @Test void roundingTo100m() { var hamburg = new WgsCoordinate(Coordinates.HAMBURG); var rounded = hamburg.roundToApproximate100m(); - assertEquals(10, rounded.latitude()); - assertEquals(53.557, rounded.longitude()); + assertEquals(10, rounded.longitude()); + assertEquals(53.557, rounded.latitude()); } } diff --git a/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java b/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java index 96e71b9d3ec..4d2141eb38e 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/StreetLinkerModuleTest.java @@ -102,12 +102,12 @@ private static class TestModel { public TestModel() { var from = StreetModelForTest.intersectionVertex( - KONGSBERG_PLATFORM_1.x - DELTA, - KONGSBERG_PLATFORM_1.y - DELTA + KONGSBERG_PLATFORM_1.y - DELTA, + KONGSBERG_PLATFORM_1.x - DELTA ); var to = StreetModelForTest.intersectionVertex( - KONGSBERG_PLATFORM_1.x + DELTA, - KONGSBERG_PLATFORM_1.y + DELTA + KONGSBERG_PLATFORM_1.y + DELTA, + KONGSBERG_PLATFORM_1.x + DELTA ); Graph graph = new Graph(); diff --git a/src/test/java/org/opentripplanner/graph_builder/module/VehicleParkingLinkingTest.java b/src/test/java/org/opentripplanner/graph_builder/module/VehicleParkingLinkingTest.java index 95f4df79b7b..23f4ffc72ca 100644 --- a/src/test/java/org/opentripplanner/graph_builder/module/VehicleParkingLinkingTest.java +++ b/src/test/java/org/opentripplanner/graph_builder/module/VehicleParkingLinkingTest.java @@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingHelper; import org.opentripplanner.routing.vehicle_parking.VehicleParkingTestGraphData; import org.opentripplanner.street.model.StreetTraversalPermission; @@ -46,8 +45,8 @@ public void setup() { @Test public void entranceWithVertexLinkingTest() { - var parking = VehicleParking - .builder() + var parking = StreetModelForTest + .vehicleParking() .entrance(builder -> builder.entranceId(id("1")).coordinate(new WgsCoordinate(A.getCoordinate())).vertex(A) ) @@ -65,8 +64,8 @@ public void entranceWithVertexLinkingTest() { @Test public void entranceWithoutVertexLinkingTest() { - var parking = VehicleParking - .builder() + var parking = StreetModelForTest + .vehicleParking() .entrance(builder -> builder .entranceId(id("1")) @@ -99,8 +98,8 @@ public void carParkingEntranceToAllTraversableStreetLinkingTest() { StreetModelForTest.streetEdge(A, C, StreetTraversalPermission.NONE); - var parking = VehicleParking - .builder() + var parking = StreetModelForTest + .vehicleParking() .entrance(builder -> builder .entranceId(id("1")) @@ -123,9 +122,8 @@ public void carParkingEntranceToAllTraversableStreetLinkingTest() { @Test public void removeEntranceWithNonExistingVertexTest() { - var vehicleParking = VehicleParking - .builder() - .id(id("VP")) + var vehicleParking = StreetModelForTest + .vehicleParking() .bicyclePlaces(true) .entrance(builder -> builder @@ -159,9 +157,8 @@ public void removeEntranceWithNonExistingVertexTest() { @Test public void removeVehicleParkingWithOneEntranceAndNonExistingVertexTest() { - var vehicleParking = VehicleParking - .builder() - .id(id("VP")) + var vehicleParking = StreetModelForTest + .vehicleParking() .bicyclePlaces(true) .entrance(builder -> builder diff --git a/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/SubgraphOnlyFerryTest.java b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/SubgraphOnlyFerryTest.java new file mode 100644 index 00000000000..26aaf12896b --- /dev/null +++ b/src/test/java/org/opentripplanner/graph_builder/module/islandpruning/SubgraphOnlyFerryTest.java @@ -0,0 +1,112 @@ +package org.opentripplanner.graph_builder.module.islandpruning; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.opentripplanner.street.model.vertex.TransitStopVertex; +import org.opentripplanner.street.model.vertex.TransitStopVertexBuilder; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.basic.TransitMode; +import org.opentripplanner.transit.model.site.RegularStop; + +class SubgraphOnlyFerryTest { + + private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + private static final RegularStop regularStop1 = TEST_MODEL.stop("TEST-1").build(); + private static final RegularStop regularStop2 = TEST_MODEL.stop("TEST-2").build(); + + @Test + void subgraphHasOnlyFerry() { + TransitStopVertex transitStopVertex = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of(TransitMode.FERRY)) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex); + + assertTrue(subgraph.hasOnlyFerryStops()); + } + + @Test + void subgraphHasOnlyNoFerry() { + TransitStopVertex transitStopVertex1 = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of(TransitMode.BUS)) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex1); + + assertFalse(subgraph.hasOnlyFerryStops()); + } + + @Test + void subgraphHasOnlyNoMode() { + TransitStopVertex transitStopVertex1 = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of()) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex1); + + assertFalse(subgraph.hasOnlyFerryStops()); + } + + @Test + void subgraphHasOnlyFerryMoreStops() { + TransitStopVertex transitStopVertex1 = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of(TransitMode.FERRY)) + .build(); + TransitStopVertex transitStopVertex2 = new TransitStopVertexBuilder() + .withStop(regularStop2) + .withModes(Set.of(TransitMode.FERRY)) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex1); + subgraph.addVertex(transitStopVertex2); + + assertTrue(subgraph.hasOnlyFerryStops()); + } + + @Test + void subgraphHasNotOnlyFerryMoreStops() { + TransitStopVertex transitStopVertex1 = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of(TransitMode.FERRY)) + .build(); + TransitStopVertex transitStopVertex2 = new TransitStopVertexBuilder() + .withStop(regularStop2) + .withModes(Set.of(TransitMode.BUS)) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex1); + subgraph.addVertex(transitStopVertex2); + + assertFalse(subgraph.hasOnlyFerryStops()); + } + + @Test + void subgraphHasNoModeMoreStops() { + TransitStopVertex transitStopVertex1 = new TransitStopVertexBuilder() + .withStop(regularStop1) + .withModes(Set.of(TransitMode.FERRY)) + .build(); + TransitStopVertex transitStopVertex2 = new TransitStopVertexBuilder() + .withStop(regularStop2) + .withModes(Set.of()) + .build(); + + Subgraph subgraph = new Subgraph(); + subgraph.addVertex(transitStopVertex1); + subgraph.addVertex(transitStopVertex2); + + assertFalse(subgraph.hasOnlyFerryStops()); + } +} diff --git a/src/test/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializerTest.java b/src/test/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializerTest.java index da295073e01..2d7df788ad3 100644 --- a/src/test/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializerTest.java +++ b/src/test/java/org/opentripplanner/model/plan/legreference/LegReferenceSerializerTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import java.time.LocalDate; import org.junit.jupiter.api.Test; @@ -86,4 +87,19 @@ void testScheduledTransitLegReferenceLegacyV2Deserialize() { assertEquals(FROM_STOP_POS, ref.fromStopPositionInPattern()); assertEquals(TO_STOP_POS, ref.toStopPositionInPattern()); } + + @Test + void testNullSerializedLegReference() { + assertNull(LegReferenceSerializer.decode(null)); + } + + @Test + void testEmptySerializedLegReference() { + assertNull(LegReferenceSerializer.decode("")); + } + + @Test + void testIllegalBase64CharacterInSerializedLegReference() { + assertNull(LegReferenceSerializer.decode("RUT:Line:5")); + } } diff --git a/src/test/java/org/opentripplanner/netex/mapping/NetexTestDataSample.java b/src/test/java/org/opentripplanner/netex/mapping/NetexTestDataSample.java index b8723fdbcf3..b72c9b05277 100644 --- a/src/test/java/org/opentripplanner/netex/mapping/NetexTestDataSample.java +++ b/src/test/java/org/opentripplanner/netex/mapping/NetexTestDataSample.java @@ -40,6 +40,7 @@ import org.rutebanken.netex.model.ScheduledStopPointRefStructure; import org.rutebanken.netex.model.ServiceAlterationEnumeration; import org.rutebanken.netex.model.ServiceJourney; +import org.rutebanken.netex.model.ServiceJourneyRefStructure; import org.rutebanken.netex.model.StopPointInJourneyPattern; import org.rutebanken.netex.model.StopPointInJourneyPatternRefStructure; import org.rutebanken.netex.model.TimetabledPassingTime; @@ -50,9 +51,11 @@ public class NetexTestDataSample { public static final String SERVICE_JOURNEY_ID = "RUT:ServiceJourney:1"; + public static final String DATED_SERVICE_JOURNEY_ID_1 = "RUT:DatedServiceJourney:1"; + public static final String DATED_SERVICE_JOURNEY_ID_2 = "RUT:DatedServiceJourney:2"; public static final List DATED_SERVICE_JOURNEY_ID = List.of( - "RUT:DatedServiceJourney:1", - "RUT:DatedServiceJourney:2" + DATED_SERVICE_JOURNEY_ID_1, + DATED_SERVICE_JOURNEY_ID_2 ); public static final List OPERATING_DAYS = List.of("2022-02-28", "2022-02-29"); private static final DayType EVERYDAY = new DayType() @@ -174,6 +177,11 @@ public NetexTestDataSample() { DatedServiceJourney datedServiceJourney = new DatedServiceJourney() .withId(DATED_SERVICE_JOURNEY_ID.get(i)) + .withJourneyRef( + List.of( + MappingSupport.createWrappedRef(SERVICE_JOURNEY_ID, ServiceJourneyRefStructure.class) + ) + ) .withServiceAlteration(ServiceAlterationEnumeration.PLANNED) .withOperatingDayRef(new OperatingDayRefStructure().withRef(operatingDay.getId())); @@ -218,6 +226,15 @@ public HierarchicalMapById getServiceJourneyById() { return serviceJourneyById; } + public DatedServiceJourney getDatedServiceJourneyById(String id) { + return datedServiceJourneyBySjId + .values() + .stream() + .filter(datedServiceJourney -> datedServiceJourney.getId().equals(id)) + .findFirst() + .orElse(null); + } + public ServiceJourney getServiceJourney() { return serviceJourneyById.lookup(SERVICE_JOURNEY_ID); } diff --git a/src/test/java/org/opentripplanner/netex/mapping/TripPatternMapperTest.java b/src/test/java/org/opentripplanner/netex/mapping/TripPatternMapperTest.java index 74448fb3638..95af8f623e5 100644 --- a/src/test/java/org/opentripplanner/netex/mapping/TripPatternMapperTest.java +++ b/src/test/java/org/opentripplanner/netex/mapping/TripPatternMapperTest.java @@ -1,9 +1,12 @@ package org.opentripplanner.netex.mapping; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import com.google.common.collect.ArrayListMultimap; +import java.util.List; import java.util.Map; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -19,17 +22,19 @@ import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; import org.rutebanken.netex.model.DatedServiceJourney; +import org.rutebanken.netex.model.DatedServiceJourneyRefStructure; import org.rutebanken.netex.model.OperatingDay; +import org.rutebanken.netex.model.ServiceAlterationEnumeration; /** * @author Thomas Gran (Capra) - tgr@capraconsulting.no (29.11.2017) */ -public class TripPatternMapperTest { +class TripPatternMapperTest { private static final FeedScopedId SERVICE_ID = TransitModelForTest.id("S01"); @Test - public void testMapTripPattern() { + void testMapTripPattern() { NetexTestDataSample sample = new NetexTestDataSample(); TripPatternMapper tripPatternMapper = new TripPatternMapper( @@ -88,9 +93,85 @@ public void testMapTripPattern() { } @Test - public void testMapTripPattern_datedServiceJourney() { + void testMapTripPattern_datedServiceJourney() { NetexTestDataSample sample = new NetexTestDataSample(); + Optional res = mapTripPattern(sample); + assertTrue(res.isPresent()); + + var r = res.get(); + + assertEquals(2, r.tripOnServiceDates().size()); + + Trip trip = r.tripPattern().scheduledTripsAsStream().findFirst().get(); + + for (TripOnServiceDate tripOnServiceDate : r.tripOnServiceDates()) { + assertEquals(trip, tripOnServiceDate.getTrip()); + assertEquals(TripAlteration.PLANNED, tripOnServiceDate.getTripAlteration()); + assertEquals( + 1, + sample + .getOperatingDaysById() + .localValues() + .stream() + .map(OperatingDay::getId) + .filter(id -> id.equals(tripOnServiceDate.getServiceDate().toString())) + .count() + ); + } + } + + @Test + void testDatedServiceJourneyReplacement() { + NetexTestDataSample sample = new NetexTestDataSample(); + DatedServiceJourney dsjReplaced = sample.getDatedServiceJourneyById( + NetexTestDataSample.DATED_SERVICE_JOURNEY_ID_1 + ); + dsjReplaced.setServiceAlteration(ServiceAlterationEnumeration.REPLACED); + DatedServiceJourney dsjReplacing = sample.getDatedServiceJourneyById( + NetexTestDataSample.DATED_SERVICE_JOURNEY_ID_2 + ); + dsjReplacing.withJourneyRef( + List.of( + MappingSupport.createWrappedRef(dsjReplaced.getId(), DatedServiceJourneyRefStructure.class) + ) + ); + Optional res = mapTripPattern(sample); + + assertTrue(res.isPresent()); + var r = res.get(); + Optional replacedTripOnServiceDate = r + .tripOnServiceDates() + .stream() + .filter(tripOnServiceDate -> + NetexTestDataSample.DATED_SERVICE_JOURNEY_ID_1.equals(tripOnServiceDate.getId().getId()) + ) + .findFirst(); + + assertTrue(replacedTripOnServiceDate.isPresent()); + assertEquals(TripAlteration.REPLACED, replacedTripOnServiceDate.get().getTripAlteration()); + + Optional replacingTripOnServiceDate = r + .tripOnServiceDates() + .stream() + .filter(tripOnServiceDate -> + NetexTestDataSample.DATED_SERVICE_JOURNEY_ID_2.equals(tripOnServiceDate.getId().getId()) + ) + .findFirst(); + + assertTrue(replacingTripOnServiceDate.isPresent()); + assertEquals(TripAlteration.PLANNED, replacingTripOnServiceDate.get().getTripAlteration()); + assertFalse(replacingTripOnServiceDate.get().getReplacementFor().isEmpty()); + + // the replaced trip should refer to the same object (object identity) whether it is accessed + // directly from the replaced DSJ or indirectly through the replacing DSJ. + assertSame( + replacingTripOnServiceDate.get().getReplacementFor().getFirst().getTrip(), + replacedTripOnServiceDate.get().getTrip() + ); + } + + private static Optional mapTripPattern(NetexTestDataSample sample) { HierarchicalMapById datedServiceJourneys = new HierarchicalMapById<>(); datedServiceJourneys.addAll(sample.getDatedServiceJourneyBySjId().values()); @@ -121,28 +202,6 @@ public void testMapTripPattern_datedServiceJourney() { Optional res = tripPatternMapper.mapTripPattern( sample.getJourneyPattern() ); - - assertTrue(res.isPresent()); - - var r = res.get(); - - assertEquals(2, r.tripOnServiceDates().size()); - - Trip trip = r.tripPattern().scheduledTripsAsStream().findFirst().get(); - - for (TripOnServiceDate tripOnServiceDate : r.tripOnServiceDates()) { - assertEquals(trip, tripOnServiceDate.getTrip()); - assertEquals(TripAlteration.PLANNED, tripOnServiceDate.getTripAlteration()); - assertEquals( - 1, - sample - .getOperatingDaysById() - .localValues() - .stream() - .map(OperatingDay::getId) - .filter(id -> id.equals(tripOnServiceDate.getServiceDate().toString())) - .count() - ); - } + return res; } } diff --git a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java index c316793ad8c..c0af4cf2701 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWayTest.java @@ -92,21 +92,6 @@ void testIsOneWayBicycle() { assertTrue(way.isOneWayReverseBicycle()); } - @Test - void testIsOneDirectionSidepath() { - OSMWay way = new OSMWay(); - assertFalse(way.isForwardDirectionSidepath()); - assertFalse(way.isReverseDirectionSidepath()); - - way.addTag("bicycle:forward", "use_sidepath"); - assertTrue(way.isForwardDirectionSidepath()); - assertFalse(way.isReverseDirectionSidepath()); - - way.addTag("bicycle:backward", "use_sidepath"); - assertTrue(way.isForwardDirectionSidepath()); - assertTrue(way.isReverseDirectionSidepath()); - } - @Test void testIsOpposableCycleway() { OSMWay way = new OSMWay(); diff --git a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java index ca5db77df12..aefd287cac8 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/model/OSMWithTagsTest.java @@ -136,7 +136,7 @@ void testBicycleDenied() { assertFalse(tags.isBicycleExplicitlyDenied(), "bicycle=" + allowedValue); } - for (var deniedValue : List.of("no", "dismount", "license", "use_sidepath")) { + for (var deniedValue : List.of("no", "dismount", "license")) { tags.addTag("bicycle", deniedValue); assertTrue(tags.isBicycleExplicitlyDenied(), "bicycle=" + deniedValue); } diff --git a/src/test/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapperTest.java b/src/test/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapperTest.java index 9a16f6a8e2e..2a8988dda61 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapperTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/tagmapping/DefaultMapperTest.java @@ -147,6 +147,48 @@ void bicycleDiscouraged() { assertEquals(2.94, discouragedProps.bicycleSafety().forward(), epsilon); } + @Test + void footUseSidepath() { + var regular = WayTestData.pedestrianTunnel(); + var props = wps.getDataForWay(regular); + assertEquals(PEDESTRIAN_AND_BICYCLE, props.getPermission()); + assertEquals(1, props.walkSafety().forward()); + + var useSidepath = WayTestData.pedestrianTunnel().addTag("foot", "use_sidepath"); + var useSidepathProps = wps.getDataForWay(useSidepath); + assertEquals(PEDESTRIAN_AND_BICYCLE, useSidepathProps.getPermission()); + assertEquals(5, useSidepathProps.walkSafety().forward()); + } + + @Test + void bicycleUseSidepath() { + var regular = WayTestData.southeastLaBonitaWay(); + var props = wps.getDataForWay(regular); + assertEquals(ALL, props.getPermission()); + assertEquals(.98, props.bicycleSafety().forward()); + + var useSidepath = WayTestData.southeastLaBonitaWay().addTag("bicycle", "use_sidepath"); + var useSidepathProps = wps.getDataForWay(useSidepath); + assertEquals(ALL, useSidepathProps.getPermission()); + assertEquals(4.9, useSidepathProps.bicycleSafety().forward(), epsilon); + + var useSidepathForward = WayTestData + .southeastLaBonitaWay() + .addTag("bicycle:forward", "use_sidepath"); + var useSidepathForwardProps = wps.getDataForWay(useSidepathForward); + assertEquals(ALL, useSidepathForwardProps.getPermission()); + assertEquals(4.9, useSidepathForwardProps.bicycleSafety().forward(), epsilon); + assertEquals(0.98, useSidepathForwardProps.bicycleSafety().back(), epsilon); + + var useSidepathBackward = WayTestData + .southeastLaBonitaWay() + .addTag("bicycle:backward", "use_sidepath"); + var useSidepathBackwardProps = wps.getDataForWay(useSidepathBackward); + assertEquals(ALL, useSidepathBackwardProps.getPermission()); + assertEquals(0.98, useSidepathBackwardProps.bicycleSafety().forward(), epsilon); + assertEquals(4.9, useSidepathBackwardProps.bicycleSafety().back(), epsilon); + } + /** * Test that two values are within epsilon of each other. */ diff --git a/src/test/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapperTest.java b/src/test/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapperTest.java index a2f84873f20..4dd52195acb 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapperTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/tagmapping/FinlandMapperTest.java @@ -162,6 +162,13 @@ public void testSafetyWithMixins() { wps.getDataForWay(wayWithMixinsAndCustomSafety).walkSafety().forward(), epsilon ); + + OSMWithTags wayWithBicycleSidePath = new OSMWithTags(); + wayWithBicycleSidePath.addTag("bicycle", "use_sidepath"); + assertEquals(8, wps.getDataForWay(wayWithBicycleSidePath).walkSafety().forward(), epsilon); + OSMWithTags wayWithFootSidePath = new OSMWithTags(); + wayWithFootSidePath.addTag("foot", "use_sidepath"); + assertEquals(8, wps.getDataForWay(wayWithFootSidePath).walkSafety().forward(), epsilon); } @Test diff --git a/src/test/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySetTest.java b/src/test/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySetTest.java index e8f5b6e51d3..24360539b6f 100644 --- a/src/test/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySetTest.java +++ b/src/test/java/org/opentripplanner/openstreetmap/wayproperty/WayPropertySetTest.java @@ -256,65 +256,6 @@ void testMotorVehicleTagDeniedPermissions() { assertTrue(permissionPair.main().allowsNothing());*/ } - @Test - void testSidepathPermissions() { - OSMWay way = new OSMWay(); - way.addTag("bicycle", "use_sidepath"); - way.addTag("highway", "primary"); - way.addTag("lanes", "2"); - way.addTag("maxspeed", "70"); - way.addTag("oneway", "yes"); - var permissionPair = getWayProperties(way); - - assertFalse(permissionPair.main().allows(StreetTraversalPermission.BICYCLE)); - assertFalse(permissionPair.back().allows(StreetTraversalPermission.BICYCLE)); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.CAR)); - assertFalse(permissionPair.back().allows(StreetTraversalPermission.CAR)); - - way = new OSMWay(); - way.addTag("bicycle:forward", "use_sidepath"); - way.addTag("highway", "tertiary"); - permissionPair = getWayProperties(way); - - assertFalse(permissionPair.main().allows(StreetTraversalPermission.BICYCLE)); - assertTrue(permissionPair.back().allows(StreetTraversalPermission.BICYCLE)); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.CAR)); - assertTrue(permissionPair.back().allows(StreetTraversalPermission.CAR)); - - way = new OSMWay(); - way.addTag("bicycle:backward", "use_sidepath"); - way.addTag("highway", "tertiary"); - permissionPair = getWayProperties(way); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.BICYCLE)); - assertFalse(permissionPair.back().allows(StreetTraversalPermission.BICYCLE)); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.CAR)); - assertTrue(permissionPair.back().allows(StreetTraversalPermission.CAR)); - - way = new OSMWay(); - way.addTag("highway", "tertiary"); - way.addTag("oneway", "yes"); - way.addTag("oneway:bicycle", "no"); - permissionPair = getWayProperties(way); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.BICYCLE)); - assertTrue(permissionPair.back().allows(StreetTraversalPermission.BICYCLE)); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.CAR)); - assertFalse(permissionPair.back().allows(StreetTraversalPermission.CAR)); - - way.addTag("bicycle:forward", "use_sidepath"); - permissionPair = getWayProperties(way); - assertFalse(permissionPair.main().allows(StreetTraversalPermission.BICYCLE)); - assertTrue(permissionPair.back().allows(StreetTraversalPermission.BICYCLE)); - - assertTrue(permissionPair.main().allows(StreetTraversalPermission.CAR)); - assertFalse(permissionPair.back().allows(StreetTraversalPermission.CAR)); - } - private StreetTraversalPermissionPair getWayProperties(OSMWay way) { WayPropertySet wayPropertySet = new WayPropertySet(); WayProperties wayData = wayPropertySet.getDataForWay(way); diff --git a/src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java b/src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java index 0bca83be8bf..78489508dc4 100644 --- a/src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java +++ b/src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java @@ -60,6 +60,8 @@ public interface RaptorTestConstants { int STOP_L = 12; int STOP_M = 13; + int NUM_STOPS = 14; + // Stop position in pattern int STOP_POS_0 = 0; int STOP_POS_1 = 1; 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 2a77996b47b..037c345bc23 100644 --- a/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java +++ b/src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java @@ -66,6 +66,8 @@ public class TestTransitData private final List constrainedTransfers = new ArrayList<>(); private final GeneralizedCostParametersBuilder costParamsBuilder = GeneralizedCostParameters.of(); + private final int[] stopBoardAlightCosts = new int[NUM_STOPS]; + private RaptorSlackProvider slackProvider = SLACK_PROVIDER; @Override @@ -300,6 +302,11 @@ public TestTransitData withConstrainedTransfer( return this; } + public TestTransitData withStopBoardAlightCost(int stop, int boardAlightCost) { + stopBoardAlightCosts[stop] = boardAlightCost; + return this; + } + public GeneralizedCostParametersBuilder mcCostParamsBuilder() { return costParamsBuilder; } @@ -352,7 +359,7 @@ public List getPatterns() { private int[] stopBoardAlightCost() { // Not implemented, no test for this yet. - return null; + return stopBoardAlightCosts; } private void expandNumOfStops(int stopIndex) { diff --git a/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopTransferCostTest.java b/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopTransferCostTest.java new file mode 100644 index 00000000000..21c1dd2a755 --- /dev/null +++ b/src/test/java/org/opentripplanner/raptor/moduletests/B05_EgressStopTransferCostTest.java @@ -0,0 +1,76 @@ +package org.opentripplanner.raptor.moduletests; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.raptor._data.transit.TestRoute.route; +import static org.opentripplanner.raptor._data.transit.TestTripSchedule.schedule; +import static org.opentripplanner.raptor.moduletests.support.RaptorModuleTestConfig.multiCriteria; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.raptor.RaptorService; +import org.opentripplanner.raptor._data.RaptorTestConstants; +import org.opentripplanner.raptor._data.transit.TestAccessEgress; +import org.opentripplanner.raptor._data.transit.TestTransitData; +import org.opentripplanner.raptor._data.transit.TestTripSchedule; +import org.opentripplanner.raptor.api.request.RaptorRequestBuilder; +import org.opentripplanner.raptor.configure.RaptorConfig; +import org.opentripplanner.raptor.moduletests.support.ModuleTestDebugLogging; +import org.opentripplanner.raptor.moduletests.support.RaptorModuleTestCase; + +/** + * FEATURE UNDER TEST + * + * This verifies that the stopTransferCost is not applied for egress legs. If this is not correctly + * handled by the heuristics optimization, the cheapest journey could be discarded. + */ +public class B05_EgressStopTransferCostTest implements RaptorTestConstants { + + private final TestTransitData data = new TestTransitData(); + private final RaptorRequestBuilder requestBuilder = new RaptorRequestBuilder<>(); + private final RaptorService raptorService = new RaptorService<>( + RaptorConfig.defaultConfigForTest() + ); + + @BeforeEach + void setup() { + data + .withRoute(route("R1", STOP_B, STOP_C).withTimetable(schedule("0:10, 0:14"))) + .withRoute(route("R2", STOP_C, STOP_D).withTimetable(schedule("0:18, 0:20"))); + + data.mcCostParamsBuilder().transferCost(0).boardCost(0); + data.withStopBoardAlightCost(STOP_D, 60000); + + requestBuilder + .searchParams() + .addAccessPaths(TestAccessEgress.free(STOP_B)) + .addEgressPaths( + TestAccessEgress.walk(STOP_C, D5m), // This will be the fastest + TestAccessEgress.walk(STOP_D, D20s) // This will be the cheapest + ) + .earliestDepartureTime(T00_00) + .latestArrivalTime(T00_30); + + ModuleTestDebugLogging.setupDebugLogging(data, requestBuilder); + } + + static List testCases() { + return RaptorModuleTestCase + .of() + .add( + multiCriteria(), + // We should get both the fastest and the c1-cheapest results + // The stopTransferCost should not be applied to the egress leg from STOP_D + "B ~ BUS R1 0:10 0:14 ~ C ~ Walk 5m [0:10 0:19 9m Tₓ0 C₁840]", + "B ~ BUS R1 0:10 0:14 ~ C ~ BUS R2 0:18 0:20 ~ D ~ Walk 20s [0:10 0:20:20 10m20s Tₓ1 C₁640]" + ) + .build(); + } + + @ParameterizedTest + @MethodSource("testCases") + void testRaptor(RaptorModuleTestCase testCase) { + assertEquals(testCase.expected(), testCase.run(raptorService, data, requestBuilder)); + } +} diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayerTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayerTest.java new file mode 100644 index 00000000000..7c674252e6a --- /dev/null +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayerTest.java @@ -0,0 +1,201 @@ +package org.opentripplanner.routing.algorithm.raptoradapter.transit; + +import static java.util.Map.entry; +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 java.time.LocalDate; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.opentripplanner.model.StopTime; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.framework.Deduplicator; +import org.opentripplanner.transit.model.network.RoutingTripPattern; +import org.opentripplanner.transit.model.network.StopPattern; +import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.timetable.TripTimes; +import org.opentripplanner.transit.model.timetable.TripTimesFactory; + +class TransitLayerTest { + + private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + private static final TripTimes TRIP_TIMES; + + private static final RoutingTripPattern TRIP_PATTERN; + + static { + var stop = TEST_MODEL.stop("TEST:STOP", 0, 0).build(); + var stopTime = new StopTime(); + stopTime.setStop(stop); + var stopPattern = new StopPattern(List.of(stopTime)); + var route = TransitModelForTest.route("1").build(); + TRIP_PATTERN = + TripPattern + .of(TransitModelForTest.id("P1")) + .withRoute(route) + .withStopPattern(stopPattern) + .build() + .getRoutingTripPattern(); + TRIP_TIMES = + TripTimesFactory.tripTimes( + TransitModelForTest.trip("1").withRoute(route).build(), + List.of(new StopTime()), + new Deduplicator() + ); + } + + @Test + void testGetTripPatternsRunningOnDateCopy() { + var date = LocalDate.of(2024, 1, 1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + date + ); + var tripPatterns = List.of(tripPatternForDate); + var transitLayer = new TransitLayer( + Map.of(date, tripPatterns), + null, + null, + null, + null, + null, + null, + null, + null + ); + var runningOnDate = transitLayer.getTripPatternsRunningOnDateCopy(date); + assertEquals(1, runningOnDate.size()); + assertEquals(tripPatterns, runningOnDate); + assertFalse(tripPatterns == runningOnDate); + assertEquals(0, transitLayer.getTripPatternsRunningOnDateCopy(date.minusDays(1)).size()); + assertEquals(0, transitLayer.getTripPatternsRunningOnDateCopy(date.plusDays(1)).size()); + } + + @Test + void testGetTripPatternsForRunningDate() { + var date = LocalDate.of(2024, 1, 1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + date + ); + var tripPatterns = List.of(tripPatternForDate); + var transitLayer = new TransitLayer( + Map.of(date, tripPatterns), + null, + null, + null, + null, + null, + null, + null, + null + ); + var runningOnDate = transitLayer.getTripPatternsForRunningDate(date); + assertEquals(1, runningOnDate.size()); + assertEquals(tripPatterns, runningOnDate); + assertTrue(tripPatterns == runningOnDate); + assertEquals(0, transitLayer.getTripPatternsForRunningDate(date.minusDays(1)).size()); + assertEquals(0, transitLayer.getTripPatternsForRunningDate(date.plusDays(1)).size()); + } + + @Test + void testGetTripPatternsOnServiceDateCopyWithSameRunningAndServiceDate() { + var date = LocalDate.of(2024, 1, 1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + date + ); + var transitLayer = new TransitLayer( + Map.of(date, List.of(tripPatternForDate)), + null, + null, + null, + null, + null, + null, + null, + null + ); + var startingOnDate = transitLayer.getTripPatternsOnServiceDateCopy(date); + assertEquals(1, startingOnDate.size()); + assertEquals(tripPatternForDate, startingOnDate.getFirst()); + assertEquals(0, transitLayer.getTripPatternsOnServiceDateCopy(date.minusDays(1)).size()); + assertEquals(0, transitLayer.getTripPatternsOnServiceDateCopy(date.plusDays(1)).size()); + } + + @Test + void testGetTripPatternsOnServiceDateCopyWithServiceRunningAfterMidnight() { + var runningDate = LocalDate.of(2024, 1, 1); + var serviceDate = runningDate.minusDays(1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + serviceDate + ); + var transitLayer = new TransitLayer( + Map.of(runningDate, List.of(tripPatternForDate)), + null, + null, + null, + null, + null, + null, + null, + null + ); + var startingOnDate = transitLayer.getTripPatternsOnServiceDateCopy(serviceDate); + // starting date should be determined by service date, not running date which refers to the + // normal calendar date that the trip pattern is running on + assertEquals(1, startingOnDate.size()); + assertEquals(tripPatternForDate, startingOnDate.getFirst()); + assertEquals(0, transitLayer.getTripPatternsOnServiceDateCopy(runningDate).size()); + } + + @Test + void testGetTripPatternsOnServiceDateCopyWithServiceRunningBeforeAndAfterMidnight() { + // This is same as the service date + var firstRunningDate = LocalDate.of(2024, 1, 1); + var secondRunningDate = firstRunningDate.plusDays(1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + firstRunningDate + ); + var transitLayer = new TransitLayer( + Map.ofEntries( + entry(firstRunningDate, List.of(tripPatternForDate)), + entry(secondRunningDate, List.of(tripPatternForDate)) + ), + null, + null, + null, + null, + null, + null, + null, + null + ); + var startingOnDate = transitLayer.getTripPatternsOnServiceDateCopy(firstRunningDate); + // Transit layer indexes trip patterns by running date and to get trip patterns for certain + // service date, we need to look up the trip patterns for the next running date as well, but + // we don't want to return duplicates + assertEquals(1, startingOnDate.size()); + assertEquals(tripPatternForDate, startingOnDate.getFirst()); + assertEquals(0, transitLayer.getTripPatternsOnServiceDateCopy(secondRunningDate).size()); + } +} diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java index 7b7ba23499c..3570d92bdbe 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java @@ -76,22 +76,28 @@ public void calculateMinCost() { // - Transit factor: 80 (min of 80 and 100) // Board cost is 500: - assertEquals(500, subject.calculateMinCost(0, 0)); + assertEquals(500, subject.calculateRemainingMinCost(0, 0, 0)); // The transfer 1s * 80 = 80 + board cost 500 - assertEquals(580, subject.calculateMinCost(1, 0)); + assertEquals(580, subject.calculateRemainingMinCost(1, 0, 0)); // Board 2 times and transfer 1: 2 * 500 + 200 - assertEquals(1200, subject.calculateMinCost(0, 1)); + assertEquals(1200, subject.calculateRemainingMinCost(0, 1, 0)); // Transit 200s * 80 + Board 4 * 500 + Transfer 3 * 200 - assertEquals(18_600, subject.calculateMinCost(200, 3)); + assertEquals(18_600, subject.calculateRemainingMinCost(200, 3, 0)); + + // Cost of egress should subtract the stop transfer cost 25 + assertEquals(-25, subject.calculateRemainingMinCost(0, -1, 1)); } @Test public void testConvertBetweenRaptorAndMainOtpDomainModel() { - assertEquals(RaptorCostConverter.toRaptorCost(BOARD_COST_SEC), subject.calculateMinCost(0, 0)); + assertEquals( + RaptorCostConverter.toRaptorCost(BOARD_COST_SEC), + subject.calculateRemainingMinCost(0, 0, 0) + ); assertEquals( RaptorCostConverter.toRaptorCost(0.8 * 20 + BOARD_COST_SEC), - subject.calculateMinCost(20, 0) + subject.calculateRemainingMinCost(20, 0, 0) ); } diff --git a/src/test/java/org/opentripplanner/routing/core/MoneyTest.java b/src/test/java/org/opentripplanner/routing/core/MoneyTest.java index 5f197708f1d..01392e64493 100644 --- a/src/test/java/org/opentripplanner/routing/core/MoneyTest.java +++ b/src/test/java/org/opentripplanner/routing/core/MoneyTest.java @@ -102,4 +102,9 @@ void greaterThan() { void serializable() { assertInstanceOf(Serializable.class, oneDollar); } + + @Test + void equalHashCode() { + assertEquals(Money.usDollars(5).hashCode(), Money.usDollars(5).hashCode()); + } } diff --git a/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java b/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java index 49dc42e6f4c..c01558338a4 100644 --- a/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java +++ b/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java @@ -39,8 +39,8 @@ public class TemporaryVerticesContainerTest { private final Graph g = new Graph(new Deduplicator()); private final StreetVertex a = StreetModelForTest.intersectionVertex("A", 1.0, 1.0); - private final StreetVertex b = StreetModelForTest.intersectionVertex("B", 0.0, 1.0); - private final StreetVertex c = StreetModelForTest.intersectionVertex("C", 1.0, 0.0); + private final StreetVertex b = StreetModelForTest.intersectionVertex("B", 1.0, 0.0); + private final StreetVertex c = StreetModelForTest.intersectionVertex("C", 0.0, 1.0); private final List permanentVertexes = Arrays.asList(a, b, c); // - And travel *origin* is 0,4 degrees on the road from B to A private final GenericLocation from = new GenericLocation(1.0, 0.4); diff --git a/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelperTest.java b/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelperTest.java index 9c80cb9cda9..90bdeb015a4 100644 --- a/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelperTest.java +++ b/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelperTest.java @@ -10,6 +10,7 @@ import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.vehicle_parking.VehicleParking.VehicleParkingEntranceCreator; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.edge.VehicleParkingEdge; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -41,8 +42,8 @@ void linkThreeVerticesTest() { @Test void linkSkippingEdgesTest() { Graph graph = new Graph(); - var vehicleParking = VehicleParking - .builder() + var vehicleParking = StreetModelForTest + .vehicleParking() .entrances( IntStream .rangeClosed(1, 3) @@ -67,8 +68,8 @@ void linkSkippingEdgesTest() { } private VehicleParking createParingWithEntrances(int entranceNumber) { - return VehicleParking - .builder() + return StreetModelForTest + .vehicleParking() .bicyclePlaces(true) .entrances( IntStream diff --git a/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingTestUtil.java b/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingTestUtil.java index 078d7350dcc..fca20322ebe 100644 --- a/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingTestUtil.java +++ b/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingTestUtil.java @@ -2,6 +2,7 @@ import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.NonLocalizedString; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.transit.model.framework.FeedScopedId; public class VehicleParkingTestUtil { @@ -25,8 +26,8 @@ public static VehicleParking createParkingWithEntrances( .coordinate(new WgsCoordinate(y, x)) .walkAccessible(true); - return VehicleParking - .builder() + return StreetModelForTest + .vehicleParking() .id(new FeedScopedId(TEST_FEED_ID, id)) .bicyclePlaces(true) .capacity(vehiclePlaces) diff --git a/src/test/java/org/opentripplanner/standalone/config/framework/json/NodeAdapterTest.java b/src/test/java/org/opentripplanner/standalone/config/framework/json/NodeAdapterTest.java index be44d1ea86f..9d3719c2285 100644 --- a/src/test/java/org/opentripplanner/standalone/config/framework/json/NodeAdapterTest.java +++ b/src/test/java/org/opentripplanner/standalone/config/framework/json/NodeAdapterTest.java @@ -34,7 +34,7 @@ public class NodeAdapterTest { public static final String NON_UNUSED_PARAMETERS = "EXPECTED_NONE"; @Test - public void testAsRawNode() { + void testAsRawNode() { NodeAdapter subject = newNodeAdapterForTest("{ child : { foo : 'bar' } }"); // Define child @@ -53,7 +53,7 @@ public void testAsRawNode() { } @Test - public void isEmpty() { + void isEmpty() { NodeAdapter subject = newNodeAdapterForTest(""); assertTrue(subject.of("alf").asObject().isEmpty()); @@ -64,7 +64,7 @@ public void isEmpty() { } @Test - public void path() { + void path() { NodeAdapter subject = newNodeAdapterForTest("{ foo : 'bar' }"); assertFalse(subject.of("foo").asObject().isEmpty()); assertTrue(subject.of("missingObject").asObject().isEmpty()); @@ -72,7 +72,7 @@ public void path() { } @Test - public void docInfo() { + void docInfo() { NodeAdapter subject = newNodeAdapterForTest("{ bool: false }"); subject.of("bool").since(V2_0).summary("B Summary").description("Ddd").asBoolean(); subject.of("en").since(V2_1).summary("EN Summary").asEnum(SECONDS); @@ -95,7 +95,7 @@ public void docInfo() { } @Test - public void asBoolean() { + void asBoolean() { NodeAdapter subject = newNodeAdapterForTest("{ aBoolean : true }"); assertTrue(subject.of("aBoolean").asBoolean()); assertTrue(subject.of("aBoolean").asBoolean(false)); @@ -103,7 +103,7 @@ public void asBoolean() { } @Test - public void asDouble() { + void asDouble() { NodeAdapter subject = newNodeAdapterForTest("{ aDouble : 7.0 }"); assertEquals(7.0, subject.of("aDouble").asDouble(-1d), 0.01); assertEquals(7.0, subject.of("aDouble").asDouble(), 0.01); @@ -111,28 +111,28 @@ public void asDouble() { } @Test - public void asDoubles() { + void asDoubles() { NodeAdapter subject = newNodeAdapterForTest("{ key : [ 2.0, 3.0, 5.0 ] }"); assertEquals(List.of(2d, 3d, 5d), subject.of("key").asDoubles(null)); assertEquals(NON_UNUSED_PARAMETERS, unusedParams(subject)); } @Test - public void asInt() { + void asInt() { NodeAdapter subject = newNodeAdapterForTest("{ aInt : 5 }"); assertEquals(5, subject.of("aInt").asInt(-1)); assertEquals(-1, subject.of("missingField").asInt(-1)); } @Test - public void asLong() { + void asLong() { NodeAdapter subject = newNodeAdapterForTest("{ key : 5 }"); assertEquals(5, subject.of("key").asLong(-1)); assertEquals(-1, subject.of("missingField").asLong(-1)); } @Test - public void asText() { + void asText() { NodeAdapter subject = newNodeAdapterForTest("{ aText : 'TEXT' }"); assertEquals("TEXT", subject.of("aText").asString("DEFAULT")); assertEquals("DEFAULT", subject.of("missingField").asString("DEFAULT")); @@ -142,19 +142,19 @@ public void asText() { } @Test - public void requiredAsText() { + void requiredAsText() { NodeAdapter subject = newNodeAdapterForTest("{ }"); assertThrows(OtpAppException.class, () -> subject.of("missingField").asString()); } @Test - public void rawAsText() { + void rawAsText() { NodeAdapter subject = newNodeAdapterForTest("{ aText : 'TEXT' }"); assertEquals("TEXT", subject.of("aText").asObject().asText()); } @Test - public void asEnum() { + void asEnum() { // Given NodeAdapter subject = newNodeAdapterForTest("{ a : 'A', abc : 'a-b-c' }"); @@ -169,7 +169,7 @@ public void asEnum() { } @Test - public void asEnumWithIllegalPropertySet() { + void asEnumWithIllegalPropertySet() { // Given NodeAdapter subject = newNodeAdapterForTest( """ @@ -205,7 +205,7 @@ public void asEnumWithIllegalPropertySet() { } @Test - public void asEnumMap() { + void asEnumMap() { // With optional enum values in map NodeAdapter subject = newNodeAdapterForTest("{ key : { A: true, B: false } }"); assertEquals( @@ -220,7 +220,7 @@ public void asEnumMap() { } @Test - public void asEnumMapWithCustomType() { + void asEnumMapWithCustomType() { // With optional enum values in map NodeAdapter subject = newNodeAdapterForTest("{ key : { A: {a:'Foo'} } }"); assertEquals( @@ -235,7 +235,7 @@ public void asEnumMapWithCustomType() { } @Test - public void asEnumMapWithDefaultValue() { + void asEnumMapWithDefaultValue() { var subject = newNodeAdapterForTest("{}"); final Map dflt = Map.of(AnEnum.A, new ARecord("Foo")); assertEquals(dflt, subject.of("key").asEnumMap(AnEnum.class, ARecord::fromJson, dflt)); @@ -243,7 +243,7 @@ public void asEnumMapWithDefaultValue() { } @Test - public void asEnumMapWithUnknownKey() { + void asEnumMapWithUnknownKey() { NodeAdapter subject = newNodeAdapterForTest("{ enumMap : { unknown : 7 } }"); subject.of("enumMap").asEnumMap(AnEnum.class, Double.class); @@ -261,7 +261,7 @@ public void asEnumMapWithUnknownKey() { } @Test - public void asEnumMapAllKeysRequired() { + void asEnumMapAllKeysRequired() { var subject = newNodeAdapterForTest("{ key : { A: true, b: false, a_B_c: true } }"); assertEquals( Map.of(AnEnum.A, true, AnEnum.B, false, AnEnum.A_B_C, true), @@ -280,7 +280,7 @@ public void asEnumMapAllKeysRequired() { } @Test - public void asEnumMapWithRequiredMissingValue() { + void asEnumMapWithRequiredMissingValue() { // A value for C is missing in map NodeAdapter subject = newNodeAdapterForTest("{ key : { A: true, B: false } }"); @@ -291,7 +291,7 @@ public void asEnumMapWithRequiredMissingValue() { } @Test - public void asEnumSet() { + void asEnumSet() { NodeAdapter subject = newNodeAdapterForTest("{ key : [ 'A', 'B' ] }"); assertEquals(Set.of(AnEnum.A, AnEnum.B), subject.of("key").asEnumSet(AnEnum.class)); assertEquals(Set.of(), subject.of("missing-key").asEnumSet(AnEnum.class)); @@ -299,13 +299,13 @@ public void asEnumSet() { } @Test - public void asEnumSetFailsUsingWrongFormat() { + void asEnumSetFailsUsingWrongFormat() { NodeAdapter subject = newNodeAdapterForTest("{ key : 'A,B' }"); assertThrows(OtpAppException.class, () -> subject.of("key").asEnumSet(AnEnum.class)); } @Test - public void asFeedScopedId() { + void asFeedScopedId() { NodeAdapter subject = newNodeAdapterForTest("{ key1: 'A:23', key2: 'B:12' }"); assertEquals("A:23", subject.of("key1").asFeedScopedId(null).toString()); assertEquals("B:12", subject.of("key2").asFeedScopedId(null).toString()); @@ -316,7 +316,7 @@ public void asFeedScopedId() { } @Test - public void asFeedScopedIds() { + void asFeedScopedIds() { NodeAdapter subject = newNodeAdapterForTest("{ routes: ['A:23', 'B:12']}"); assertEquals("[A:23, B:12]", subject.of("routes").asFeedScopedIds(List.of()).toString()); assertEquals("[]", subject.of("missing-key").asFeedScopedIds(List.of()).toString()); @@ -328,7 +328,7 @@ public void asFeedScopedIds() { } @Test - public void asFeedScopedIdSet() { + void asFeedScopedIdSet() { NodeAdapter subject = newNodeAdapterForTest("{ routes: ['A:23', 'B:12', 'A:23']}"); assertEquals( List.of( @@ -342,7 +342,7 @@ public void asFeedScopedIdSet() { } @Test - public void asDateOrRelativePeriod() { + void asDateOrRelativePeriod() { // Given var subject = newNodeAdapterForTest("{ 'a' : '2020-02-28', 'b' : '-P3Y' }"); var utc = ZoneIds.UTC; @@ -362,7 +362,7 @@ public void asDateOrRelativePeriod() { } @Test - public void testParsePeriodDateThrowsException() { + void testParsePeriodDateThrowsException() { // Given NodeAdapter subject = newNodeAdapterForTest("{ 'foo' : 'bar' }"); @@ -374,7 +374,7 @@ public void testParsePeriodDateThrowsException() { } @Test - public void asDuration() { + void asDuration() { NodeAdapter subject = newNodeAdapterForTest("{ k1:'PT1s', k2:'3h2m1s', k3:7 }"); // as required duration @@ -390,13 +390,13 @@ public void asDuration() { } @Test - public void requiredAsDuration() { + void requiredAsDuration() { NodeAdapter subject = newNodeAdapterForTest("{ }"); assertThrows(OtpAppException.class, () -> subject.of("missingField").asDuration()); } @Test - public void asDurations() { + void asDurations() { NodeAdapter subject = newNodeAdapterForTest("{ key1 : ['PT1s', '2h'] }"); assertEquals("[PT1S, PT2H]", subject.of("key1").asDurations(List.of()).toString()); assertEquals("[PT3H]", subject.of("missing-key").asDurations(List.of(D3h)).toString()); @@ -404,7 +404,7 @@ public void asDurations() { } @Test - public void asLocale() { + void asLocale() { NodeAdapter subject = newNodeAdapterForTest( "{ key1 : 'no', key2 : 'no_NO', key3 : 'no_NO_NY' }" ); @@ -415,14 +415,14 @@ public void asLocale() { } @Test - public void asPattern() { + void asPattern() { NodeAdapter subject = newNodeAdapterForTest("{ key : 'Ab*a' }"); assertEquals("Ab*a", subject.of("key").asPattern("ABC").toString()); assertEquals("ABC", subject.of("missingField").asPattern("ABC").toString()); } @Test - public void uri() { + void uri() { var URL = "gs://bucket/a.obj"; NodeAdapter subject = newNodeAdapterForTest("{ aUri : '" + URL + "' }"); @@ -433,14 +433,14 @@ public void uri() { } @Test - public void uriSyntaxException() { + void uriSyntaxException() { NodeAdapter subject = newNodeAdapterForTest("{ aUri : 'error$%uri' }"); assertThrows(OtpAppException.class, () -> subject.of("aUri").asUri(null), "error$%uri"); } @Test - public void uriRequiredValueMissing() { + void uriRequiredValueMissing() { NodeAdapter subject = newNodeAdapterForTest("{ }"); assertThrows( @@ -451,7 +451,7 @@ public void uriRequiredValueMissing() { } @Test - public void uris() { + void uris() { NodeAdapter subject = newNodeAdapterForTest("{ foo : ['gs://a/b', 'gs://c/d'] }"); assertEquals("[gs://a/b, gs://c/d]", subject.of("foo").asUris().toString()); @@ -461,7 +461,7 @@ public void uris() { } @Test - public void urisNotAnArrayException() { + void urisNotAnArrayException() { NodeAdapter subject = newNodeAdapterForTest("{ 'uris': 'no array' }"); assertThrows( @@ -472,7 +472,7 @@ public void urisNotAnArrayException() { } @Test - public void objectAsList() { + void objectAsList() { NodeAdapter subject = newNodeAdapterForTest("{ key : [{ a: 'I' }, { a: '2' } ] }"); List result = subject @@ -487,21 +487,21 @@ public void objectAsList() { } @Test - public void asCostLinearFunction() { + void asCostLinearFunction() { NodeAdapter subject = newNodeAdapterForTest("{ key : '400+8x' }"); assertEquals("6m40s + 8.0 t", subject.of("key").asCostLinearFunction(null).toString()); assertNull(subject.of("no-key").asCostLinearFunction(null)); } @Test - public void asTimePenalty() { + void asTimePenalty() { NodeAdapter subject = newNodeAdapterForTest("{ key : '400+8x' }"); assertEquals("6m40s + 8.0 t", subject.of("key").asTimePenalty(null).toString()); assertNull(subject.of("no-key").asTimePenalty(null)); } @Test - public void asZoneId() { + void asZoneId() { NodeAdapter subject = newNodeAdapterForTest( "{ key1 : 'UTC', key2 : 'Europe/Oslo', key3 : '+02:00', key4: 'invalid' }" ); @@ -515,7 +515,7 @@ public void asZoneId() { } @Test - public void asTextSet() { + void asTextSet() { NodeAdapter subject = newNodeAdapterForTest("{ ids : ['A', 'C', 'F'] }"); assertEquals( Set.of("A", "C", "F"), @@ -528,7 +528,7 @@ public void asTextSet() { } @Test - public void isNonEmptyArray() { + void isNonEmptyArray() { NodeAdapter subject = newNodeAdapterForTest("{ foo : ['A'], bar: [], foobar: true }"); assertTrue(subject.of("foo").asObject().isNonEmptyArray()); assertFalse(subject.of("bar").asObject().isNonEmptyArray()); @@ -538,13 +538,13 @@ public void isNonEmptyArray() { } @Test - public void deduplicateChildren() { + void deduplicateChildren() { NodeAdapter subject = newNodeAdapterForTest("{ foo : { enabled: true } }"); assertSame(subject.of("foo").asObject(), subject.of("foo").asObject()); } @Test - public void unusedParams() { + void unusedParams() { // Given: two parameters a and b NodeAdapter subject = newNodeAdapterForTest("{ foo : { a: true, b: false } }"); var buf = new StringBuilder(); @@ -557,6 +557,29 @@ public void unusedParams() { assertEquals("Unexpected config parameter: 'foo.b:false' in 'Test'", buf.toString()); } + @Test + void unknownParameters() { + // Given: two parameters a and b + var subject = newNodeAdapterForTest("{ foo : { a: true, b: false } }"); + + // When: Access ONLY parameter 'a', but not 'b' + subject.of("foo").asObject().of("a").asBoolean(); + + assertTrue(subject.hasUnknownParameters()); + } + + @Test + void allParametersAreKnown() { + // Given: two parameters a and b + var subject = newNodeAdapterForTest("{ foo : { a: true, b: false } }"); + + var object = subject.of("foo").asObject(); + object.of("a").asBoolean(); + object.of("b").asBoolean(); + + assertFalse(subject.hasUnknownParameters()); + } + private static String unusedParams(NodeAdapter subject) { var buf = new StringBuilder(); subject.logAllWarnings(m -> buf.append('\n').append(m)); 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 d50c8a74dfc..4a0a0d1b848 100644 --- a/src/test/java/org/opentripplanner/street/model/_data/StreetModelForTest.java +++ b/src/test/java/org/opentripplanner/street/model/_data/StreetModelForTest.java @@ -9,6 +9,7 @@ import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.service.vehiclerental.model.TestFreeFloatingRentalVehicleBuilder; import org.opentripplanner.service.vehiclerental.street.VehicleRentalPlaceVertex; import org.opentripplanner.street.model.RentalFormFactor; @@ -29,16 +30,16 @@ public class StreetModelForTest { public static StreetVertex V4 = intersectionVertex("V4", 3, 3); public static IntersectionVertex intersectionVertex(Coordinate c) { - return intersectionVertex(c.x, c.y); + return intersectionVertex(c.y, c.x); } public static IntersectionVertex intersectionVertex(double lat, double lon) { var label = "%s_%s".formatted(lat, lon); - return new LabelledIntersectionVertex(label, lat, lon, false, false); + return new LabelledIntersectionVertex(label, lon, lat, false, false); } public static IntersectionVertex intersectionVertex(String label, double lat, double lon) { - return new LabelledIntersectionVertex(label, lat, lon, false, false); + return new LabelledIntersectionVertex(label, lon, lat, false, false); } @Nonnull @@ -111,4 +112,8 @@ public static VehicleRentalPlaceVertex rentalVertex(RentalFormFactor formFactor) } return new VehicleRentalPlaceVertex(rentalVehicleBuilder.build()); } + + public static VehicleParking.VehicleParkingBuilder vehicleParking() { + return VehicleParking.builder().id(id("vehicle-parking-1")).coordinate(WgsCoordinate.GREENWICH); + } } diff --git a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java index 318f133f15e..7de7b0c7ce6 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java @@ -46,7 +46,7 @@ public class StreetEdgeTest { public void before() { v0 = intersectionVertex("maple_0th", 0.0, 0.0); // label, X, Y v1 = intersectionVertex("maple_1st", 2.0, 2.0); - v2 = intersectionVertex("maple_2nd", 1.0, 2.0); + v2 = intersectionVertex("maple_2nd", 2.0, 1.0); this.proto = StreetSearchRequest @@ -73,7 +73,7 @@ public void testInAndOutAngles() { assertEquals(90, e1.getOutAngle()); // 2 new ones - StreetVertex u = intersectionVertex("test1", 2.0, 1.0); + StreetVertex u = intersectionVertex("test1", 1.0, 2.0); StreetVertex v = intersectionVertex("test2", 2.0, 2.0); // Second edge, heading straight North diff --git a/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingEdgeTest.java b/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingEdgeTest.java index 7dad61fbd6b..81bee23ad54 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingEdgeTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingEdgeTest.java @@ -10,6 +10,7 @@ import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.street.search.request.StreetSearchRequest; import org.opentripplanner.street.search.state.State; @@ -130,8 +131,8 @@ private VehicleParking createVehicleParking( boolean carPlaces, VehicleParkingSpaces availability ) { - return VehicleParking - .builder() + return StreetModelForTest + .vehicleParking() .id(TransitModelForTest.id("VehicleParking")) .bicyclePlaces(bicyclePlaces) .carPlaces(carPlaces) diff --git a/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingPreferredTagsTest.java b/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingPreferredTagsTest.java index 5969121b6d6..579ae4e964d 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingPreferredTagsTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingPreferredTagsTest.java @@ -11,9 +11,9 @@ import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.routing.api.request.StreetMode; -import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingEntrance; import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.search.request.StreetSearchRequest; @@ -62,9 +62,8 @@ private void runTest( double expectedCost, boolean arriveBy ) { - var parking = VehicleParking - .builder() - .coordinate(COORDINATE) + var parking = StreetModelForTest + .vehicleParking() .tags(parkingTags) .availability(VehicleParkingSpaces.builder().bicycleSpaces(100).build()) .bicyclePlaces(true) diff --git a/src/test/java/org/opentripplanner/transit/speed_test/support/AssertSpeedTestSetup.java b/src/test/java/org/opentripplanner/transit/speed_test/support/AssertSpeedTestSetup.java index 031df343a9a..4113d5979b1 100644 --- a/src/test/java/org/opentripplanner/transit/speed_test/support/AssertSpeedTestSetup.java +++ b/src/test/java/org/opentripplanner/transit/speed_test/support/AssertSpeedTestSetup.java @@ -15,7 +15,7 @@ public static void assertTestDateHasData( ) { int numberOfPatternForTestDate = transitModel .getTransitLayer() - .getTripPatternsForDate(config.testDate) + .getTripPatternsForRunningDate(config.testDate) .size(); if (numberOfPatternForTestDate < 10) { diff --git a/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java b/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java index 2bcb49defae..a31b5cfb387 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java @@ -16,6 +16,7 @@ import org.opentripplanner.routing.vehicle_parking.VehicleParkingState; import org.opentripplanner.routing.vehicle_parking.VehicleParkingTestGraphData; import org.opentripplanner.routing.vehicle_parking.VehicleParkingTestUtil; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.edge.StreetVehicleParkingLink; import org.opentripplanner.street.model.edge.VehicleParkingEdge; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; @@ -142,7 +143,10 @@ void deleteVehicleParkingTest() { @Test void addNotOperatingVehicleParkingTest() { - var vehicleParking = VehicleParking.builder().state(VehicleParkingState.CLOSED).build(); + var vehicleParking = StreetModelForTest + .vehicleParking() + .state(VehicleParkingState.CLOSED) + .build(); when(dataSource.getUpdates()).thenReturn(List.of(vehicleParking)); runUpdaterOnce(); @@ -155,8 +159,8 @@ void addNotOperatingVehicleParkingTest() { void updateNotOperatingVehicleParkingTest() { var vehiclePlaces = VehicleParkingSpaces.builder().bicycleSpaces(1).build(); - var vehicleParking = VehicleParking - .builder() + var vehicleParking = StreetModelForTest + .vehicleParking() .availability(vehiclePlaces) .state(VehicleParkingState.CLOSED) .build(); @@ -175,8 +179,8 @@ void updateNotOperatingVehicleParkingTest() { vehiclePlaces = VehicleParkingSpaces.builder().bicycleSpaces(2).build(); vehicleParking = - VehicleParking - .builder() + StreetModelForTest + .vehicleParking() .availability(vehiclePlaces) .state(VehicleParkingState.CLOSED) .build(); @@ -194,7 +198,10 @@ void updateNotOperatingVehicleParkingTest() { @Test void deleteNotOperatingVehicleParkingTest() { - var vehicleParking = VehicleParking.builder().state(VehicleParkingState.CLOSED).build(); + var vehicleParking = StreetModelForTest + .vehicleParking() + .state(VehicleParkingState.CLOSED) + .build(); when(dataSource.getUpdates()).thenReturn(List.of(vehicleParking)); runUpdaterOnce(); diff --git a/src/test/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdaterTest.java b/src/test/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdaterTest.java index 09dd1cf1cb9..99910e04f7c 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdaterTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdaterTest.java @@ -8,10 +8,10 @@ import java.util.List; import org.junit.jupiter.api.Test; -import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.MultiPolygon; import org.locationtech.jts.geom.Polygon; +import org.opentripplanner._support.geometry.Polygons; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.service.vehiclerental.model.GeofencingZone; import org.opentripplanner.service.vehiclerental.street.BusinessAreaBorder; @@ -35,30 +35,16 @@ class GeofencingVertexUpdaterTest { final GeofencingVertexUpdater updater = new GeofencingVertexUpdater(ignored -> List.of(insideFrognerPark, halfInHalfOutFrognerPark, businessBorder) ); - StreetEdge outsideFrognerPark = streetEdge(outsideFrognerPark1, outsideFrognerPark2); - - GeometryFactory fac = GeometryUtils.getGeometryFactory(); - Polygon frognerPark = fac.createPolygon( - new Coordinate[] { - new Coordinate(59.93112978539807, 10.691099320272173), - new Coordinate(59.92231848097069, 10.691099320272173), - new Coordinate(59.92231848097069, 10.711758464910503), - new Coordinate(59.92231848097069, 10.691099320272173), - new Coordinate(59.93112978539807, 10.691099320272173), - } - ); - final GeofencingZone zone = new GeofencingZone(id("frogner-park"), frognerPark, true, false); - Polygon osloPolygon = fac.createPolygon( - new Coordinate[] { - new Coordinate(59.961055202323195, 10.62535658370308), - new Coordinate(59.889009435700416, 10.62535658370308), - new Coordinate(59.889009435700416, 10.849791142928694), - new Coordinate(59.961055202323195, 10.849791142928694), - new Coordinate(59.961055202323195, 10.62535658370308), - } + + static GeometryFactory fac = GeometryUtils.getGeometryFactory(); + final GeofencingZone zone = new GeofencingZone( + id("frogner-park"), + Polygons.OSLO_FROGNER_PARK, + true, + false ); - MultiPolygon osloMultiPolygon = fac.createMultiPolygon(new Polygon[] { osloPolygon }); + MultiPolygon osloMultiPolygon = fac.createMultiPolygon(new Polygon[] { Polygons.OSLO }); final GeofencingZone businessArea = new GeofencingZone( id("oslo"), osloMultiPolygon, diff --git a/src/test/resources/standalone/config/router-config.json b/src/test/resources/standalone/config/router-config.json index 3a07cddfeb8..3b43ef1c5d2 100644 --- a/src/test/resources/standalone/config/router-config.json +++ b/src/test/resources/standalone/config/router-config.json @@ -334,6 +334,12 @@ "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", @@ -414,6 +420,12 @@ "fromDateTime": "-P1D", "timeout": 300000 } + }, + { + "type": "vehicle-parking", + "feedId": "bikeep", + "sourceType": "bikeep", + "url": "https://services.bikeep.com/location/v1/public-areas/no-baia-mobility/locations" } ], "rideHailingServices": [
+ * This verifies that the stopTransferCost is not applied for egress legs. If this is not correctly + * handled by the heuristics optimization, the cheapest journey could be discarded. + */ +public class B05_EgressStopTransferCostTest implements RaptorTestConstants { + + private final TestTransitData data = new TestTransitData(); + private final RaptorRequestBuilder requestBuilder = new RaptorRequestBuilder<>(); + private final RaptorService raptorService = new RaptorService<>( + RaptorConfig.defaultConfigForTest() + ); + + @BeforeEach + void setup() { + data + .withRoute(route("R1", STOP_B, STOP_C).withTimetable(schedule("0:10, 0:14"))) + .withRoute(route("R2", STOP_C, STOP_D).withTimetable(schedule("0:18, 0:20"))); + + data.mcCostParamsBuilder().transferCost(0).boardCost(0); + data.withStopBoardAlightCost(STOP_D, 60000); + + requestBuilder + .searchParams() + .addAccessPaths(TestAccessEgress.free(STOP_B)) + .addEgressPaths( + TestAccessEgress.walk(STOP_C, D5m), // This will be the fastest + TestAccessEgress.walk(STOP_D, D20s) // This will be the cheapest + ) + .earliestDepartureTime(T00_00) + .latestArrivalTime(T00_30); + + ModuleTestDebugLogging.setupDebugLogging(data, requestBuilder); + } + + static List testCases() { + return RaptorModuleTestCase + .of() + .add( + multiCriteria(), + // We should get both the fastest and the c1-cheapest results + // The stopTransferCost should not be applied to the egress leg from STOP_D + "B ~ BUS R1 0:10 0:14 ~ C ~ Walk 5m [0:10 0:19 9m Tₓ0 C₁840]", + "B ~ BUS R1 0:10 0:14 ~ C ~ BUS R2 0:18 0:20 ~ D ~ Walk 20s [0:10 0:20:20 10m20s Tₓ1 C₁640]" + ) + .build(); + } + + @ParameterizedTest + @MethodSource("testCases") + void testRaptor(RaptorModuleTestCase testCase) { + assertEquals(testCase.expected(), testCase.run(raptorService, data, requestBuilder)); + } +} diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayerTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayerTest.java new file mode 100644 index 00000000000..7c674252e6a --- /dev/null +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/TransitLayerTest.java @@ -0,0 +1,201 @@ +package org.opentripplanner.routing.algorithm.raptoradapter.transit; + +import static java.util.Map.entry; +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 java.time.LocalDate; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.opentripplanner.model.StopTime; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.framework.Deduplicator; +import org.opentripplanner.transit.model.network.RoutingTripPattern; +import org.opentripplanner.transit.model.network.StopPattern; +import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.timetable.TripTimes; +import org.opentripplanner.transit.model.timetable.TripTimesFactory; + +class TransitLayerTest { + + private static final TransitModelForTest TEST_MODEL = TransitModelForTest.of(); + private static final TripTimes TRIP_TIMES; + + private static final RoutingTripPattern TRIP_PATTERN; + + static { + var stop = TEST_MODEL.stop("TEST:STOP", 0, 0).build(); + var stopTime = new StopTime(); + stopTime.setStop(stop); + var stopPattern = new StopPattern(List.of(stopTime)); + var route = TransitModelForTest.route("1").build(); + TRIP_PATTERN = + TripPattern + .of(TransitModelForTest.id("P1")) + .withRoute(route) + .withStopPattern(stopPattern) + .build() + .getRoutingTripPattern(); + TRIP_TIMES = + TripTimesFactory.tripTimes( + TransitModelForTest.trip("1").withRoute(route).build(), + List.of(new StopTime()), + new Deduplicator() + ); + } + + @Test + void testGetTripPatternsRunningOnDateCopy() { + var date = LocalDate.of(2024, 1, 1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + date + ); + var tripPatterns = List.of(tripPatternForDate); + var transitLayer = new TransitLayer( + Map.of(date, tripPatterns), + null, + null, + null, + null, + null, + null, + null, + null + ); + var runningOnDate = transitLayer.getTripPatternsRunningOnDateCopy(date); + assertEquals(1, runningOnDate.size()); + assertEquals(tripPatterns, runningOnDate); + assertFalse(tripPatterns == runningOnDate); + assertEquals(0, transitLayer.getTripPatternsRunningOnDateCopy(date.minusDays(1)).size()); + assertEquals(0, transitLayer.getTripPatternsRunningOnDateCopy(date.plusDays(1)).size()); + } + + @Test + void testGetTripPatternsForRunningDate() { + var date = LocalDate.of(2024, 1, 1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + date + ); + var tripPatterns = List.of(tripPatternForDate); + var transitLayer = new TransitLayer( + Map.of(date, tripPatterns), + null, + null, + null, + null, + null, + null, + null, + null + ); + var runningOnDate = transitLayer.getTripPatternsForRunningDate(date); + assertEquals(1, runningOnDate.size()); + assertEquals(tripPatterns, runningOnDate); + assertTrue(tripPatterns == runningOnDate); + assertEquals(0, transitLayer.getTripPatternsForRunningDate(date.minusDays(1)).size()); + assertEquals(0, transitLayer.getTripPatternsForRunningDate(date.plusDays(1)).size()); + } + + @Test + void testGetTripPatternsOnServiceDateCopyWithSameRunningAndServiceDate() { + var date = LocalDate.of(2024, 1, 1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + date + ); + var transitLayer = new TransitLayer( + Map.of(date, List.of(tripPatternForDate)), + null, + null, + null, + null, + null, + null, + null, + null + ); + var startingOnDate = transitLayer.getTripPatternsOnServiceDateCopy(date); + assertEquals(1, startingOnDate.size()); + assertEquals(tripPatternForDate, startingOnDate.getFirst()); + assertEquals(0, transitLayer.getTripPatternsOnServiceDateCopy(date.minusDays(1)).size()); + assertEquals(0, transitLayer.getTripPatternsOnServiceDateCopy(date.plusDays(1)).size()); + } + + @Test + void testGetTripPatternsOnServiceDateCopyWithServiceRunningAfterMidnight() { + var runningDate = LocalDate.of(2024, 1, 1); + var serviceDate = runningDate.minusDays(1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + serviceDate + ); + var transitLayer = new TransitLayer( + Map.of(runningDate, List.of(tripPatternForDate)), + null, + null, + null, + null, + null, + null, + null, + null + ); + var startingOnDate = transitLayer.getTripPatternsOnServiceDateCopy(serviceDate); + // starting date should be determined by service date, not running date which refers to the + // normal calendar date that the trip pattern is running on + assertEquals(1, startingOnDate.size()); + assertEquals(tripPatternForDate, startingOnDate.getFirst()); + assertEquals(0, transitLayer.getTripPatternsOnServiceDateCopy(runningDate).size()); + } + + @Test + void testGetTripPatternsOnServiceDateCopyWithServiceRunningBeforeAndAfterMidnight() { + // This is same as the service date + var firstRunningDate = LocalDate.of(2024, 1, 1); + var secondRunningDate = firstRunningDate.plusDays(1); + + var tripPatternForDate = new TripPatternForDate( + TRIP_PATTERN, + List.of(TRIP_TIMES), + List.of(), + firstRunningDate + ); + var transitLayer = new TransitLayer( + Map.ofEntries( + entry(firstRunningDate, List.of(tripPatternForDate)), + entry(secondRunningDate, List.of(tripPatternForDate)) + ), + null, + null, + null, + null, + null, + null, + null, + null + ); + var startingOnDate = transitLayer.getTripPatternsOnServiceDateCopy(firstRunningDate); + // Transit layer indexes trip patterns by running date and to get trip patterns for certain + // service date, we need to look up the trip patterns for the next running date as well, but + // we don't want to return duplicates + assertEquals(1, startingOnDate.size()); + assertEquals(tripPatternForDate, startingOnDate.getFirst()); + assertEquals(0, transitLayer.getTripPatternsOnServiceDateCopy(secondRunningDate).size()); + } +} diff --git a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java index 7b7ba23499c..3570d92bdbe 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java @@ -76,22 +76,28 @@ public void calculateMinCost() { // - Transit factor: 80 (min of 80 and 100) // Board cost is 500: - assertEquals(500, subject.calculateMinCost(0, 0)); + assertEquals(500, subject.calculateRemainingMinCost(0, 0, 0)); // The transfer 1s * 80 = 80 + board cost 500 - assertEquals(580, subject.calculateMinCost(1, 0)); + assertEquals(580, subject.calculateRemainingMinCost(1, 0, 0)); // Board 2 times and transfer 1: 2 * 500 + 200 - assertEquals(1200, subject.calculateMinCost(0, 1)); + assertEquals(1200, subject.calculateRemainingMinCost(0, 1, 0)); // Transit 200s * 80 + Board 4 * 500 + Transfer 3 * 200 - assertEquals(18_600, subject.calculateMinCost(200, 3)); + assertEquals(18_600, subject.calculateRemainingMinCost(200, 3, 0)); + + // Cost of egress should subtract the stop transfer cost 25 + assertEquals(-25, subject.calculateRemainingMinCost(0, -1, 1)); } @Test public void testConvertBetweenRaptorAndMainOtpDomainModel() { - assertEquals(RaptorCostConverter.toRaptorCost(BOARD_COST_SEC), subject.calculateMinCost(0, 0)); + assertEquals( + RaptorCostConverter.toRaptorCost(BOARD_COST_SEC), + subject.calculateRemainingMinCost(0, 0, 0) + ); assertEquals( RaptorCostConverter.toRaptorCost(0.8 * 20 + BOARD_COST_SEC), - subject.calculateMinCost(20, 0) + subject.calculateRemainingMinCost(20, 0, 0) ); } diff --git a/src/test/java/org/opentripplanner/routing/core/MoneyTest.java b/src/test/java/org/opentripplanner/routing/core/MoneyTest.java index 5f197708f1d..01392e64493 100644 --- a/src/test/java/org/opentripplanner/routing/core/MoneyTest.java +++ b/src/test/java/org/opentripplanner/routing/core/MoneyTest.java @@ -102,4 +102,9 @@ void greaterThan() { void serializable() { assertInstanceOf(Serializable.class, oneDollar); } + + @Test + void equalHashCode() { + assertEquals(Money.usDollars(5).hashCode(), Money.usDollars(5).hashCode()); + } } diff --git a/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java b/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java index 49dc42e6f4c..c01558338a4 100644 --- a/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java +++ b/src/test/java/org/opentripplanner/routing/core/TemporaryVerticesContainerTest.java @@ -39,8 +39,8 @@ public class TemporaryVerticesContainerTest { private final Graph g = new Graph(new Deduplicator()); private final StreetVertex a = StreetModelForTest.intersectionVertex("A", 1.0, 1.0); - private final StreetVertex b = StreetModelForTest.intersectionVertex("B", 0.0, 1.0); - private final StreetVertex c = StreetModelForTest.intersectionVertex("C", 1.0, 0.0); + private final StreetVertex b = StreetModelForTest.intersectionVertex("B", 1.0, 0.0); + private final StreetVertex c = StreetModelForTest.intersectionVertex("C", 0.0, 1.0); private final List permanentVertexes = Arrays.asList(a, b, c); // - And travel *origin* is 0,4 degrees on the road from B to A private final GenericLocation from = new GenericLocation(1.0, 0.4); diff --git a/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelperTest.java b/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelperTest.java index 9c80cb9cda9..90bdeb015a4 100644 --- a/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelperTest.java +++ b/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingHelperTest.java @@ -10,6 +10,7 @@ import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.vehicle_parking.VehicleParking.VehicleParkingEntranceCreator; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.edge.VehicleParkingEdge; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.transit.model.framework.FeedScopedId; @@ -41,8 +42,8 @@ void linkThreeVerticesTest() { @Test void linkSkippingEdgesTest() { Graph graph = new Graph(); - var vehicleParking = VehicleParking - .builder() + var vehicleParking = StreetModelForTest + .vehicleParking() .entrances( IntStream .rangeClosed(1, 3) @@ -67,8 +68,8 @@ void linkSkippingEdgesTest() { } private VehicleParking createParingWithEntrances(int entranceNumber) { - return VehicleParking - .builder() + return StreetModelForTest + .vehicleParking() .bicyclePlaces(true) .entrances( IntStream diff --git a/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingTestUtil.java b/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingTestUtil.java index 078d7350dcc..fca20322ebe 100644 --- a/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingTestUtil.java +++ b/src/test/java/org/opentripplanner/routing/vehicle_parking/VehicleParkingTestUtil.java @@ -2,6 +2,7 @@ import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.NonLocalizedString; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.transit.model.framework.FeedScopedId; public class VehicleParkingTestUtil { @@ -25,8 +26,8 @@ public static VehicleParking createParkingWithEntrances( .coordinate(new WgsCoordinate(y, x)) .walkAccessible(true); - return VehicleParking - .builder() + return StreetModelForTest + .vehicleParking() .id(new FeedScopedId(TEST_FEED_ID, id)) .bicyclePlaces(true) .capacity(vehiclePlaces) diff --git a/src/test/java/org/opentripplanner/standalone/config/framework/json/NodeAdapterTest.java b/src/test/java/org/opentripplanner/standalone/config/framework/json/NodeAdapterTest.java index be44d1ea86f..9d3719c2285 100644 --- a/src/test/java/org/opentripplanner/standalone/config/framework/json/NodeAdapterTest.java +++ b/src/test/java/org/opentripplanner/standalone/config/framework/json/NodeAdapterTest.java @@ -34,7 +34,7 @@ public class NodeAdapterTest { public static final String NON_UNUSED_PARAMETERS = "EXPECTED_NONE"; @Test - public void testAsRawNode() { + void testAsRawNode() { NodeAdapter subject = newNodeAdapterForTest("{ child : { foo : 'bar' } }"); // Define child @@ -53,7 +53,7 @@ public void testAsRawNode() { } @Test - public void isEmpty() { + void isEmpty() { NodeAdapter subject = newNodeAdapterForTest(""); assertTrue(subject.of("alf").asObject().isEmpty()); @@ -64,7 +64,7 @@ public void isEmpty() { } @Test - public void path() { + void path() { NodeAdapter subject = newNodeAdapterForTest("{ foo : 'bar' }"); assertFalse(subject.of("foo").asObject().isEmpty()); assertTrue(subject.of("missingObject").asObject().isEmpty()); @@ -72,7 +72,7 @@ public void path() { } @Test - public void docInfo() { + void docInfo() { NodeAdapter subject = newNodeAdapterForTest("{ bool: false }"); subject.of("bool").since(V2_0).summary("B Summary").description("Ddd").asBoolean(); subject.of("en").since(V2_1).summary("EN Summary").asEnum(SECONDS); @@ -95,7 +95,7 @@ public void docInfo() { } @Test - public void asBoolean() { + void asBoolean() { NodeAdapter subject = newNodeAdapterForTest("{ aBoolean : true }"); assertTrue(subject.of("aBoolean").asBoolean()); assertTrue(subject.of("aBoolean").asBoolean(false)); @@ -103,7 +103,7 @@ public void asBoolean() { } @Test - public void asDouble() { + void asDouble() { NodeAdapter subject = newNodeAdapterForTest("{ aDouble : 7.0 }"); assertEquals(7.0, subject.of("aDouble").asDouble(-1d), 0.01); assertEquals(7.0, subject.of("aDouble").asDouble(), 0.01); @@ -111,28 +111,28 @@ public void asDouble() { } @Test - public void asDoubles() { + void asDoubles() { NodeAdapter subject = newNodeAdapterForTest("{ key : [ 2.0, 3.0, 5.0 ] }"); assertEquals(List.of(2d, 3d, 5d), subject.of("key").asDoubles(null)); assertEquals(NON_UNUSED_PARAMETERS, unusedParams(subject)); } @Test - public void asInt() { + void asInt() { NodeAdapter subject = newNodeAdapterForTest("{ aInt : 5 }"); assertEquals(5, subject.of("aInt").asInt(-1)); assertEquals(-1, subject.of("missingField").asInt(-1)); } @Test - public void asLong() { + void asLong() { NodeAdapter subject = newNodeAdapterForTest("{ key : 5 }"); assertEquals(5, subject.of("key").asLong(-1)); assertEquals(-1, subject.of("missingField").asLong(-1)); } @Test - public void asText() { + void asText() { NodeAdapter subject = newNodeAdapterForTest("{ aText : 'TEXT' }"); assertEquals("TEXT", subject.of("aText").asString("DEFAULT")); assertEquals("DEFAULT", subject.of("missingField").asString("DEFAULT")); @@ -142,19 +142,19 @@ public void asText() { } @Test - public void requiredAsText() { + void requiredAsText() { NodeAdapter subject = newNodeAdapterForTest("{ }"); assertThrows(OtpAppException.class, () -> subject.of("missingField").asString()); } @Test - public void rawAsText() { + void rawAsText() { NodeAdapter subject = newNodeAdapterForTest("{ aText : 'TEXT' }"); assertEquals("TEXT", subject.of("aText").asObject().asText()); } @Test - public void asEnum() { + void asEnum() { // Given NodeAdapter subject = newNodeAdapterForTest("{ a : 'A', abc : 'a-b-c' }"); @@ -169,7 +169,7 @@ public void asEnum() { } @Test - public void asEnumWithIllegalPropertySet() { + void asEnumWithIllegalPropertySet() { // Given NodeAdapter subject = newNodeAdapterForTest( """ @@ -205,7 +205,7 @@ public void asEnumWithIllegalPropertySet() { } @Test - public void asEnumMap() { + void asEnumMap() { // With optional enum values in map NodeAdapter subject = newNodeAdapterForTest("{ key : { A: true, B: false } }"); assertEquals( @@ -220,7 +220,7 @@ public void asEnumMap() { } @Test - public void asEnumMapWithCustomType() { + void asEnumMapWithCustomType() { // With optional enum values in map NodeAdapter subject = newNodeAdapterForTest("{ key : { A: {a:'Foo'} } }"); assertEquals( @@ -235,7 +235,7 @@ public void asEnumMapWithCustomType() { } @Test - public void asEnumMapWithDefaultValue() { + void asEnumMapWithDefaultValue() { var subject = newNodeAdapterForTest("{}"); final Map dflt = Map.of(AnEnum.A, new ARecord("Foo")); assertEquals(dflt, subject.of("key").asEnumMap(AnEnum.class, ARecord::fromJson, dflt)); @@ -243,7 +243,7 @@ public void asEnumMapWithDefaultValue() { } @Test - public void asEnumMapWithUnknownKey() { + void asEnumMapWithUnknownKey() { NodeAdapter subject = newNodeAdapterForTest("{ enumMap : { unknown : 7 } }"); subject.of("enumMap").asEnumMap(AnEnum.class, Double.class); @@ -261,7 +261,7 @@ public void asEnumMapWithUnknownKey() { } @Test - public void asEnumMapAllKeysRequired() { + void asEnumMapAllKeysRequired() { var subject = newNodeAdapterForTest("{ key : { A: true, b: false, a_B_c: true } }"); assertEquals( Map.of(AnEnum.A, true, AnEnum.B, false, AnEnum.A_B_C, true), @@ -280,7 +280,7 @@ public void asEnumMapAllKeysRequired() { } @Test - public void asEnumMapWithRequiredMissingValue() { + void asEnumMapWithRequiredMissingValue() { // A value for C is missing in map NodeAdapter subject = newNodeAdapterForTest("{ key : { A: true, B: false } }"); @@ -291,7 +291,7 @@ public void asEnumMapWithRequiredMissingValue() { } @Test - public void asEnumSet() { + void asEnumSet() { NodeAdapter subject = newNodeAdapterForTest("{ key : [ 'A', 'B' ] }"); assertEquals(Set.of(AnEnum.A, AnEnum.B), subject.of("key").asEnumSet(AnEnum.class)); assertEquals(Set.of(), subject.of("missing-key").asEnumSet(AnEnum.class)); @@ -299,13 +299,13 @@ public void asEnumSet() { } @Test - public void asEnumSetFailsUsingWrongFormat() { + void asEnumSetFailsUsingWrongFormat() { NodeAdapter subject = newNodeAdapterForTest("{ key : 'A,B' }"); assertThrows(OtpAppException.class, () -> subject.of("key").asEnumSet(AnEnum.class)); } @Test - public void asFeedScopedId() { + void asFeedScopedId() { NodeAdapter subject = newNodeAdapterForTest("{ key1: 'A:23', key2: 'B:12' }"); assertEquals("A:23", subject.of("key1").asFeedScopedId(null).toString()); assertEquals("B:12", subject.of("key2").asFeedScopedId(null).toString()); @@ -316,7 +316,7 @@ public void asFeedScopedId() { } @Test - public void asFeedScopedIds() { + void asFeedScopedIds() { NodeAdapter subject = newNodeAdapterForTest("{ routes: ['A:23', 'B:12']}"); assertEquals("[A:23, B:12]", subject.of("routes").asFeedScopedIds(List.of()).toString()); assertEquals("[]", subject.of("missing-key").asFeedScopedIds(List.of()).toString()); @@ -328,7 +328,7 @@ public void asFeedScopedIds() { } @Test - public void asFeedScopedIdSet() { + void asFeedScopedIdSet() { NodeAdapter subject = newNodeAdapterForTest("{ routes: ['A:23', 'B:12', 'A:23']}"); assertEquals( List.of( @@ -342,7 +342,7 @@ public void asFeedScopedIdSet() { } @Test - public void asDateOrRelativePeriod() { + void asDateOrRelativePeriod() { // Given var subject = newNodeAdapterForTest("{ 'a' : '2020-02-28', 'b' : '-P3Y' }"); var utc = ZoneIds.UTC; @@ -362,7 +362,7 @@ public void asDateOrRelativePeriod() { } @Test - public void testParsePeriodDateThrowsException() { + void testParsePeriodDateThrowsException() { // Given NodeAdapter subject = newNodeAdapterForTest("{ 'foo' : 'bar' }"); @@ -374,7 +374,7 @@ public void testParsePeriodDateThrowsException() { } @Test - public void asDuration() { + void asDuration() { NodeAdapter subject = newNodeAdapterForTest("{ k1:'PT1s', k2:'3h2m1s', k3:7 }"); // as required duration @@ -390,13 +390,13 @@ public void asDuration() { } @Test - public void requiredAsDuration() { + void requiredAsDuration() { NodeAdapter subject = newNodeAdapterForTest("{ }"); assertThrows(OtpAppException.class, () -> subject.of("missingField").asDuration()); } @Test - public void asDurations() { + void asDurations() { NodeAdapter subject = newNodeAdapterForTest("{ key1 : ['PT1s', '2h'] }"); assertEquals("[PT1S, PT2H]", subject.of("key1").asDurations(List.of()).toString()); assertEquals("[PT3H]", subject.of("missing-key").asDurations(List.of(D3h)).toString()); @@ -404,7 +404,7 @@ public void asDurations() { } @Test - public void asLocale() { + void asLocale() { NodeAdapter subject = newNodeAdapterForTest( "{ key1 : 'no', key2 : 'no_NO', key3 : 'no_NO_NY' }" ); @@ -415,14 +415,14 @@ public void asLocale() { } @Test - public void asPattern() { + void asPattern() { NodeAdapter subject = newNodeAdapterForTest("{ key : 'Ab*a' }"); assertEquals("Ab*a", subject.of("key").asPattern("ABC").toString()); assertEquals("ABC", subject.of("missingField").asPattern("ABC").toString()); } @Test - public void uri() { + void uri() { var URL = "gs://bucket/a.obj"; NodeAdapter subject = newNodeAdapterForTest("{ aUri : '" + URL + "' }"); @@ -433,14 +433,14 @@ public void uri() { } @Test - public void uriSyntaxException() { + void uriSyntaxException() { NodeAdapter subject = newNodeAdapterForTest("{ aUri : 'error$%uri' }"); assertThrows(OtpAppException.class, () -> subject.of("aUri").asUri(null), "error$%uri"); } @Test - public void uriRequiredValueMissing() { + void uriRequiredValueMissing() { NodeAdapter subject = newNodeAdapterForTest("{ }"); assertThrows( @@ -451,7 +451,7 @@ public void uriRequiredValueMissing() { } @Test - public void uris() { + void uris() { NodeAdapter subject = newNodeAdapterForTest("{ foo : ['gs://a/b', 'gs://c/d'] }"); assertEquals("[gs://a/b, gs://c/d]", subject.of("foo").asUris().toString()); @@ -461,7 +461,7 @@ public void uris() { } @Test - public void urisNotAnArrayException() { + void urisNotAnArrayException() { NodeAdapter subject = newNodeAdapterForTest("{ 'uris': 'no array' }"); assertThrows( @@ -472,7 +472,7 @@ public void urisNotAnArrayException() { } @Test - public void objectAsList() { + void objectAsList() { NodeAdapter subject = newNodeAdapterForTest("{ key : [{ a: 'I' }, { a: '2' } ] }"); List result = subject @@ -487,21 +487,21 @@ public void objectAsList() { } @Test - public void asCostLinearFunction() { + void asCostLinearFunction() { NodeAdapter subject = newNodeAdapterForTest("{ key : '400+8x' }"); assertEquals("6m40s + 8.0 t", subject.of("key").asCostLinearFunction(null).toString()); assertNull(subject.of("no-key").asCostLinearFunction(null)); } @Test - public void asTimePenalty() { + void asTimePenalty() { NodeAdapter subject = newNodeAdapterForTest("{ key : '400+8x' }"); assertEquals("6m40s + 8.0 t", subject.of("key").asTimePenalty(null).toString()); assertNull(subject.of("no-key").asTimePenalty(null)); } @Test - public void asZoneId() { + void asZoneId() { NodeAdapter subject = newNodeAdapterForTest( "{ key1 : 'UTC', key2 : 'Europe/Oslo', key3 : '+02:00', key4: 'invalid' }" ); @@ -515,7 +515,7 @@ public void asZoneId() { } @Test - public void asTextSet() { + void asTextSet() { NodeAdapter subject = newNodeAdapterForTest("{ ids : ['A', 'C', 'F'] }"); assertEquals( Set.of("A", "C", "F"), @@ -528,7 +528,7 @@ public void asTextSet() { } @Test - public void isNonEmptyArray() { + void isNonEmptyArray() { NodeAdapter subject = newNodeAdapterForTest("{ foo : ['A'], bar: [], foobar: true }"); assertTrue(subject.of("foo").asObject().isNonEmptyArray()); assertFalse(subject.of("bar").asObject().isNonEmptyArray()); @@ -538,13 +538,13 @@ public void isNonEmptyArray() { } @Test - public void deduplicateChildren() { + void deduplicateChildren() { NodeAdapter subject = newNodeAdapterForTest("{ foo : { enabled: true } }"); assertSame(subject.of("foo").asObject(), subject.of("foo").asObject()); } @Test - public void unusedParams() { + void unusedParams() { // Given: two parameters a and b NodeAdapter subject = newNodeAdapterForTest("{ foo : { a: true, b: false } }"); var buf = new StringBuilder(); @@ -557,6 +557,29 @@ public void unusedParams() { assertEquals("Unexpected config parameter: 'foo.b:false' in 'Test'", buf.toString()); } + @Test + void unknownParameters() { + // Given: two parameters a and b + var subject = newNodeAdapterForTest("{ foo : { a: true, b: false } }"); + + // When: Access ONLY parameter 'a', but not 'b' + subject.of("foo").asObject().of("a").asBoolean(); + + assertTrue(subject.hasUnknownParameters()); + } + + @Test + void allParametersAreKnown() { + // Given: two parameters a and b + var subject = newNodeAdapterForTest("{ foo : { a: true, b: false } }"); + + var object = subject.of("foo").asObject(); + object.of("a").asBoolean(); + object.of("b").asBoolean(); + + assertFalse(subject.hasUnknownParameters()); + } + private static String unusedParams(NodeAdapter subject) { var buf = new StringBuilder(); subject.logAllWarnings(m -> buf.append('\n').append(m)); 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 d50c8a74dfc..4a0a0d1b848 100644 --- a/src/test/java/org/opentripplanner/street/model/_data/StreetModelForTest.java +++ b/src/test/java/org/opentripplanner/street/model/_data/StreetModelForTest.java @@ -9,6 +9,7 @@ import org.opentripplanner.framework.geometry.SphericalDistanceLibrary; import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.service.vehiclerental.model.TestFreeFloatingRentalVehicleBuilder; import org.opentripplanner.service.vehiclerental.street.VehicleRentalPlaceVertex; import org.opentripplanner.street.model.RentalFormFactor; @@ -29,16 +30,16 @@ public class StreetModelForTest { public static StreetVertex V4 = intersectionVertex("V4", 3, 3); public static IntersectionVertex intersectionVertex(Coordinate c) { - return intersectionVertex(c.x, c.y); + return intersectionVertex(c.y, c.x); } public static IntersectionVertex intersectionVertex(double lat, double lon) { var label = "%s_%s".formatted(lat, lon); - return new LabelledIntersectionVertex(label, lat, lon, false, false); + return new LabelledIntersectionVertex(label, lon, lat, false, false); } public static IntersectionVertex intersectionVertex(String label, double lat, double lon) { - return new LabelledIntersectionVertex(label, lat, lon, false, false); + return new LabelledIntersectionVertex(label, lon, lat, false, false); } @Nonnull @@ -111,4 +112,8 @@ public static VehicleRentalPlaceVertex rentalVertex(RentalFormFactor formFactor) } return new VehicleRentalPlaceVertex(rentalVehicleBuilder.build()); } + + public static VehicleParking.VehicleParkingBuilder vehicleParking() { + return VehicleParking.builder().id(id("vehicle-parking-1")).coordinate(WgsCoordinate.GREENWICH); + } } diff --git a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java index 318f133f15e..7de7b0c7ce6 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeTest.java @@ -46,7 +46,7 @@ public class StreetEdgeTest { public void before() { v0 = intersectionVertex("maple_0th", 0.0, 0.0); // label, X, Y v1 = intersectionVertex("maple_1st", 2.0, 2.0); - v2 = intersectionVertex("maple_2nd", 1.0, 2.0); + v2 = intersectionVertex("maple_2nd", 2.0, 1.0); this.proto = StreetSearchRequest @@ -73,7 +73,7 @@ public void testInAndOutAngles() { assertEquals(90, e1.getOutAngle()); // 2 new ones - StreetVertex u = intersectionVertex("test1", 2.0, 1.0); + StreetVertex u = intersectionVertex("test1", 1.0, 2.0); StreetVertex v = intersectionVertex("test2", 2.0, 2.0); // Second edge, heading straight North diff --git a/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingEdgeTest.java b/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingEdgeTest.java index 7dad61fbd6b..81bee23ad54 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingEdgeTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingEdgeTest.java @@ -10,6 +10,7 @@ import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.street.search.request.StreetSearchRequest; import org.opentripplanner.street.search.state.State; @@ -130,8 +131,8 @@ private VehicleParking createVehicleParking( boolean carPlaces, VehicleParkingSpaces availability ) { - return VehicleParking - .builder() + return StreetModelForTest + .vehicleParking() .id(TransitModelForTest.id("VehicleParking")) .bicyclePlaces(bicyclePlaces) .carPlaces(carPlaces) diff --git a/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingPreferredTagsTest.java b/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingPreferredTagsTest.java index 5969121b6d6..579ae4e964d 100644 --- a/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingPreferredTagsTest.java +++ b/src/test/java/org/opentripplanner/street/model/edge/VehicleParkingPreferredTagsTest.java @@ -11,9 +11,9 @@ import org.opentripplanner.framework.geometry.WgsCoordinate; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.routing.api.request.StreetMode; -import org.opentripplanner.routing.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingEntrance; import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.search.request.StreetSearchRequest; @@ -62,9 +62,8 @@ private void runTest( double expectedCost, boolean arriveBy ) { - var parking = VehicleParking - .builder() - .coordinate(COORDINATE) + var parking = StreetModelForTest + .vehicleParking() .tags(parkingTags) .availability(VehicleParkingSpaces.builder().bicycleSpaces(100).build()) .bicyclePlaces(true) diff --git a/src/test/java/org/opentripplanner/transit/speed_test/support/AssertSpeedTestSetup.java b/src/test/java/org/opentripplanner/transit/speed_test/support/AssertSpeedTestSetup.java index 031df343a9a..4113d5979b1 100644 --- a/src/test/java/org/opentripplanner/transit/speed_test/support/AssertSpeedTestSetup.java +++ b/src/test/java/org/opentripplanner/transit/speed_test/support/AssertSpeedTestSetup.java @@ -15,7 +15,7 @@ public static void assertTestDateHasData( ) { int numberOfPatternForTestDate = transitModel .getTransitLayer() - .getTripPatternsForDate(config.testDate) + .getTripPatternsForRunningDate(config.testDate) .size(); if (numberOfPatternForTestDate < 10) { diff --git a/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java b/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java index 2bcb49defae..a31b5cfb387 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java @@ -16,6 +16,7 @@ import org.opentripplanner.routing.vehicle_parking.VehicleParkingState; import org.opentripplanner.routing.vehicle_parking.VehicleParkingTestGraphData; import org.opentripplanner.routing.vehicle_parking.VehicleParkingTestUtil; +import org.opentripplanner.street.model._data.StreetModelForTest; import org.opentripplanner.street.model.edge.StreetVehicleParkingLink; import org.opentripplanner.street.model.edge.VehicleParkingEdge; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; @@ -142,7 +143,10 @@ void deleteVehicleParkingTest() { @Test void addNotOperatingVehicleParkingTest() { - var vehicleParking = VehicleParking.builder().state(VehicleParkingState.CLOSED).build(); + var vehicleParking = StreetModelForTest + .vehicleParking() + .state(VehicleParkingState.CLOSED) + .build(); when(dataSource.getUpdates()).thenReturn(List.of(vehicleParking)); runUpdaterOnce(); @@ -155,8 +159,8 @@ void addNotOperatingVehicleParkingTest() { void updateNotOperatingVehicleParkingTest() { var vehiclePlaces = VehicleParkingSpaces.builder().bicycleSpaces(1).build(); - var vehicleParking = VehicleParking - .builder() + var vehicleParking = StreetModelForTest + .vehicleParking() .availability(vehiclePlaces) .state(VehicleParkingState.CLOSED) .build(); @@ -175,8 +179,8 @@ void updateNotOperatingVehicleParkingTest() { vehiclePlaces = VehicleParkingSpaces.builder().bicycleSpaces(2).build(); vehicleParking = - VehicleParking - .builder() + StreetModelForTest + .vehicleParking() .availability(vehiclePlaces) .state(VehicleParkingState.CLOSED) .build(); @@ -194,7 +198,10 @@ void updateNotOperatingVehicleParkingTest() { @Test void deleteNotOperatingVehicleParkingTest() { - var vehicleParking = VehicleParking.builder().state(VehicleParkingState.CLOSED).build(); + var vehicleParking = StreetModelForTest + .vehicleParking() + .state(VehicleParkingState.CLOSED) + .build(); when(dataSource.getUpdates()).thenReturn(List.of(vehicleParking)); runUpdaterOnce(); diff --git a/src/test/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdaterTest.java b/src/test/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdaterTest.java index 09dd1cf1cb9..99910e04f7c 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdaterTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_rental/GeofencingVertexUpdaterTest.java @@ -8,10 +8,10 @@ import java.util.List; import org.junit.jupiter.api.Test; -import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.MultiPolygon; import org.locationtech.jts.geom.Polygon; +import org.opentripplanner._support.geometry.Polygons; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.service.vehiclerental.model.GeofencingZone; import org.opentripplanner.service.vehiclerental.street.BusinessAreaBorder; @@ -35,30 +35,16 @@ class GeofencingVertexUpdaterTest { final GeofencingVertexUpdater updater = new GeofencingVertexUpdater(ignored -> List.of(insideFrognerPark, halfInHalfOutFrognerPark, businessBorder) ); - StreetEdge outsideFrognerPark = streetEdge(outsideFrognerPark1, outsideFrognerPark2); - - GeometryFactory fac = GeometryUtils.getGeometryFactory(); - Polygon frognerPark = fac.createPolygon( - new Coordinate[] { - new Coordinate(59.93112978539807, 10.691099320272173), - new Coordinate(59.92231848097069, 10.691099320272173), - new Coordinate(59.92231848097069, 10.711758464910503), - new Coordinate(59.92231848097069, 10.691099320272173), - new Coordinate(59.93112978539807, 10.691099320272173), - } - ); - final GeofencingZone zone = new GeofencingZone(id("frogner-park"), frognerPark, true, false); - Polygon osloPolygon = fac.createPolygon( - new Coordinate[] { - new Coordinate(59.961055202323195, 10.62535658370308), - new Coordinate(59.889009435700416, 10.62535658370308), - new Coordinate(59.889009435700416, 10.849791142928694), - new Coordinate(59.961055202323195, 10.849791142928694), - new Coordinate(59.961055202323195, 10.62535658370308), - } + + static GeometryFactory fac = GeometryUtils.getGeometryFactory(); + final GeofencingZone zone = new GeofencingZone( + id("frogner-park"), + Polygons.OSLO_FROGNER_PARK, + true, + false ); - MultiPolygon osloMultiPolygon = fac.createMultiPolygon(new Polygon[] { osloPolygon }); + MultiPolygon osloMultiPolygon = fac.createMultiPolygon(new Polygon[] { Polygons.OSLO }); final GeofencingZone businessArea = new GeofencingZone( id("oslo"), osloMultiPolygon, diff --git a/src/test/resources/standalone/config/router-config.json b/src/test/resources/standalone/config/router-config.json index 3a07cddfeb8..3b43ef1c5d2 100644 --- a/src/test/resources/standalone/config/router-config.json +++ b/src/test/resources/standalone/config/router-config.json @@ -334,6 +334,12 @@ "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", @@ -414,6 +420,12 @@ "fromDateTime": "-P1D", "timeout": 300000 } + }, + { + "type": "vehicle-parking", + "feedId": "bikeep", + "sourceType": "bikeep", + "url": "https://services.bikeep.com/location/v1/public-areas/no-baia-mobility/locations" } ], "rideHailingServices": [