diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml index 5a48da6bc44..2853c7ee00b 100644 --- a/.github/workflows/cibuild.yml +++ b/.github/workflows/cibuild.yml @@ -51,9 +51,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..5e016c22bb2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ ## Overview [![Join the chat at https://gitter.im/opentripplanner/OpenTripPLanner](https://badges.gitter.im/opentripplanner/OpenTripPlanner.svg)](https://gitter.im/opentripplanner/OpenTripPlanner) +[![Matrix](https://img.shields.io/matrix/opentripplanner%3Amatrix.org?label=Matrix%20chat)](https://matrix.to/#/#opentripplanner_OpenTripPlanner:gitter.im) [![codecov](https://codecov.io/gh/opentripplanner/OpenTripPlanner/branch/dev-2.x/graph/badge.svg?token=ak4PbIKgZ1)](https://codecov.io/gh/opentripplanner/OpenTripPlanner) +[![Commit activity](https://img.shields.io/github/commit-activity/y/opentripplanner/OpenTripPlanner)](https://github.com/opentripplanner/OpenTripPlanner/graphs/contributors) [![Docker Pulls](https://img.shields.io/docker/pulls/opentripplanner/opentripplanner)](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 d3b8c10754b..08a9146ceb1 100644 --- a/client-next/package-lock.json +++ b/client-next/package-lock.json @@ -23,13 +23,13 @@ "@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.73", - "@types/react-dom": "18.2.23", - "@typescript-eslint/eslint-plugin": "7.5.0", - "@typescript-eslint/parser": "7.5.0", + "@testing-library/react": "15.0.2", + "@types/react": "18.2.79", + "@types/react-dom": "18.2.25", + "@typescript-eslint/eslint-plugin": "7.7.0", + "@typescript-eslint/parser": "7.7.0", "@vitejs/plugin-react": "4.2.1", - "@vitest/coverage-v8": "1.4.0", + "@vitest/coverage-v8": "1.5.0", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.29.1", @@ -39,9 +39,9 @@ "eslint-plugin-react-refresh": "0.4.6", "jsdom": "24.0.0", "prettier": "3.2.5", - "typescript": "5.4.3", - "vite": "5.2.7", - "vitest": "1.4.0" + "typescript": "5.4.5", + "vite": "5.2.8", + "vitest": "1.5.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -3353,36 +3353,36 @@ } }, "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.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-15.0.2.tgz", + "integrity": "sha512-5mzIpuytB1ctpyywvyaY2TAAUQVCZIGqwiqFQf6u9lvj/SJQepGUzNV18Xpk+NLCaCE2j7CWrZE0tEf9xLZYiQ==", "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": { "react": "^18.0.0", @@ -3455,12 +3455,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,18 +3522,18 @@ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/react": { - "version": "18.2.73", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.73.tgz", - "integrity": "sha512-XcGdod0Jjv84HOC7N5ziY3x+qL0AfmubvKOZ9hJjJ2yd5EE+KYjWhdOjt387e9HPheHkdggF9atTifMRtyAaRA==", + "version": "18.2.79", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz", + "integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.2.23", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.23.tgz", - "integrity": "sha512-ZQ71wgGOTmDYpnav2knkjr3qXdAFu0vsk8Ci5w3pGAIdj7/kKAyn+VsQDhXsmzzzepAiI9leWMmubXz690AI/A==", + "version": "18.2.25", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.25.tgz", + "integrity": "sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==", "dev": true, "dependencies": { "@types/react": "*" @@ -3582,22 +3576,22 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.5.0.tgz", - "integrity": "sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.0.tgz", + "integrity": "sha512-GJWR0YnfrKnsRoluVO3PRb9r5aMZriiMMM/RHj5nnTrBy1/wIgk76XCtCKcnXGjpZQJQRFtGV9/0JJ6n30uwpQ==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/type-utils": "7.5.0", - "@typescript-eslint/utils": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.7.0", + "@typescript-eslint/type-utils": "7.7.0", + "@typescript-eslint/utils": "7.7.0", + "@typescript-eslint/visitor-keys": "7.7.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" @@ -3650,15 +3644,15 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.5.0.tgz", - "integrity": "sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.7.0.tgz", + "integrity": "sha512-fNcDm3wSwVM8QYL4HKVBggdIPAy9Q41vcvC/GtDobw3c4ndVT3K6cqudUmjHPw8EAp4ufax0o58/xvWaP2FmTg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.5.0", - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/typescript-estree": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@typescript-eslint/scope-manager": "7.7.0", + "@typescript-eslint/types": "7.7.0", + "@typescript-eslint/typescript-estree": "7.7.0", + "@typescript-eslint/visitor-keys": "7.7.0", "debug": "^4.3.4" }, "engines": { @@ -3678,13 +3672,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.5.0.tgz", - "integrity": "sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.0.tgz", + "integrity": "sha512-/8INDn0YLInbe9Wt7dK4cXLDYp0fNHP5xKLHvZl3mOT5X17rK/YShXaiNmorl+/U4VKCVIjJnx4Ri5b0y+HClw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0" + "@typescript-eslint/types": "7.7.0", + "@typescript-eslint/visitor-keys": "7.7.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3695,15 +3689,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.5.0.tgz", - "integrity": "sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.7.0.tgz", + "integrity": "sha512-bOp3ejoRYrhAlnT/bozNQi3nio9tIgv3U5C0mVDdZC7cpcQEDZXvq8inrHYghLVwuNABRqrMW5tzAv88Vy77Sg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.5.0", - "@typescript-eslint/utils": "7.5.0", + "@typescript-eslint/typescript-estree": "7.7.0", + "@typescript-eslint/utils": "7.7.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" @@ -3722,9 +3716,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.5.0.tgz", - "integrity": "sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.0.tgz", + "integrity": "sha512-G01YPZ1Bd2hn+KPpIbrAhEWOn5lQBrjxkzHkWvP6NucMXFtfXoevK82hzQdpfuQYuhkvFDeQYbzXCjR1z9Z03w==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3735,19 +3729,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.5.0.tgz", - "integrity": "sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.0.tgz", + "integrity": "sha512-8p71HQPE6CbxIBy2kWHqM1KGrC07pk6RJn40n0DSc6bMOBBREZxSDJ+BmRzc8B5OdaMh1ty3mkuWRg4sCFiDQQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/visitor-keys": "7.5.0", + "@typescript-eslint/types": "7.7.0", + "@typescript-eslint/visitor-keys": "7.7.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" @@ -3796,18 +3790,18 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.5.0.tgz", - "integrity": "sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.7.0.tgz", + "integrity": "sha512-LKGAXMPQs8U/zMRFXDZOzmMKgFv3COlxUQ+2NMPhbqgVm6R1w+nU1i4836Pmxu9jZAuIeyySNrN/6Rc657ggig==", "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.5.0", - "@typescript-eslint/types": "7.5.0", - "@typescript-eslint/typescript-estree": "7.5.0", - "semver": "^7.5.4" + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "7.7.0", + "@typescript-eslint/types": "7.7.0", + "@typescript-eslint/typescript-estree": "7.7.0", + "semver": "^7.6.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3854,13 +3848,13 @@ "dev": true }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.5.0.tgz", - "integrity": "sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.0.tgz", + "integrity": "sha512-h0WHOj8MhdhY8YWkzIF30R379y0NqyOHExI9N9KCzvmu05EgG4FumeYa3ccfKUSphyWkWQE1ybVrgz/Pbam6YA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.5.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.7.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -3896,9 +3890,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.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.5.0.tgz", + "integrity": "sha512-1igVwlcqw1QUMdfcMlzzY4coikSIBN944pkueGi0pawrX5I5Z+9hxdTR+w3Sg6Q3eZhvdMAs8ZaF9JuTG1uYOQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.1", @@ -3913,24 +3907,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.0" } }, "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.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.0.tgz", + "integrity": "sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==", "dev": true, "dependencies": { - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", "chai": "^4.3.10" }, "funding": { @@ -3938,12 +3931,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.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.0.tgz", + "integrity": "sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==", "dev": true, "dependencies": { - "@vitest/utils": "1.4.0", + "@vitest/utils": "1.5.0", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -3979,9 +3972,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.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.0.tgz", + "integrity": "sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -4025,9 +4018,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.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.0.tgz", + "integrity": "sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -4037,9 +4030,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.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.0.tgz", + "integrity": "sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -4232,12 +4225,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": { @@ -5284,38 +5277,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", @@ -5613,26 +5574,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", @@ -5986,15 +5927,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", @@ -7311,22 +7243,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", @@ -8532,9 +8448,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" @@ -8744,22 +8660,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", @@ -10164,18 +10064,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", @@ -10723,9 +10611,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", @@ -10944,20 +10832,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", @@ -10968,9 +10842,9 @@ } }, "node_modules/vite": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.7.tgz", - "integrity": "sha512-k14PWOKLI6pMaSzAuGtT+Cf0YmIx12z9YGon39onaJNy8DLBfBJrzg9FQEmkAM5lpHBZs9wksWAsyF/HkpEwJA==", + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz", + "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==", "dev": true, "dependencies": { "esbuild": "^0.20.1", @@ -11023,9 +10897,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.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.0.tgz", + "integrity": "sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -11045,16 +10919,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.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.0.tgz", + "integrity": "sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==", "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.0", + "@vitest/runner": "1.5.0", + "@vitest/snapshot": "1.5.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -11066,9 +10940,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.0", "why-is-node-running": "^2.2.2" }, "bin": { @@ -11083,8 +10957,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.0", + "@vitest/ui": "1.5.0", "happy-dom": "*", "jsdom": "*" }, diff --git a/client-next/package.json b/client-next/package.json index d3f583057d8..643acd1c082 100644 --- a/client-next/package.json +++ b/client-next/package.json @@ -32,13 +32,13 @@ "@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.73", - "@types/react-dom": "18.2.23", - "@typescript-eslint/eslint-plugin": "7.5.0", - "@typescript-eslint/parser": "7.5.0", + "@testing-library/react": "15.0.2", + "@types/react": "18.2.79", + "@types/react-dom": "18.2.25", + "@typescript-eslint/eslint-plugin": "7.7.0", + "@typescript-eslint/parser": "7.7.0", "@vitejs/plugin-react": "4.2.1", - "@vitest/coverage-v8": "1.4.0", + "@vitest/coverage-v8": "1.5.0", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.29.1", @@ -48,8 +48,8 @@ "eslint-plugin-react-refresh": "0.4.6", "jsdom": "24.0.0", "prettier": "3.2.5", - "typescript": "5.4.3", - "vite": "5.2.7", - "vitest": "1.4.0" + "typescript": "5.4.5", + "vite": "5.2.8", + "vitest": "1.5.0" } } diff --git a/doc-templates/VehicleParking.md b/doc-templates/VehicleParking.md index 37988ef7902..50bd8806267 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 and NOI 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,10 @@ All updaters have the following parameters in common: +## NOI Open Data Hub + + + ## Changelog diff --git a/docs/Changelog.md b/docs/Changelog.md index e1773cb0a5e..25c8cb80594 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -8,6 +8,12 @@ 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) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.5.0 (2024-03-13) diff --git a/docs/RouterConfiguration.md b/docs/RouterConfiguration.md index 503b8c7370f..e0f1d81b590 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", 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 -

backwardsDelayPropagationType

+

backwardsDelayPropagationType

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

url

+

url

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

headers

+

headers

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

backwardsDelayPropagationType

+

backwardsDelayPropagationType

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

features

+

features

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

headers

+

headers

**Since version:** `2.3` ∙ **Type:** `map of string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[7] +**Path:** /updaters/[8] HTTP headers to add to the request. Any header key, value can be inserted. diff --git a/docs/sandbox/VehicleParking.md b/docs/sandbox/VehicleParking.md index 4bfc2f6bd36..2721fff9b0c 100644 --- a/docs/sandbox/VehicleParking.md +++ b/docs/sandbox/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 and NOI 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 @@ -60,7 +61,7 @@ This will end up in the API responses as the feed id of of the parking lot. **Since version:** `2.2` ∙ **Type:** `enum` ∙ **Cardinality:** `Required` **Path:** /updaters/[2] -**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` +**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub` The source of the vehicle updates. @@ -110,7 +111,7 @@ Used for converting abstract opening hours into concrete points in time. | frequency | `duration` | How often to update the source. | *Optional* | `"PT1M"` | 2.2 | | [sourceType](#u__3__sourceType) | `enum` | The source of the vehicle updates. | *Required* | | 2.2 | | [timeZone](#u__3__timeZone) | `time-zone` | The time zone of the feed. | *Optional* | | 2.2 | -| url | `string` | URL of the resource. | *Optional* | | 2.2 | +| url | `string` | URL of the resource. | *Required* | | 2.2 | | [headers](#u__3__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.2 | | [tags](#u__3__tags) | `string[]` | Tags to add to the parking lots. | *Optional* | | 2.2 | @@ -130,7 +131,7 @@ This will end up in the API responses as the feed id of of the parking lot. **Since version:** `2.2` ∙ **Type:** `enum` ∙ **Cardinality:** `Required` **Path:** /updaters/[3] -**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` +**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub` The source of the vehicle updates. @@ -196,7 +197,7 @@ Tags to add to the parking lots. | [feedId](#u__4__feedId) | `string` | The name of the data source. | *Required* | | 2.2 | | frequency | `duration` | How often to update the source. | *Optional* | `"PT1M"` | 2.3 | | [sourceType](#u__4__sourceType) | `enum` | The source of the vehicle updates. | *Required* | | 2.2 | -| url | `uri` | URL of the locations endpoint. | *Optional* | | 2.3 | +| url | `uri` | URL of the locations endpoint. | *Required* | | 2.3 | | [headers](#u__4__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.3 | @@ -215,7 +216,7 @@ This will end up in the API responses as the feed id of of the parking lot. **Since version:** `2.2` ∙ **Type:** `enum` ∙ **Cardinality:** `Required` **Path:** /updaters/[4] -**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` +**Enum values:** `park-api` | `bicycle-park-api` | `hsl-park` | `bikely` | `noi-open-data-hub` The source of the vehicle updates. @@ -250,6 +251,67 @@ HTTP headers to add to the request. Any header key, value can be inserted. +## NOI Open Data Hub + + + + +| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | +|---------------------------------|:---------------:|----------------------------------------------------------------------------|:----------:|---------------|:-----:| +| type = "vehicle-parking" | `enum` | The type of the updater. | *Required* | | 1.5 | +| [feedId](#u__5__feedId) | `string` | The name of the data source. | *Required* | | 2.2 | +| frequency | `duration` | How often to update the source. | *Optional* | `"PT1M"` | 2.6 | +| [sourceType](#u__5__sourceType) | `enum` | The source of the vehicle updates. | *Required* | | 2.2 | +| url | `uri` | URL of the locations endpoint. | *Required* | | 2.6 | +| [headers](#u__5__headers) | `map of string` | HTTP headers to add to the request. Any header key, value can be inserted. | *Optional* | | 2.6 | + + +#### Details + +

feedId

+ +**Since version:** `2.2` ∙ **Type:** `string` ∙ **Cardinality:** `Required` +**Path:** /updaters/[5] + +The name of the data source. + +This will end up in the API responses as the feed id of of the parking lot. + +

sourceType

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

headers

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

authenticationType

+

authenticationType

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

customMidnight

+

customMidnight

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

fullyQualifiedNamespace

+

fullyQualifiedNamespace

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

servicebus-url

+

servicebus-url

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

authenticationType

+

authenticationType

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

customMidnight

+

customMidnight

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

fullyQualifiedNamespace

+

fullyQualifiedNamespace

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

servicebus-url

+

servicebus-url

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

url

+

url

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

headers

+

headers

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

earlyStart

+

earlyStart

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

url

+

url

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

headers

+

headers

**Since version:** `2.3` ∙ **Type:** `map of string` ∙ **Cardinality:** `Optional` -**Path:** /updaters/[9] +**Path:** /updaters/[10] HTTP headers to add to the request. Any header key, value can be inserted. diff --git a/pom.xml b/pom.xml index 0de0e7ef711..66d2919b3c4 100644 --- a/pom.xml +++ b/pom.xml @@ -59,15 +59,15 @@ 149 31.0 - 2.51 + 2.51.1 2.17.0 - 3.1.5 + 3.1.6 5.10.2 1.12.3 5.5.3 1.5.3 - 9.9.1 - 2.0.12 + 9.10.0 + 2.0.13 2.0.15 1.26 4.0.5 @@ -158,7 +158,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.3.0 + 3.4.0 @@ -185,7 +185,7 @@ org.apache.maven.plugins maven-source-plugin - 3.3.0 + 3.3.1 attach-sources @@ -312,7 +312,7 @@ org.jacoco jacoco-maven-plugin - 0.8.11 + 0.8.12 - - com.googlecode.json-simple - json-simple - 1.1.1 - - - - junit - junit - - - - @@ -688,7 +674,7 @@ org.entur.gbfs gbfs-java-model - 3.0.26 + 3.1.1 @@ -707,7 +693,7 @@ com.tngtech.archunit archunit - 1.2.1 + 1.3.0 test diff --git a/renovate.json5 b/renovate.json5 index 8a7f57e6764..b3472c1a0d1 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -46,14 +46,21 @@ // noise, so we slow it down a bit { "matchPackageNames": [ - "org.entur.gbfs:gbfs-java-model", - "ch.qos.logback:logback-classic", - "io.github.git-commit-id:git-commit-id-maven-plugin" + "org.entur.gbfs:gbfs-java-model" ], "matchUpdateTypes": ["patch"], "schedule": "on the 18th day of the month", "automerge": true }, + { + "matchPackageNames": [ + "ch.qos.logback:logback-classic", + "io.github.git-commit-id:git-commit-id-maven-plugin", + "org.apache.maven.plugins:maven-gpg-plugin" + ], + "schedule": "on the 19th day of the month", + "automerge": true + }, { // https://github.com/graphql-java-kickstart/renovate-config/blob/main/default.json "description": "GraphQL Java (ignoring snapshot builds)", @@ -110,12 +117,12 @@ "org.apache.commons:commons-compress", // only used by tests // maven plugins "org.codehaus.mojo:build-helper-maven-plugin", - "org.apache.maven.plugins:maven-gpg-plugin", "org.apache.maven.plugins:maven-source-plugin", "com.hubspot.maven.plugins:prettier-maven-plugin", "com.google.cloud.tools:jib-maven-plugin", "org.apache.maven.plugins:maven-shade-plugin", - "org.apache.maven.plugins:maven-compiler-plugin" + "org.apache.maven.plugins:maven-compiler-plugin", + "org.apache.maven.plugins:maven-jar-plugin" ], "matchPackagePrefixes": [ "org.junit.jupiter:", diff --git a/src/client/debug-client-preview/index.html b/src/client/debug-client-preview/index.html index 3973571de30..1b561ffc656 100644 --- a/src/client/debug-client-preview/index.html +++ b/src/client/debug-client-preview/index.html @@ -5,8 +5,8 @@ OTP Debug Client - - + +
diff --git a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/TestTransitService.java b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/TestTransitService.java new file mode 100644 index 00000000000..d187b611ef3 --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/TestTransitService.java @@ -0,0 +1,23 @@ +package org.opentripplanner.ext.vectortiles.layers; + +import java.util.Set; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.basic.TransitMode; +import org.opentripplanner.transit.model.network.Route; +import org.opentripplanner.transit.model.site.StopLocation; +import org.opentripplanner.transit.service.DefaultTransitService; +import org.opentripplanner.transit.service.TransitModel; + +public class TestTransitService extends DefaultTransitService { + + public TestTransitService(TransitModel transitModel) { + super(transitModel); + } + + @Override + public Set getRoutesForStop(StopLocation stop) { + return Set.of( + TransitModelForTest.route("1").withMode(TransitMode.RAIL).withGtfsType(100).build() + ); + } +} diff --git a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/stations/DigitransitStationPropertyMapperTest.java b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/stations/DigitransitStationPropertyMapperTest.java new file mode 100644 index 00000000000..da85285954d --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/stations/DigitransitStationPropertyMapperTest.java @@ -0,0 +1,46 @@ +package org.opentripplanner.ext.vectortiles.layers.stations; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.transit.model._data.TransitModelForTest.id; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.opentripplanner.ext.vectortiles.layers.TestTransitService; +import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.framework.Deduplicator; +import org.opentripplanner.transit.model.site.Station; +import org.opentripplanner.transit.service.StopModel; +import org.opentripplanner.transit.service.TransitModel; + +public class DigitransitStationPropertyMapperTest { + + @Test + void map() { + var deduplicator = new Deduplicator(); + var transitModel = new TransitModel(new StopModel(), deduplicator); + transitModel.index(); + var transitService = new TestTransitService(transitModel); + + var mapper = DigitransitStationPropertyMapper.create(transitService, Locale.US); + + var station = Station + .of(id("a-station")) + .withCoordinate(1, 1) + .withName(I18NString.of("A station")) + .build(); + + TransitModelForTest.of().stop("stop-1").withParentStation(station).build(); + + Map map = new HashMap<>(); + mapper.map(station).forEach(o -> map.put(o.key(), o.value())); + + assertEquals("F:a-station", map.get("gtfsId")); + assertEquals("A station", map.get("name")); + assertEquals("", map.get("type")); + assertEquals("[{\"mode\":\"RAIL\",\"shortName\":\"R1\"}]", map.get("routes")); + assertEquals("[\"F:stop-1\"]", map.get("stops")); + } +} diff --git a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/stops/RealtimeStopsLayerTest.java b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/stops/RealtimeStopsLayerTest.java new file mode 100644 index 00000000000..8f2e1f94fa6 --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/stops/RealtimeStopsLayerTest.java @@ -0,0 +1,104 @@ +package org.opentripplanner.ext.vectortiles.layers.stops; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.framework.time.TimeUtils.time; +import static org.opentripplanner.model.plan.TestItineraryBuilder.newItinerary; + +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opentripplanner._support.time.ZoneIds; +import org.opentripplanner.ext.realtimeresolver.RealtimeResolver; +import org.opentripplanner.framework.i18n.I18NString; +import org.opentripplanner.model.plan.Place; +import org.opentripplanner.routing.alertpatch.AlertEffect; +import org.opentripplanner.routing.alertpatch.EntitySelector; +import org.opentripplanner.routing.alertpatch.TimePeriod; +import org.opentripplanner.routing.alertpatch.TransitAlert; +import org.opentripplanner.routing.impl.TransitAlertServiceImpl; +import org.opentripplanner.routing.services.TransitAlertService; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.framework.Deduplicator; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.network.Route; +import org.opentripplanner.transit.model.site.RegularStop; +import org.opentripplanner.transit.service.DefaultTransitService; +import org.opentripplanner.transit.service.StopModel; +import org.opentripplanner.transit.service.TransitModel; + +public class RealtimeStopsLayerTest { + + private RegularStop stop; + private RegularStop stop2; + + @BeforeEach + public void setUp() { + var name = I18NString.of("name"); + var desc = I18NString.of("desc"); + stop = + StopModel + .of() + .regularStop(new FeedScopedId("F", "name")) + .withName(name) + .withDescription(desc) + .withCoordinate(50, 10) + .build(); + stop2 = + StopModel + .of() + .regularStop(new FeedScopedId("F", "name")) + .withName(name) + .withDescription(desc) + .withCoordinate(51, 10) + .build(); + } + + @Test + void realtimeStopLayer() { + var deduplicator = new Deduplicator(); + var transitModel = new TransitModel(new StopModel(), deduplicator); + transitModel.initTimeZone(ZoneIds.HELSINKI); + transitModel.index(); + var alertService = new TransitAlertServiceImpl(transitModel); + var transitService = new DefaultTransitService(transitModel) { + @Override + public TransitAlertService getTransitAlertService() { + return alertService; + } + }; + + Route route = TransitModelForTest.route("route").build(); + var itinerary = newItinerary(Place.forStop(stop), time("11:00")) + .bus(route, 1, time("11:05"), time("11:20"), Place.forStop(stop2)) + .build(); + var startDate = ZonedDateTime.now(ZoneIds.HELSINKI).minusDays(1).toEpochSecond(); + var endDate = ZonedDateTime.now(ZoneIds.HELSINKI).plusDays(1).toEpochSecond(); + var alert = TransitAlert + .of(stop.getId()) + .addEntity(new EntitySelector.Stop(stop.getId())) + .addTimePeriod(new TimePeriod(startDate, endDate)) + .withEffect(AlertEffect.NO_SERVICE) + .build(); + transitService.getTransitAlertService().setAlerts(List.of(alert)); + + var itineraries = List.of(itinerary); + RealtimeResolver.populateLegsWithRealtime(itineraries, transitService); + + DigitransitRealtimeStopPropertyMapper mapper = new DigitransitRealtimeStopPropertyMapper( + transitService, + new Locale("en-US") + ); + + Map map = new HashMap<>(); + mapper.map(stop).forEach(o -> map.put(o.key(), o.value())); + + assertEquals("F:name", map.get("gtfsId")); + assertEquals("name", map.get("name")); + assertEquals("desc", map.get("desc")); + assertEquals(true, map.get("closedByServiceAlert")); + } +} diff --git a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/stops/StopsLayerTest.java b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/stops/StopsLayerTest.java index a914cf9db6c..4c3e60701bd 100644 --- a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/stops/StopsLayerTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/stops/StopsLayerTest.java @@ -1,32 +1,16 @@ package org.opentripplanner.ext.vectortiles.layers.stops; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.opentripplanner.framework.time.TimeUtils.time; -import static org.opentripplanner.model.plan.TestItineraryBuilder.newItinerary; -import java.time.Instant; -import java.time.LocalDate; -import java.time.ZonedDateTime; import java.util.HashMap; -import java.util.List; import java.util.Locale; import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.opentripplanner._support.time.ZoneIds; -import org.opentripplanner.ext.realtimeresolver.RealtimeResolver; +import org.opentripplanner.ext.vectortiles.layers.TestTransitService; import org.opentripplanner.framework.i18n.TranslatedString; -import org.opentripplanner.model.plan.Place; -import org.opentripplanner.routing.alertpatch.AlertEffect; -import org.opentripplanner.routing.alertpatch.EntitySelector; -import org.opentripplanner.routing.alertpatch.TimePeriod; -import org.opentripplanner.routing.alertpatch.TransitAlert; -import org.opentripplanner.routing.impl.TransitAlertServiceImpl; -import org.opentripplanner.routing.services.TransitAlertService; -import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; -import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.StopModel; @@ -35,7 +19,6 @@ public class StopsLayerTest { private RegularStop stop; - private RegularStop stop2; @BeforeEach public void setUp() { @@ -67,14 +50,6 @@ public void setUp() { .withDescription(descTranslations) .withCoordinate(50, 10) .build(); - stop2 = - StopModel - .of() - .regularStop(new FeedScopedId("F", "name")) - .withName(nameTranslations) - .withDescription(descTranslations) - .withCoordinate(51, 10) - .build(); } @Test @@ -82,7 +57,7 @@ public void digitransitStopPropertyMapperTest() { var deduplicator = new Deduplicator(); var transitModel = new TransitModel(new StopModel(), deduplicator); transitModel.index(); - var transitService = new DefaultTransitService(transitModel); + var transitService = new TestTransitService(transitModel); DigitransitStopPropertyMapper mapper = DigitransitStopPropertyMapper.create( transitService, @@ -95,6 +70,7 @@ public void digitransitStopPropertyMapperTest() { assertEquals("F:name", map.get("gtfsId")); assertEquals("name", map.get("name")); assertEquals("desc", map.get("desc")); + assertEquals("[{\"gtfsType\":100}]", map.get("routes")); } @Test @@ -115,49 +91,4 @@ public void digitransitStopPropertyMapperTranslationTest() { assertEquals("nameDE", map.get("name")); assertEquals("descDE", map.get("desc")); } - - @Test - public void digitransitRealtimeStopPropertyMapperTest() { - var deduplicator = new Deduplicator(); - var transitModel = new TransitModel(new StopModel(), deduplicator); - transitModel.initTimeZone(ZoneIds.HELSINKI); - transitModel.index(); - var alertService = new TransitAlertServiceImpl(transitModel); - var transitService = new DefaultTransitService(transitModel) { - @Override - public TransitAlertService getTransitAlertService() { - return alertService; - } - }; - - Route route = TransitModelForTest.route("route").build(); - var itinerary = newItinerary(Place.forStop(stop), time("11:00")) - .bus(route, 1, time("11:05"), time("11:20"), Place.forStop(stop2)) - .build(); - var startDate = ZonedDateTime.now(ZoneIds.HELSINKI).minusDays(1).toEpochSecond(); - var endDate = ZonedDateTime.now(ZoneIds.HELSINKI).plusDays(1).toEpochSecond(); - var alert = TransitAlert - .of(stop.getId()) - .addEntity(new EntitySelector.Stop(stop.getId())) - .addTimePeriod(new TimePeriod(startDate, endDate)) - .withEffect(AlertEffect.NO_SERVICE) - .build(); - transitService.getTransitAlertService().setAlerts(List.of(alert)); - - var itineraries = List.of(itinerary); - RealtimeResolver.populateLegsWithRealtime(itineraries, transitService); - - DigitransitRealtimeStopPropertyMapper mapper = new DigitransitRealtimeStopPropertyMapper( - transitService, - new Locale("en-US") - ); - - Map map = new HashMap<>(); - mapper.map(stop).forEach(o -> map.put(o.key(), o.value())); - - assertEquals("F:name", map.get("gtfsId")); - assertEquals("name", map.get("name")); - assertEquals("desc", map.get("desc")); - assertEquals(true, map.get("closedByServiceAlert")); - } } diff --git a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingGroupsLayerTest.java b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingGroupsLayerTest.java index 1ec7d042894..60ba9100f43 100644 --- a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingGroupsLayerTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/VehicleParkingGroupsLayerTest.java @@ -144,7 +144,7 @@ public void digitransitVehicleParkingGroupPropertyMapperTest() { assertEquals("groupName", map.get("name").toString()); assertEquals( - "[{\"bicyclePlaces\":false,\"carPlaces\":true,\"name\":\"name\",\"id\":\"F:id\"}]", + "[{\"carPlaces\":true,\"bicyclePlaces\":false,\"id\":\"F:id\",\"name\":\"name\"}]", map.get("vehicleParking") ); } @@ -162,7 +162,7 @@ public void digitransitVehicleParkingGroupPropertyMapperTranslationTest() { assertEquals("groupDE", map.get("name").toString()); assertEquals( - "[{\"bicyclePlaces\":false,\"carPlaces\":true,\"name\":\"DE\",\"id\":\"F:id\"}]", + "[{\"carPlaces\":true,\"bicyclePlaces\":false,\"id\":\"F:id\",\"name\":\"DE\"}]", map.get("vehicleParking") ); } diff --git a/src/ext-test/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdaterTest.java b/src/ext-test/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdaterTest.java new file mode 100644 index 00000000000..f931e3d964e --- /dev/null +++ b/src/ext-test/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdaterTest.java @@ -0,0 +1,51 @@ +package org.opentripplanner.ext.vehicleparking.noi; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.time.Duration; +import org.junit.jupiter.api.Test; +import org.opentripplanner.test.support.ResourceLoader; +import org.opentripplanner.updater.spi.HttpHeaders; + +class NoiUpdaterTest { + + @Test + void parse() { + var uri = ResourceLoader.of(this).uri("stations.json"); + var parameters = new NoiUpdaterParameters( + "noi", + uri, + "noi", + Duration.ofSeconds(30), + HttpHeaders.empty() + ); + var updater = new NoiUpdater(parameters); + updater.update(); + var lots = updater.getUpdates(); + + assertEquals(14, lots.size()); + + lots.forEach(l -> assertNotNull(l.getName())); + + var first = lots.getFirst(); + assertEquals("noi:105", first.getId().toString()); + assertEquals("(46.49817, 11.35726)", first.getCoordinate().toString()); + assertEquals("P05 - Laurin", first.getName().toString()); + assertEquals(57, first.getAvailability().getCarSpaces()); + assertEquals(90, first.getCapacity().getCarSpaces()); + + var last = lots.getLast(); + assertEquals( + "noi:935af00d-aa5f-eb11-9889-501ac5928d31-0.8458736393052522", + last.getId().toString() + ); + assertEquals("(46.5057, 11.3395)", last.getCoordinate().toString()); + assertEquals( + "Parksensoren Bozen - PNI Parksensor Nr.10 Commissariato - Viale Eugenio di savoia", + last.getName().toString() + ); + assertEquals(0, last.getAvailability().getCarSpaces()); + assertEquals(1, last.getCapacity().getCarSpaces()); + } +} diff --git a/src/ext-test/resources/org/opentripplanner/ext/vehicleparking/noi/stations.json b/src/ext-test/resources/org/opentripplanner/ext/vehicleparking/noi/stations.json new file mode 100644 index 00000000000..2bbb07ac98b --- /dev/null +++ b/src/ext-test/resources/org/opentripplanner/ext/vehicleparking/noi/stations.json @@ -0,0 +1,159 @@ +{ + "last_updated": 1711368767, + "ttl": 0, + "version": "3.0.0", + "data": { + "stations": [ + { + "type": "station", + "station_id": "105", + "name": "P05 - Laurin", + "lat": 46.498174, + "lon": 11.357255, + "city": "Bolzano - Bozen", + "capacity": 90, + "free": 57 + }, + { + "type": "station", + "station_id": "TRENTO:areaexsitviacanestrinip1", + "name": "Area ex SIT via Canestrini - P1", + "lat": 46.0691, + "lon": 11.1162, + "address": "Lung'Adige Monte Grappa", + "city": "Trento", + "capacity": 300, + "free": 0 + }, + { + "type": "station", + "station_id": "ROVERETO:asm", + "name": "A.S.M.", + "lat": 45.893593, + "lon": 11.036507, + "address": "Piazzale ex-A.S.M - Via Manzoni - Rovereto", + "city": "Rovereto", + "capacity": 145, + "free": 42 + }, + { + "type": "station", + "station_id": "ROVERETO:centrostorico", + "name": "Centro Storico", + "lat": 45.890306, + "lon": 11.045004, + "address": "Viale dei Colli - Rovereto", + "city": "Rovereto", + "capacity": 143, + "free": 20 + }, + { + "type": "station", + "station_id": "ROVERETO:mart", + "name": "Mart", + "lat": 45.894705, + "lon": 11.044661, + "address": "Mart - Via Sticcotta - Rovereto", + "city": "Rovereto", + "capacity": 224, + "free": 224 + }, + { + "type": "sensor", + "station_id": "001bc50670100557-0.30188412882192206", + "group_name": "area viale Druso", + "group_id": "area_viale_druso", + "name": "piazzetta Mazzoni 3", + "lat": 46.495025, + "lon": 11.347069, + "address": "area viale Druso", + "city": "Bolzano - Bozen", + "free": false + }, + { + "type": "sensor", + "station_id": "001bc50670100541-0.9632040952321754", + "group_name": "Via A. Rosmini 22-26", + "group_id": "via_a_rosmini_22_26", + "name": "Via A. Rosmini 22-26", + "lat": 46.498292, + "lon": 11.348031, + "address": "Via A. Rosmini 22-26", + "city": "Bolzano - Bozen", + "free": false + }, + { + "type": "sensor", + "station_id": "001bc50670112a6b-0.6239539554369709", + "group_name": "Via Amalfi", + "group_id": "via_amalfi", + "name": "Via Amalfi angolo Via Druso", + "lat": 46.495283, + "lon": 11.332472, + "address": "Via Amalfi", + "city": "Bolzano - Bozen", + "free": false + }, + { + "type": "sensor", + "station_id": "001bc5067010064d-0.18879954213280836", + "group_name": "area viale Druso", + "group_id": "area_viale_druso", + "name": "piazzetta Mazzoni 4", + "lat": 46.495056, + "lon": 11.347056, + "address": "area viale Druso", + "city": "Bolzano - Bozen", + "free": false + }, + { + "type": "sensor", + "station_id": "001bc50670112976-0.4989211141789258", + "group_name": "Viale Druso 237", + "group_id": "viale_druso_237", + "name": "Viale Druso 237", + "lat": 46.495, + "lon": 11.328703, + "address": "Viale Druso 237", + "city": "Bolzano - Bozen", + "free": true + }, + { + "type": "sensor", + "station_id": "9398a35b-ef3d-eb11-b9ed-0050f244b601-0.12775006754129703", + "group_id": "", + "name": "Parksensoren Bozen - PNI Parksensor Nr.3 Siegesplatz Parkplatz", + "lat": 46.501, + "lon": 11.3431, + "free": true + }, + { + "type": "sensor", + "station_id": "7776d25f-f03d-eb11-b9ed-0050f244b601-0.4355636862513992", + "group_id": "", + "name": "Parksensoren Bozen - PNI Parksensor Nr.5 DucaDaostastrasse", + "lat": 46.4953, + "lon": 11.3396, + "free": false + }, + { + "type": "sensor", + "station_id": "e3e26add-ee3d-eb11-b9ed-0050f244b601-0.8423578257530036", + "group_id": "", + "name": "Parksensoren Bozen - PNI Parksensor Nr.4 Bahnhof BZ Richtung Rittnerseilbahn", + "lat": 46.497, + "lon": 11.3583, + "free": false + }, + { + "type": "sensor", + "station_id": "935af00d-aa5f-eb11-9889-501ac5928d31-0.8458736393052522", + "group_id": "", + "name": "Parksensoren Bozen - PNI Parksensor Nr.10 Commissariato - Viale Eugenio di savoia", + "lat": 46.5057, + "lon": 11.3395, + "free": false + } + ] + } +} \ No newline at end of file diff --git a/src/ext/java/org/opentripplanner/ext/vectortiles/layers/stations/DigitransitStationPropertyMapper.java b/src/ext/java/org/opentripplanner/ext/vectortiles/layers/stations/DigitransitStationPropertyMapper.java index a828cd37a7c..18b4a5f388d 100644 --- a/src/ext/java/org/opentripplanner/ext/vectortiles/layers/stations/DigitransitStationPropertyMapper.java +++ b/src/ext/java/org/opentripplanner/ext/vectortiles/layers/stations/DigitransitStationPropertyMapper.java @@ -1,13 +1,14 @@ package org.opentripplanner.ext.vectortiles.layers.stations; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Collection; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.stream.Collectors; -import org.json.simple.JSONArray; import org.opentripplanner.apis.support.mapping.PropertyMapper; import org.opentripplanner.framework.i18n.I18NStringMapper; +import org.opentripplanner.framework.json.ObjectMappers; import org.opentripplanner.inspector.vector.KeyValue; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.site.Station; @@ -16,6 +17,7 @@ public class DigitransitStationPropertyMapper extends PropertyMapper { + private static final ObjectMapper OBJECT_MAPPER = ObjectMappers.ignoringExtraFields(); private final TransitService transitService; private final I18NStringMapper i18NStringMapper; @@ -33,41 +35,47 @@ public static DigitransitStationPropertyMapper create( @Override public Collection map(Station station) { - var childStops = station.getChildStops(); - - return List.of( - new KeyValue("gtfsId", station.getId().toString()), - new KeyValue("name", i18NStringMapper.mapNonnullToApi(station.getName())), - new KeyValue( - "type", - childStops - .stream() - .flatMap(stop -> transitService.getPatternsForStop(stop).stream()) - .map(tripPattern -> tripPattern.getMode().name()) - .distinct() - .collect(Collectors.joining(",")) - ), - new KeyValue( - "stops", - JSONArray.toJSONString( - childStops.stream().map(StopLocation::getId).map(FeedScopedId::toString).toList() - ) - ), - new KeyValue( - "routes", - JSONArray.toJSONString( + try { + var childStops = station.getChildStops(); + return List.of( + new KeyValue("gtfsId", station.getId().toString()), + new KeyValue("name", i18NStringMapper.mapNonnullToApi(station.getName())), + new KeyValue( + "type", childStops .stream() - .flatMap(stop -> transitService.getRoutesForStop(stop).stream()) + .flatMap(stop -> transitService.getPatternsForStop(stop).stream()) + .map(tripPattern -> tripPattern.getMode().name()) .distinct() - .map(route -> - route.getShortName() == null - ? Map.of("mode", route.getMode().name()) - : Map.of("mode", route.getMode().name(), "shortName", route.getShortName()) - ) - .collect(Collectors.toList()) + .collect(Collectors.joining(",")) + ), + new KeyValue( + "stops", + OBJECT_MAPPER.writeValueAsString( + childStops.stream().map(StopLocation::getId).map(FeedScopedId::toString).toList() + ) + ), + new KeyValue( + "routes", + OBJECT_MAPPER.writeValueAsString( + childStops + .stream() + .flatMap(stop -> transitService.getRoutesForStop(stop).stream()) + .distinct() + .map(route -> { + var obj = OBJECT_MAPPER.createObjectNode(); + obj.put("mode", route.getMode().name()); + if (route.getShortName() != null) { + obj.put("shortName", route.getShortName()); + } + return obj; + }) + .toList() + ) ) - ) - ); + ); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } } } diff --git a/src/ext/java/org/opentripplanner/ext/vectortiles/layers/stops/DigitransitStopPropertyMapper.java b/src/ext/java/org/opentripplanner/ext/vectortiles/layers/stops/DigitransitStopPropertyMapper.java index 643ed63b7f8..d10e221b1d5 100644 --- a/src/ext/java/org/opentripplanner/ext/vectortiles/layers/stops/DigitransitStopPropertyMapper.java +++ b/src/ext/java/org/opentripplanner/ext/vectortiles/layers/stops/DigitransitStopPropertyMapper.java @@ -1,16 +1,16 @@ package org.opentripplanner.ext.vectortiles.layers.stops; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; import org.opentripplanner.apis.support.mapping.PropertyMapper; -import org.opentripplanner.framework.collection.ListUtils; import org.opentripplanner.framework.i18n.I18NStringMapper; +import org.opentripplanner.framework.json.ObjectMappers; import org.opentripplanner.inspector.vector.KeyValue; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.RegularStop; @@ -18,6 +18,7 @@ public class DigitransitStopPropertyMapper extends PropertyMapper { + private static final ObjectMapper OBJECT_MAPPER = ObjectMappers.ignoringExtraFields(); private final TransitService transitService; private final I18NStringMapper i18NStringMapper; @@ -59,17 +60,20 @@ protected static Collection getBaseKeyValues( } protected static String getRoutes(TransitService transitService, RegularStop stop) { - return JSONArray.toJSONString( - transitService + try { + var objects = transitService .getRoutesForStop(stop) .stream() .map(route -> { - JSONObject routeObject = new JSONObject(); + var routeObject = OBJECT_MAPPER.createObjectNode(); routeObject.put("gtfsType", route.getGtfsType()); return routeObject; }) - .toList() - ); + .toList(); + return OBJECT_MAPPER.writeValueAsString(objects); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } } protected static String getType(TransitService transitService, RegularStop stop) { diff --git a/src/ext/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/DigitransitVehicleParkingGroupPropertyMapper.java b/src/ext/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/DigitransitVehicleParkingGroupPropertyMapper.java index 33f415c157a..efafc969619 100644 --- a/src/ext/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/DigitransitVehicleParkingGroupPropertyMapper.java +++ b/src/ext/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/DigitransitVehicleParkingGroupPropertyMapper.java @@ -1,17 +1,19 @@ package org.opentripplanner.ext.vectortiles.layers.vehicleparkings; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Collection; import java.util.List; import java.util.Locale; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; import org.opentripplanner.apis.support.mapping.PropertyMapper; import org.opentripplanner.framework.i18n.I18NStringMapper; +import org.opentripplanner.framework.json.ObjectMappers; import org.opentripplanner.inspector.vector.KeyValue; public class DigitransitVehicleParkingGroupPropertyMapper extends PropertyMapper { + private static final ObjectMapper OBJECT_MAPPER = ObjectMappers.ignoringExtraFields(); private final I18NStringMapper i18NStringMapper; public DigitransitVehicleParkingGroupPropertyMapper(Locale locale) { @@ -24,25 +26,28 @@ public static DigitransitVehicleParkingGroupPropertyMapper create(Locale locale) @Override protected Collection map(VehicleParkingAndGroup parkingAndGroup) { - var group = parkingAndGroup.vehicleParkingGroup(); - String parking = JSONArray.toJSONString( - parkingAndGroup + try { + var group = parkingAndGroup.vehicleParkingGroup(); + var lots = parkingAndGroup .vehicleParking() .stream() .map(vehicleParkingPlace -> { - JSONObject parkingObject = new JSONObject(); + var parkingObject = OBJECT_MAPPER.createObjectNode(); parkingObject.put("carPlaces", vehicleParkingPlace.hasCarPlaces()); parkingObject.put("bicyclePlaces", vehicleParkingPlace.hasBicyclePlaces()); parkingObject.put("id", vehicleParkingPlace.getId().toString()); parkingObject.put("name", i18NStringMapper.mapToApi(vehicleParkingPlace.getName())); return parkingObject; }) - .toList() - ); - return List.of( - new KeyValue("id", group.id().toString()), - new KeyValue("name", i18NStringMapper.mapToApi(group.name())), - new KeyValue("vehicleParking", parking) - ); + .toList(); + var string = OBJECT_MAPPER.writeValueAsString(lots); + return List.of( + new KeyValue("id", group.id().toString()), + new KeyValue("name", i18NStringMapper.mapToApi(group.name())), + new KeyValue("vehicleParking", string) + ); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } } } diff --git a/src/ext/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/StadtnaviVehicleParkingPropertyMapper.java b/src/ext/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/StadtnaviVehicleParkingPropertyMapper.java index c938f9736fd..bccc2b4de4d 100644 --- a/src/ext/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/StadtnaviVehicleParkingPropertyMapper.java +++ b/src/ext/java/org/opentripplanner/ext/vectortiles/layers/vehicleparkings/StadtnaviVehicleParkingPropertyMapper.java @@ -1,11 +1,12 @@ package org.opentripplanner.ext.vectortiles.layers.vehicleparkings; +import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Collection; import java.util.List; import java.util.Locale; -import org.json.simple.JSONObject; import org.opentripplanner.apis.support.mapping.PropertyMapper; import org.opentripplanner.framework.i18n.I18NStringMapper; +import org.opentripplanner.framework.json.ObjectMappers; import org.opentripplanner.inspector.vector.KeyValue; import org.opentripplanner.model.calendar.openinghours.OsmOpeningHoursSupport; import org.opentripplanner.routing.vehicle_parking.VehicleParking; @@ -13,6 +14,7 @@ public class StadtnaviVehicleParkingPropertyMapper extends PropertyMapper { + private static final ObjectMapper OBJECT_MAPPER = ObjectMappers.ignoringExtraFields(); private final DigitransitVehicleParkingPropertyMapper digitransitMapper; private final I18NStringMapper i18NStringMapper; @@ -57,13 +59,13 @@ private static List mapPlaces(String key, VehicleParkingSpaces places) return List.of(); } - var json = new JSONObject(); + var json = OBJECT_MAPPER.createObjectNode(); json.put("bicyclePlaces", places.getBicycleSpaces()); json.put("carPlaces", places.getCarSpaces()); json.put("wheelchairAccessibleCarPlaces", places.getWheelchairAccessibleCarSpaces()); return List.of( - new KeyValue(key, JSONObject.toJSONString(json)), + new KeyValue(key, json.toString()), new KeyValue(subKey(key, "bicyclePlaces"), places.getBicycleSpaces()), new KeyValue(subKey(key, "carPlaces"), places.getCarSpaces()), new KeyValue( diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdater.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdater.java new file mode 100644 index 00000000000..d5ef7e63d13 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdater.java @@ -0,0 +1,77 @@ +package org.opentripplanner.ext.vehicleparking.noi; + +import com.fasterxml.jackson.databind.JsonNode; +import org.opentripplanner.framework.geometry.WgsCoordinate; +import org.opentripplanner.framework.i18n.NonLocalizedString; +import org.opentripplanner.framework.tostring.ToStringBuilder; +import org.opentripplanner.routing.vehicle_parking.VehicleParking; +import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; +import org.opentripplanner.routing.vehicle_parking.VehicleParkingState; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.updater.spi.GenericJsonDataSource; + +/** + * Vehicle parking updater class for NOI's open data hub format APIs. + */ +public class NoiUpdater extends GenericJsonDataSource { + + private static final String JSON_PARSE_PATH = "data/stations"; + private final NoiUpdaterParameters params; + + public NoiUpdater(NoiUpdaterParameters parameters) { + super(parameters.url().toString(), JSON_PARSE_PATH, parameters.httpHeaders()); + this.params = parameters; + } + + @Override + protected VehicleParking parseElement(JsonNode jsonNode) { + var type = jsonNode.path("type").textValue(); + VehicleParkingSpaces capacity, availability; + if (type.equals("station")) { + capacity = extractSpaces(jsonNode, "capacity"); + availability = extractSpaces(jsonNode, "free"); + } else if (type.equals("sensor")) { + capacity = VehicleParkingSpaces.builder().carSpaces(1).build(); + var isFree = jsonNode.path("free").asBoolean(); + availability = VehicleParkingSpaces.builder().carSpaces(isFree ? 1 : 0).build(); + } else { + throw new IllegalArgumentException("Unknown type '%s'".formatted(type)); + } + + var vehicleParkId = new FeedScopedId(params.feedId(), jsonNode.path("station_id").asText()); + var name = new NonLocalizedString(jsonNode.path("name").asText()); + double lat = jsonNode.path("lat").asDouble(); + double lon = jsonNode.path("lon").asDouble(); + var coordinate = new WgsCoordinate(lat, lon); + VehicleParking.VehicleParkingEntranceCreator entrance = builder -> + builder + .entranceId(new FeedScopedId(params.feedId(), vehicleParkId.getId() + "/entrance")) + .coordinate(coordinate) + .walkAccessible(true) + .carAccessible(true); + + return VehicleParking + .builder() + .id(vehicleParkId) + .name(name) + .state(VehicleParkingState.OPERATIONAL) + .coordinate(coordinate) + .capacity(capacity) + .availability(availability) + .carPlaces(true) + .entrance(entrance) + .build(); + } + + private static VehicleParkingSpaces extractSpaces(JsonNode jsonNode, String free) { + return VehicleParkingSpaces.builder().carSpaces(jsonNode.get(free).asInt()).build(); + } + + @Override + public String toString() { + return ToStringBuilder + .of(this.getClass()) + .addStr("url", this.params.url().toString()) + .toString(); + } +} diff --git a/src/ext/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdaterParameters.java b/src/ext/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdaterParameters.java new file mode 100644 index 00000000000..371ffdc9f33 --- /dev/null +++ b/src/ext/java/org/opentripplanner/ext/vehicleparking/noi/NoiUpdaterParameters.java @@ -0,0 +1,25 @@ +package org.opentripplanner.ext.vehicleparking.noi; + +import java.net.URI; +import java.time.Duration; +import org.opentripplanner.updater.spi.HttpHeaders; +import org.opentripplanner.updater.vehicle_parking.VehicleParkingSourceType; +import org.opentripplanner.updater.vehicle_parking.VehicleParkingUpdaterParameters; + +/** + * Class that extends {@link VehicleParkingUpdaterParameters} with parameters required by {@link + * NoiUpdater}. + */ +public record NoiUpdaterParameters( + String configRef, + URI url, + String feedId, + Duration frequency, + HttpHeaders httpHeaders +) + implements VehicleParkingUpdaterParameters { + @Override + public VehicleParkingSourceType sourceType() { + return VehicleParkingSourceType.NOI_OPEN_DATA_HUB; + } +} diff --git a/src/main/java/org/opentripplanner/apis/transmodel/mapping/FilterMapper.java b/src/main/java/org/opentripplanner/apis/transmodel/mapping/FilterMapper.java index 9141ce4c104..71d2691e47a 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/mapping/FilterMapper.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/mapping/FilterMapper.java @@ -83,7 +83,7 @@ static void mapFilterOldWay( List tModes = new ArrayList<>(); if (GqlUtil.hasArgument(environment, "modes")) { Map modesInput = environment.getArgument("modes"); - if (modesInput.containsKey("transportModes")) { + if (modesInput.get("transportModes") != null) { List> transportModes = (List>) modesInput.get( "transportModes" ); diff --git a/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java b/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java index 59259905944..e7a13dcacc9 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/mapping/RequestModesMapper.java @@ -15,7 +15,7 @@ class RequestModesMapper { * Maps GraphQL Modes input type to RequestModes. *

* 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 87a225fc0b3..85915cfeb48 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 c0cd049bd83..b1e90044bf2 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWay.java @@ -109,20 +109,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. */ @@ -188,18 +174,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 51bc9da1dd8..636584eb770 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMWithTags.java @@ -403,14 +403,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/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/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/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/routerconfig/updaters/VehicleParkingUpdaterConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerconfig/updaters/VehicleParkingUpdaterConfig.java index e808ad6905c..f0306941547 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,6 +2,7 @@ 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; @@ -9,6 +10,7 @@ import java.util.Set; 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; @@ -50,7 +52,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 +68,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 +77,17 @@ 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) + ); }; } 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..3fe465a8cd5 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingDataSourceFactory.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingDataSourceFactory.java @@ -4,6 +4,8 @@ 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 +38,7 @@ public static DataSource create( openingHoursCalendarService ); case BIKELY -> new BikelyUpdater((BikelyUpdaterParameters) parameters); + case NOI_OPEN_DATA_HUB -> new NoiUpdater((NoiUpdaterParameters) 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..3a0cb7d31b3 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,5 @@ public enum VehicleParkingSourceType { BICYCLE_PARK_API, HSL_PARK, BIKELY, + NOI_OPEN_DATA_HUB, } 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/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/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 18d92a5eec8..3c060150714 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/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/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/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/street/model/_data/StreetModelForTest.java b/src/test/java/org/opentripplanner/street/model/_data/StreetModelForTest.java index 94ebaec73c4..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; @@ -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/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/resources/standalone/config/router-config.json b/src/test/resources/standalone/config/router-config.json index 3a07cddfeb8..f5eaa1f942b 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",