diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml index 8c2caf88f5c..5ae8006e0f5 100644 --- a/.github/workflows/cibuild.yml +++ b/.github/workflows/cibuild.yml @@ -132,7 +132,7 @@ jobs: LOCAL_BRANCH: local-pages REMOTE_BRANCH: main TOKEN: ${{ secrets.CHANGELOG_TOKEN }} - MASTER_BRANCH_VERSION: 2.5.0 + MASTER_BRANCH_VERSION: 2.6.0 steps: diff --git a/client/.env b/client/.env index a2d09f3b312..2f5d271b1e4 100644 --- a/client/.env +++ b/client/.env @@ -1,3 +1,3 @@ VITE_API_URL=/otp/transmodel/v3 VITE_DEBUG_STYLE_URL=/otp/routers/default/inspector/vectortile/style.json - +VITE_GRAPHIQL_URL=/graphiql?flavor=transmodel diff --git a/client/.env.development b/client/.env.development index 35840c239bd..1cb7d9235e3 100644 --- a/client/.env.development +++ b/client/.env.development @@ -1,2 +1,3 @@ VITE_API_URL=http://localhost:8080/otp/transmodel/v3 -VITE_DEBUG_STYLE_URL=http://localhost:8080/otp/routers/default/inspector/vectortile/style.json \ No newline at end of file +VITE_DEBUG_STYLE_URL=http://localhost:8080/otp/routers/default/inspector/vectortile/style.json +VITE_GRAPHIQL_URL=http://localhost:8080/graphiql?flavor=transmodel \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json index 4632899b38f..5619317e0bd 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -12,7 +12,7 @@ "bootstrap": "5.3.3", "graphql": "16.9.0", "graphql-request": "7.1.0", - "maplibre-gl": "4.6.0", + "maplibre-gl": "4.7.0", "react": "18.3.1", "react-bootstrap": "2.10.4", "react-dom": "18.3.1", @@ -24,24 +24,24 @@ "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.4.1", "@testing-library/react": "16.0.1", - "@types/react": "18.3.5", + "@types/react": "18.3.7", "@types/react-dom": "18.3.0", "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", "@vitejs/plugin-react": "4.3.1", - "@vitest/coverage-v8": "2.0.5", - "eslint": "8.57.0", + "@vitest/coverage-v8": "2.1.1", + "eslint": "8.57.1", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.30.0", - "eslint-plugin-jsx-a11y": "6.9.0", - "eslint-plugin-react": "7.35.1", + "eslint-plugin-jsx-a11y": "6.10.0", + "eslint-plugin-react": "7.36.1", "eslint-plugin-react-hooks": "4.6.2", - "eslint-plugin-react-refresh": "0.4.11", + "eslint-plugin-react-refresh": "0.4.12", "jsdom": "25.0.0", "prettier": "3.3.3", - "typescript": "5.5.4", - "vite": "5.4.2", - "vitest": "2.0.5" + "typescript": "5.6.2", + "vite": "5.4.6", + "vitest": "2.1.1" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1775,10 +1775,11 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, + "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -2745,12 +2746,14 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -2794,10 +2797,12 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", - "dev": true + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -2937,10 +2942,11 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", @@ -3951,9 +3957,9 @@ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/react": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", - "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==", + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.7.tgz", + "integrity": "sha512-KUnDCJF5+AiZd8owLIeVHqmW9yM4sqmDVf2JRJiBMFkGvkoZ4/WyV2lL4zVsoinmRS/W3FeEdZLEWFRofnT2FQ==", "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -4232,20 +4238,20 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.0.5.tgz", - "integrity": "sha512-qeFcySCg5FLO2bHHSa0tAZAOnAUbp4L6/A5JDuj9+bt53JREl8hpLjLHEWF0e/gWc8INVpJaqA7+Ene2rclpZg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.1.tgz", + "integrity": "sha512-md/A7A3c42oTT8JUHSqjP5uKTWJejzUW4jalpvs+rZ27gsURsMU8DEb+8Jf8C6Kj2gwfSHJqobDNBuoqlm0cFw==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^0.2.3", - "debug": "^4.3.5", + "debug": "^4.3.6", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-lib-source-maps": "^5.0.6", "istanbul-reports": "^3.1.7", - "magic-string": "^0.30.10", + "magic-string": "^0.30.11", "magicast": "^0.3.4", "std-env": "^3.7.0", "test-exclude": "^7.0.1", @@ -4255,18 +4261,24 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "2.0.5" + "@vitest/browser": "2.1.1", + "vitest": "2.1.1" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } } }, "node_modules/@vitest/expect": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.5.tgz", - "integrity": "sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.1.tgz", + "integrity": "sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.0.5", - "@vitest/utils": "2.0.5", + "@vitest/spy": "2.1.1", + "@vitest/utils": "2.1.1", "chai": "^5.1.1", "tinyrainbow": "^1.2.0" }, @@ -4274,10 +4286,38 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@vitest/mocker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.1.tgz", + "integrity": "sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "^2.1.0-beta.1", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.11" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/spy": "2.1.1", + "msw": "^2.3.5", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, "node_modules/@vitest/pretty-format": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.5.tgz", - "integrity": "sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.1.tgz", + "integrity": "sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4288,13 +4328,13 @@ } }, "node_modules/@vitest/runner": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.0.5.tgz", - "integrity": "sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.1.tgz", + "integrity": "sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.0.5", + "@vitest/utils": "2.1.1", "pathe": "^1.1.2" }, "funding": { @@ -4302,14 +4342,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.0.5.tgz", - "integrity": "sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.1.tgz", + "integrity": "sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.0.5", - "magic-string": "^0.30.10", + "@vitest/pretty-format": "2.1.1", + "magic-string": "^0.30.11", "pathe": "^1.1.2" }, "funding": { @@ -4317,9 +4357,9 @@ } }, "node_modules/@vitest/spy": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.5.tgz", - "integrity": "sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.1.tgz", + "integrity": "sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==", "dev": true, "license": "MIT", "dependencies": { @@ -4330,14 +4370,13 @@ } }, "node_modules/@vitest/utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.0.5.tgz", - "integrity": "sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.1.tgz", + "integrity": "sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.0.5", - "estree-walker": "^3.0.3", + "@vitest/pretty-format": "2.1.1", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" }, @@ -4761,15 +4800,25 @@ } }, "node_modules/axe-core": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.9.1.tgz", - "integrity": "sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz", + "integrity": "sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==", "dev": true, "license": "MPL-2.0", "engines": { "node": ">=4" } }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/babel-plugin-syntax-trailing-function-commas": { "version": "7.0.0-beta.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz", @@ -5491,13 +5540,13 @@ "dev": true }, "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -6045,16 +6094,17 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -6239,9 +6289,9 @@ } }, "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.9.0.tgz", - "integrity": "sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.0.tgz", + "integrity": "sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg==", "dev": true, "license": "MIT", "dependencies": { @@ -6249,8 +6299,8 @@ "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", - "axe-core": "^4.9.1", - "axobject-query": "~3.1.1", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", "es-iterator-helpers": "^1.0.19", @@ -6266,7 +6316,7 @@ "node": ">=4.0" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { @@ -6279,16 +6329,6 @@ "deep-equal": "^2.0.5" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/axobject-query": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", - "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "deep-equal": "^2.0.5" - } - }, "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", @@ -6314,9 +6354,9 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.35.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.1.tgz", - "integrity": "sha512-B5ok2JgbaaWn/zXbKCGgKDNL2tsID3Pd/c/yvjcpsd9HQDwyYc/TQv3AZMmOvrJgCs3AnYNUHRCQEMMQAYJ7Yg==", + "version": "7.36.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.36.1.tgz", + "integrity": "sha512-/qwbqNXZoq+VP30s1d4Nc1C5GTxjJQjk4Jzs4Wq2qzxFM7dSmuG2UkIjg2USMLh3A/aVcUNrK7v0J5U1XEGGwA==", "dev": true, "license": "MIT", "dependencies": { @@ -6359,9 +6399,9 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.11.tgz", - "integrity": "sha512-wrAKxMbVr8qhXTtIKfXqAn5SAtRZt0aXxe5P23Fh4pUAdC6XEsybGLB8P0PI4j1yYqOgUEUlzKAGDfo7rJOjcw==", + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.12.tgz", + "integrity": "sha512-9neVjoGv20FwYtCP6CB1dzR1vr57ZDNOXst21wd2xJ/cTlM2xLq0GWVlSNTdMn/4BtP6cHYBMCSp1wFBJ9jBsg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -6456,6 +6496,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6466,6 +6507,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -6481,6 +6523,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -6493,6 +6536,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -6569,80 +6613,6 @@ "node": ">=0.10.0" } }, - "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execa/node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/execa/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/execa/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/execa/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", @@ -7486,15 +7456,6 @@ "node": ">= 14" } }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "engines": { - "node": ">=16.17.0" - } - }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -8037,18 +7998,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -8740,13 +8689,13 @@ } }, "node_modules/magic-string": { - "version": "0.30.10", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", - "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/magicast": { @@ -8819,9 +8768,9 @@ } }, "node_modules/maplibre-gl": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.6.0.tgz", - "integrity": "sha512-zobZK+fE+XM+7K81fk5pSBYWZlTGjGT0P96y2fR4DV2ry35ZBfAd0uWNatll69EgYeE+uOhN1MvEk+z1PCuyOQ==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.7.0.tgz", + "integrity": "sha512-hkt7je7NxiMQE8EpCxLWP8t6tkK6SkrMe0hIBjYd4Ar/Q7BOCILxthGmGnU993Mwmkvs2mGiXnVUSOK12DeCzg==", "license": "BSD-3-Clause", "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", @@ -8865,12 +8814,6 @@ "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", "license": "ISC" }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -8975,10 +8918,11 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, "node_modules/murmurhash-js": { "version": "1.0.0", @@ -9100,33 +9044,6 @@ "node": ">=0.10.0" } }, - "node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/nullthrows": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", @@ -9601,9 +9518,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "dev": true, "license": "ISC" }, @@ -9629,9 +9546,9 @@ } }, "node_modules/postcss": { - "version": "8.4.41", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", - "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "funding": [ { @@ -9650,8 +9567,8 @@ "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -10547,10 +10464,11 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -10864,18 +10782,6 @@ "node": ">=4" } }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -10984,9 +10890,16 @@ "dev": true }, "node_modules/tinybench": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz", - "integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz", + "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==", "dev": true, "license": "MIT" }, @@ -11017,9 +10930,9 @@ } }, "node_modules/tinyspy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.0.tgz", - "integrity": "sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", "dev": true, "license": "MIT", "engines": { @@ -11265,9 +11178,9 @@ } }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -11491,14 +11404,14 @@ } }, "node_modules/vite": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz", - "integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==", + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz", + "integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.41", + "postcss": "^8.4.43", "rollup": "^4.20.0" }, "bin": { @@ -11551,16 +11464,15 @@ } }, "node_modules/vite-node": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.0.5.tgz", - "integrity": "sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.1.tgz", + "integrity": "sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", - "debug": "^4.3.5", + "debug": "^4.3.6", "pathe": "^1.1.2", - "tinyrainbow": "^1.2.0", "vite": "^5.0.0" }, "bin": { @@ -11574,30 +11486,30 @@ } }, "node_modules/vitest": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.0.5.tgz", - "integrity": "sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.1.tgz", + "integrity": "sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==", "dev": true, "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.3.0", - "@vitest/expect": "2.0.5", - "@vitest/pretty-format": "^2.0.5", - "@vitest/runner": "2.0.5", - "@vitest/snapshot": "2.0.5", - "@vitest/spy": "2.0.5", - "@vitest/utils": "2.0.5", + "@vitest/expect": "2.1.1", + "@vitest/mocker": "2.1.1", + "@vitest/pretty-format": "^2.1.1", + "@vitest/runner": "2.1.1", + "@vitest/snapshot": "2.1.1", + "@vitest/spy": "2.1.1", + "@vitest/utils": "2.1.1", "chai": "^5.1.1", - "debug": "^4.3.5", - "execa": "^8.0.1", - "magic-string": "^0.30.10", + "debug": "^4.3.6", + "magic-string": "^0.30.11", "pathe": "^1.1.2", "std-env": "^3.7.0", - "tinybench": "^2.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.0", "tinypool": "^1.0.0", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.0.5", + "vite-node": "2.1.1", "why-is-node-running": "^2.3.0" }, "bin": { @@ -11612,8 +11524,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.0.5", - "@vitest/ui": "2.0.5", + "@vitest/browser": "2.1.1", + "@vitest/ui": "2.1.1", "happy-dom": "*", "jsdom": "*" }, diff --git a/client/package.json b/client/package.json index 5b26215fe5f..2cd0d1f8937 100644 --- a/client/package.json +++ b/client/package.json @@ -21,7 +21,7 @@ "bootstrap": "5.3.3", "graphql": "16.9.0", "graphql-request": "7.1.0", - "maplibre-gl": "4.6.0", + "maplibre-gl": "4.7.0", "react": "18.3.1", "react-bootstrap": "2.10.4", "react-dom": "18.3.1", @@ -33,23 +33,23 @@ "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.4.1", "@testing-library/react": "16.0.1", - "@types/react": "18.3.5", + "@types/react": "18.3.7", "@types/react-dom": "18.3.0", "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", "@vitejs/plugin-react": "4.3.1", - "@vitest/coverage-v8": "2.0.5", - "eslint": "8.57.0", + "@vitest/coverage-v8": "2.1.1", + "eslint": "8.57.1", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.30.0", - "eslint-plugin-jsx-a11y": "6.9.0", - "eslint-plugin-react": "7.35.1", + "eslint-plugin-jsx-a11y": "6.10.0", + "eslint-plugin-react": "7.36.1", "eslint-plugin-react-hooks": "4.6.2", - "eslint-plugin-react-refresh": "0.4.11", + "eslint-plugin-react-refresh": "0.4.12", "jsdom": "25.0.0", "prettier": "3.3.3", - "typescript": "5.5.4", - "vite": "5.4.2", - "vitest": "2.0.5" + "typescript": "5.6.2", + "vite": "5.4.6", + "vitest": "2.1.1" } } diff --git a/client/src/components/ItineraryList/ItineraryGraphiQLAuthorityLink.tsx b/client/src/components/ItineraryList/ItineraryGraphiQLAuthorityLink.tsx new file mode 100644 index 00000000000..8ec3e1353ec --- /dev/null +++ b/client/src/components/ItineraryList/ItineraryGraphiQLAuthorityLink.tsx @@ -0,0 +1,25 @@ +import { authorityQueryAsString } from '../../static/query/authorityQuery.tsx'; +import { Maybe } from '../../gql/graphql.ts'; +const graphiQLUrl = import.meta.env.VITE_GRAPHIQL_URL; + +export function ItineraryGraphiQLAuthorityLink({ + legId, + legName, +}: { + legId: string | undefined; + legName: Maybe | undefined; +}) { + const queryID = { id: legId }; + const formattedQuery = encodeURIComponent(authorityQueryAsString); + const formattedQueryID = encodeURIComponent(JSON.stringify(queryID)); + + return ( + + {legName} + + ); +} diff --git a/client/src/components/ItineraryList/ItineraryGraphiQLLineLink.tsx b/client/src/components/ItineraryList/ItineraryGraphiQLLineLink.tsx new file mode 100644 index 00000000000..f54f61a204b --- /dev/null +++ b/client/src/components/ItineraryList/ItineraryGraphiQLLineLink.tsx @@ -0,0 +1,18 @@ +import { lineQueryAsString } from '../../static/query/lineQuery.tsx'; +const graphiQLUrl = import.meta.env.VITE_GRAPHIQL_URL; + +export function ItineraryGraphiQLLineLink({ legId, legName }: { legId: string; legName: string }) { + const queryID = { id: legId }; + const formattedQuery = encodeURIComponent(lineQueryAsString); + const formattedQueryID = encodeURIComponent(JSON.stringify(queryID)); + + return ( + + {legName} + + ); +} diff --git a/client/src/components/ItineraryList/ItineraryGraphiQLQuayLink.tsx b/client/src/components/ItineraryList/ItineraryGraphiQLQuayLink.tsx new file mode 100644 index 00000000000..795cecd0703 --- /dev/null +++ b/client/src/components/ItineraryList/ItineraryGraphiQLQuayLink.tsx @@ -0,0 +1,25 @@ +import { quayQueryAsString } from '../../static/query/quayQuery.tsx'; +import { Maybe } from '../../gql/graphql.ts'; +const graphiQLUrl = import.meta.env.VITE_GRAPHIQL_URL; + +export function ItineraryGraphiQLQuayLink({ + legId, + legName, +}: { + legId: string | undefined; + legName: Maybe | undefined; +}) { + const queryID = { id: legId }; + const formattedQuery = encodeURIComponent(quayQueryAsString); + const formattedQueryID = encodeURIComponent(JSON.stringify(queryID)); + + return ( + + {legName} + + ); +} diff --git a/client/src/components/ItineraryList/ItineraryHeaderContent.tsx b/client/src/components/ItineraryList/ItineraryHeaderContent.tsx index 419b7a2ebb9..cf3ca227b63 100644 --- a/client/src/components/ItineraryList/ItineraryHeaderContent.tsx +++ b/client/src/components/ItineraryList/ItineraryHeaderContent.tsx @@ -45,6 +45,7 @@ export function ItineraryHeaderContent({ }} />
{leg.mode}{' '} {leg.line && ( <> - - {leg.line.publicCode} {leg.toEstimatedCall?.destinationDisplay?.frontText} - - , {leg.authority?.name} + + , )}{' '} {leg.mode !== Mode.Foot && ( <>
- {leg.fromPlace.name} →{' '} + →{' '} )}{' '} - {!isLast && {leg.toPlace.name}} + {!isLast && }
); diff --git a/client/src/components/ItineraryList/LegTime.tsx b/client/src/components/ItineraryList/LegTime.tsx index 1b3d01e0d05..0a574a7bccc 100644 --- a/client/src/components/ItineraryList/LegTime.tsx +++ b/client/src/components/ItineraryList/LegTime.tsx @@ -11,11 +11,15 @@ export function LegTime({ }) { return aimedTime !== expectedTime ? ( <> - {formatTime(expectedTime, 'short')} - {formatTime(aimedTime, 'short')} + + {formatTime(expectedTime, 'short')} + + + {formatTime(aimedTime, 'short')} + ) : ( - + {formatTime(expectedTime, 'short')} {hasRealtime && (on time)} diff --git a/client/src/components/ItineraryList/useHeaderLegContentStyleCalculations.ts b/client/src/components/ItineraryList/useHeaderLegContentStyleCalculations.ts index 642a71320b7..64e5f6c967b 100644 --- a/client/src/components/ItineraryList/useHeaderLegContentStyleCalculations.ts +++ b/client/src/components/ItineraryList/useHeaderLegContentStyleCalculations.ts @@ -1,6 +1,6 @@ import { useMemo } from 'react'; import { isTransitMode } from '../../util/isTransitMode.ts'; -import { getColorForMode } from '../../util/getColorForMode.ts'; +import { getColorForLeg } from '../../util/getColorForLeg.ts'; import { generateTextColor } from '../../util/generateTextColor.ts'; import { Leg } from '../../gql/graphql.ts'; @@ -27,7 +27,7 @@ export function useHeaderLegContentStyleCalculations( const showPublicCode = widthPx > 40 && isTransitMode(leg.mode) && leg.line?.publicCode && leg.line.publicCode.length <= 6; - const modeColor = getColorForMode(leg.mode); + const modeColor = getColorForLeg(leg); const legTextColor = useMemo(() => generateTextColor(modeColor), [modeColor]); return { widthPx, leftPx, legTextColor, modeColor, showPublicCode }; diff --git a/client/src/components/MapView/LegLines.tsx b/client/src/components/MapView/LegLines.tsx index 41b02ece83a..8f6e8405997 100644 --- a/client/src/components/MapView/LegLines.tsx +++ b/client/src/components/MapView/LegLines.tsx @@ -1,7 +1,7 @@ import { TripPattern } from '../../gql/graphql.ts'; import { Layer, Source } from 'react-map-gl'; import { decode } from '@googlemaps/polyline-codec'; -import { getColorForMode } from '../../util/getColorForMode.ts'; +import { getColorForLeg } from '../../util/getColorForLeg.ts'; export function LegLines({ tripPattern }: { tripPattern?: TripPattern }) { return ( @@ -28,7 +28,7 @@ export function LegLines({ tripPattern }: { tripPattern?: TripPattern }) { 'line-cap': 'round', }} paint={{ - 'line-color': getColorForMode(leg.mode), + 'line-color': getColorForLeg(leg), 'line-width': 5, }} /> diff --git a/client/src/components/SearchBar/GraphiQLRouteButton.tsx b/client/src/components/SearchBar/GraphiQLRouteButton.tsx new file mode 100644 index 00000000000..550964dd5a8 --- /dev/null +++ b/client/src/components/SearchBar/GraphiQLRouteButton.tsx @@ -0,0 +1,18 @@ +import { Button } from 'react-bootstrap'; +import { TripQueryVariables } from '../../gql/graphql.ts'; +import { queryAsString } from '../../static/query/tripQuery.tsx'; +const graphiQLUrl = import.meta.env.VITE_GRAPHIQL_URL; + +function GraphiQLRouteButton({ tripQueryVariables }: { tripQueryVariables: TripQueryVariables }) { + const formattedVariables = encodeURIComponent(JSON.stringify(tripQueryVariables)); + const formattedQuery = encodeURIComponent(queryAsString); + + return ( +
+ +
+ ); +} +export default GraphiQLRouteButton; diff --git a/client/src/components/SearchBar/SearchBar.tsx b/client/src/components/SearchBar/SearchBar.tsx index dbdb5db29bf..dfcbc6ac36e 100644 --- a/client/src/components/SearchBar/SearchBar.tsx +++ b/client/src/components/SearchBar/SearchBar.tsx @@ -15,6 +15,7 @@ import Navbar from 'react-bootstrap/Navbar'; import { ServerInfoTooltip } from './ServerInfoTooltip.tsx'; import { useRef, useState } from 'react'; import logo from '../../static/img/otp-logo.svg'; +import GraphiQLRouteButton from './GraphiQLRouteButton.tsx'; type SearchBarProps = { onRoute: () => void; @@ -61,6 +62,7 @@ export function SearchBar({ onRoute, tripQueryVariables, setTripQueryVariables, Route + ); } diff --git a/client/src/hooks/useTripQuery.ts b/client/src/hooks/useTripQuery.ts index e50e7019b66..5ff6fc80a1f 100644 --- a/client/src/hooks/useTripQuery.ts +++ b/client/src/hooks/useTripQuery.ts @@ -1,96 +1,12 @@ import { useCallback, useEffect, useState } from 'react'; -import { graphql } from '../gql'; import { request } from 'graphql-request'; // eslint-disable-line import/no-unresolved import { QueryType, TripQueryVariables } from '../gql/graphql.ts'; import { getApiUrl } from '../util/getApiUrl.ts'; +import { query } from '../static/query/tripQuery.tsx'; /** General purpose trip query document for debugging trip searches - TODO: should live in a separate file, and split into fragments for readability */ -const query = graphql(` - query trip( - $from: Location! - $to: Location! - $arriveBy: Boolean - $dateTime: DateTime - $numTripPatterns: Int - $searchWindow: Int - $modes: Modes - $itineraryFiltersDebug: ItineraryFilterDebugProfile - $pageCursor: String - ) { - trip( - from: $from - to: $to - arriveBy: $arriveBy - dateTime: $dateTime - numTripPatterns: $numTripPatterns - searchWindow: $searchWindow - modes: $modes - itineraryFilters: { debug: $itineraryFiltersDebug } - pageCursor: $pageCursor - ) { - previousPageCursor - nextPageCursor - tripPatterns { - aimedStartTime - aimedEndTime - expectedEndTime - expectedStartTime - duration - distance - legs { - id - mode - aimedStartTime - aimedEndTime - expectedEndTime - expectedStartTime - realtime - distance - duration - fromPlace { - name - quay { - id - } - } - toPlace { - name - quay { - id - } - } - toEstimatedCall { - destinationDisplay { - frontText - } - } - line { - publicCode - name - } - authority { - name - } - pointsOnLink { - points - } - interchangeTo { - staySeated - } - interchangeFrom { - staySeated - } - } - systemNotices { - tag - } - } - } - } -`); type TripQueryHook = ( variables?: TripQueryVariables, @@ -126,6 +42,5 @@ export const useTripQuery: TripQueryHook = (variables) => { } // eslint-disable-next-line react-hooks/exhaustive-deps }, [variables?.from, variables?.to]); - return [data, loading, callback]; }; diff --git a/client/src/static/query/authorityQuery.tsx b/client/src/static/query/authorityQuery.tsx new file mode 100644 index 00000000000..e5ff99b133d --- /dev/null +++ b/client/src/static/query/authorityQuery.tsx @@ -0,0 +1,13 @@ +import { graphql } from '../../gql'; +import { print } from 'graphql/index'; + +export const query = graphql(` + query authority($id: String!) { + authority(id: $id) { + name + id + } + } +`); + +export const authorityQueryAsString = print(query); diff --git a/client/src/static/query/lineQuery.tsx b/client/src/static/query/lineQuery.tsx new file mode 100644 index 00000000000..4a3a9826a39 --- /dev/null +++ b/client/src/static/query/lineQuery.tsx @@ -0,0 +1,13 @@ +import { graphql } from '../../gql'; +import { print } from 'graphql/index'; + +export const query = graphql(` + query line($id: ID!) { + line(id: $id) { + name + publicCode + } + } +`); + +export const lineQueryAsString = print(query); diff --git a/client/src/static/query/quayQuery.tsx b/client/src/static/query/quayQuery.tsx new file mode 100644 index 00000000000..e4ab9af3146 --- /dev/null +++ b/client/src/static/query/quayQuery.tsx @@ -0,0 +1,15 @@ +import { graphql } from '../../gql'; +import { print } from 'graphql/index'; + +export const query = graphql(` + query quay($id: String!) { + quay(id: $id) { + stopPlace { + id + name + } + } + } +`); + +export const quayQueryAsString = print(query); diff --git a/client/src/static/query/tripQuery.tsx b/client/src/static/query/tripQuery.tsx new file mode 100644 index 00000000000..ef8183b0832 --- /dev/null +++ b/client/src/static/query/tripQuery.tsx @@ -0,0 +1,93 @@ +import { graphql } from '../../gql'; +import { print } from 'graphql/index'; + +export const query = graphql(` + query trip( + $from: Location! + $to: Location! + $arriveBy: Boolean + $dateTime: DateTime + $numTripPatterns: Int + $searchWindow: Int + $modes: Modes + $itineraryFiltersDebug: ItineraryFilterDebugProfile + $pageCursor: String + ) { + trip( + from: $from + to: $to + arriveBy: $arriveBy + dateTime: $dateTime + numTripPatterns: $numTripPatterns + searchWindow: $searchWindow + modes: $modes + itineraryFilters: { debug: $itineraryFiltersDebug } + pageCursor: $pageCursor + ) { + previousPageCursor + nextPageCursor + tripPatterns { + aimedStartTime + aimedEndTime + expectedEndTime + expectedStartTime + duration + distance + legs { + id + mode + aimedStartTime + aimedEndTime + expectedEndTime + expectedStartTime + realtime + distance + duration + fromPlace { + name + quay { + id + } + } + toPlace { + name + quay { + id + } + } + toEstimatedCall { + destinationDisplay { + frontText + } + } + line { + publicCode + name + id + presentation { + colour + } + } + authority { + name + id + } + pointsOnLink { + points + } + interchangeTo { + staySeated + } + interchangeFrom { + staySeated + } + } + systemNotices { + tag + } + } + } + } +`); + +export const queryAsString = print(query); diff --git a/client/src/util/getColorForMode.ts b/client/src/util/getColorForLeg.ts similarity index 64% rename from client/src/util/getColorForMode.ts rename to client/src/util/getColorForLeg.ts index cb1ad8b6981..ecb8cbc1676 100644 --- a/client/src/util/getColorForMode.ts +++ b/client/src/util/getColorForLeg.ts @@ -1,6 +1,6 @@ -import { Mode } from '../gql/graphql.ts'; +import { Leg, Mode } from '../gql/graphql.ts'; -export const getColorForMode = function (mode: Mode) { +const getColorForMode = function (mode: Mode) { if (mode === Mode.Foot) return '#191616'; if (mode === Mode.Bicycle) return '#5076D9'; if (mode === Mode.Scooter) return '#253664'; @@ -19,3 +19,14 @@ export const getColorForMode = function (mode: Mode) { if (mode === Mode.Taxi) return '#81304C'; return '#aaa'; }; + +/** + * Extract a line color from a leg. If there isn't one given by its line, this method returns a fallback color. + */ +export const getColorForLeg = function (leg: Leg) { + if (leg.line?.presentation?.colour) { + return `#${leg.line.presentation.colour}`; + } else { + return getColorForMode(leg.mode); + } +}; diff --git a/doc/templates/Configuration.md b/doc/templates/Configuration.md index 0ef96b8d4fa..6f6f4fc9960 100644 --- a/doc/templates/Configuration.md +++ b/doc/templates/Configuration.md @@ -146,7 +146,7 @@ text inserted is valid JSON (starts with `{` and ends with `}`). Variable substitution is performed on configuration file after the include file directive; Hence variable substitution is also performed on the text in the injected file. -Here is an example including variable substitution, assuming version 2.5.0 of OTP: +Here is an example including variable substitution, assuming version 2.6.0 of OTP: ```JSON // build-config.json @@ -170,7 +170,7 @@ The result will look like this: { "transitFeeds": [ { - "source": "netex-v2.5.0.obj" + "source": "netex-v2.6.0.obj" } ] } diff --git a/doc/user/Basic-Tutorial.md b/doc/user/Basic-Tutorial.md index d6e46f3f1f5..64eeded6b01 100644 --- a/doc/user/Basic-Tutorial.md +++ b/doc/user/Basic-Tutorial.md @@ -18,9 +18,9 @@ JAR containing all other libraries needed for OTP to work, and is available from repository. You will be able to go to [the OTP directory at Maven Central](https://repo1.maven.org/maven2/org/opentripplanner/otp/), navigate to -the [directory of releases](https://repo1.maven.org/maven2/org/opentripplanner/otp/2.5.0/), +the [directory of releases](https://repo1.maven.org/maven2/org/opentripplanner/otp/2.6.0/), and download -the [file with `shaded.jar` suffix](https://repo1.maven.org/maven2/org/opentripplanner/otp/2.5.0/otp-2.5.0-shaded.jar) +the [file with `shaded.jar` suffix](https://repo1.maven.org/maven2/org/opentripplanner/otp/2.6.0/otp-2.6.0-shaded.jar) . You may also want to get your own copy of the OTP source code @@ -129,7 +129,7 @@ below and in other tutorials. The simplest way to use OTP is to build a graph in a single step and start a server immediately, without saving it to disk. The command to do so is: - $ java -Xmx2G -jar otp-2.5.0-shaded.jar --build --serve /home/username/otp + $ java -Xmx2G -jar otp-2.6.0-shaded.jar --build --serve /home/username/otp where `/home/username/otp` should be the directory where you put your configuration and input files. @@ -154,13 +154,13 @@ build a graph from street and transit data then save it to a file using the `--b command line parameters together. If for example your current working directory (`.`) contains the input files and the OTP JAR file, you can use this command: - $ java -Xmx2G -jar otp-2.5.0-shaded.jar --build --save . + $ java -Xmx2G -jar otp-2.6.0-shaded.jar --build --save . This will produce a file called `graph.obj` in the same directory as the inputs. The server can then be started later using the `--load` parameter, and will read this file instead of building the graph from scratch: - $ java -Xmx2G -jar otp-2.5.0-shaded.jar --load . + $ java -Xmx2G -jar otp-2.6.0-shaded.jar --load . Another reason to perform these two phases separately is that the building process loads the entire GTFS and OSM data sets into memory, so can require significantly more memory than just running a @@ -177,16 +177,16 @@ graph once, and then layer transit data on top of the streets to make the final Again assuming the input files and OTP JAR file are in the current working directory, you can build a street graph with OSM and elevation data only (ignoring transit input files) with this command: - $ java -Xmx2G -jar otp-2.5.0-shaded.jar --buildStreet . + $ java -Xmx2G -jar otp-2.6.0-shaded.jar --buildStreet . Then, to build a graph layering transit data on top of the saved street graph (built using the previous command): - $ java -Xmx2G -jar otp-2.5.0-shaded.jar --loadStreet --save . + $ java -Xmx2G -jar otp-2.6.0-shaded.jar --loadStreet --save . Finally, the server can be started using the `--load` parameter: - $ java -Xmx2G -jar otp-2.5.0-shaded.jar --load . + $ java -Xmx2G -jar otp-2.6.0-shaded.jar --load . ## Command Line Switches diff --git a/doc/user/BoardingLocations.md b/doc/user/BoardingLocations.md index 7151b40e176..bf92033624b 100644 --- a/doc/user/BoardingLocations.md +++ b/doc/user/BoardingLocations.md @@ -60,3 +60,8 @@ add the following to `build-config.json`: Some stations have a middle platform with a stop on either side of it. In such a case, you can simply add two or more references separated by a semicolon, as seen in [this example](https://www.openstreetmap.org/way/27558650). + +## Related chat threads + +- [Thread by Tim Fowle](https://matrix.to/#/!oXNNoHKzbaSOlFzLEt:gitter.im/$740KuVeCc65IW7HO9VjvYk92ACk0cOcjKA_BJhnDMSU?via=gitter.im&via=matrix.org&via=builtin.io) +- [Thread by Sam Cedarbaum](https://matrix.to/#/!oXNNoHKzbaSOlFzLEt:gitter.im/$XY7X9KC0FNSajQ8zDEPUARlv6QHOUd3Qn0R3G2POpqk?via=gitter.im&via=matrix.org&via=builtin.io) \ No newline at end of file diff --git a/doc/user/Changelog.md b/doc/user/Changelog.md index 5e93c90b66c..844b89abc1e 100644 --- a/doc/user/Changelog.md +++ b/doc/user/Changelog.md @@ -3,61 +3,60 @@ The changelog lists most feature changes between each release. The list is automatically created based on merged pull requests. Search GitHub issues and pull requests for smaller issues. -## 2.6.0-SNAPSHOT (under development) +## 2.7.0-SNAPSHOT (under development) -- ISO-8601 date time for GTFS API itinerary responses [#5660](https://github.com/opentripplanner/OpenTripPlanner/pull/5660) +- Extra leg when transferring at the same stop [#5984](https://github.com/opentripplanner/OpenTripPlanner/pull/5984) +[](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) + +## 2.6.0 (2024-09-18) + +### Notable Changes + +- ISO-8601 date time for GTFS GraphQL API itinerary responses [#5660](https://github.com/opentripplanner/OpenTripPlanner/pull/5660) +- Add support for query parameter to enforce booking time in trip search for flexible services [#5606](https://github.com/opentripplanner/OpenTripPlanner/pull/5606) +- New GTFS GraphQL plan query [#5185](https://github.com/opentripplanner/OpenTripPlanner/pull/5185) +- Make new debug client the default, move old one to `classic-debug` [#5924](https://github.com/opentripplanner/OpenTripPlanner/pull/5924) [#5962](https://github.com/opentripplanner/OpenTripPlanner/pull/5962) [#6012](https://github.com/opentripplanner/OpenTripPlanner/pull/6012) [#6053](https://github.com/opentripplanner/OpenTripPlanner/pull/6053) +- Disable Legacy REST API by default [#5948](https://github.com/opentripplanner/OpenTripPlanner/pull/5948) + +### Detailed changes by Pull Request + +- SIRI real time improvements and bug fixes [#5867](https://github.com/opentripplanner/OpenTripPlanner/pull/5867) [#5931](https://github.com/opentripplanner/OpenTripPlanner/pull/5931) [#5865](https://github.com/opentripplanner/OpenTripPlanner/pull/5865) +- Real time improvements and bug fixes [#5726](https://github.com/opentripplanner/OpenTripPlanner/pull/5726) [#5941](https://github.com/opentripplanner/OpenTripPlanner/pull/5941) [#6007](https://github.com/opentripplanner/OpenTripPlanner/pull/6007) - Fix street routing on roundabout [#5732](https://github.com/opentripplanner/OpenTripPlanner/pull/5732) - Expose route sort order in GTFS API [#5764](https://github.com/opentripplanner/OpenTripPlanner/pull/5764) - Fix issue with cancellations on trip patterns that run after midnight [#5719](https://github.com/opentripplanner/OpenTripPlanner/pull/5719) -- Fix handling of null transport mode filter [#5789](https://github.com/opentripplanner/OpenTripPlanner/pull/5789) - Discourage instead of ban cycling on use_sidepath ways and do the same for walking on foot=use_sidepath [#5790](https://github.com/opentripplanner/OpenTripPlanner/pull/5790) - Prune islands with mode-less stop vertices [#5782](https://github.com/opentripplanner/OpenTripPlanner/pull/5782) - Overwrite default WALK directMode when it is not set in the request, but modes is set [#5779](https://github.com/opentripplanner/OpenTripPlanner/pull/5779) - Fix trip duplication in Graph Builder DSJ mapping [#5794](https://github.com/opentripplanner/OpenTripPlanner/pull/5794) -- Optionally abort startup when unknown configuration parameters were detected [#5676](https://github.com/opentripplanner/OpenTripPlanner/pull/5676) - Fix bug in heuristics cost calculation for egress legs [#5783](https://github.com/opentripplanner/OpenTripPlanner/pull/5783) -- Fix handling of implicit access and egress mode parameters. [#5821](https://github.com/opentripplanner/OpenTripPlanner/pull/5821) -- Make naming of stopTransferCosts/stopBoardAlightCosts consistent [#5822](https://github.com/opentripplanner/OpenTripPlanner/pull/5822) +- Fix handling of implicit access and egress mode parameters [#5821](https://github.com/opentripplanner/OpenTripPlanner/pull/5821) - Namer for applying street names to nearby sidewalks [#5774](https://github.com/opentripplanner/OpenTripPlanner/pull/5774) - Implement GTFS Flex safe duration spec draft [#5796](https://github.com/opentripplanner/OpenTripPlanner/pull/5796) -- Document and validate timeRange GraphQL parameter [#5834](https://github.com/opentripplanner/OpenTripPlanner/pull/5834) -- Log the origin of a request that causes a transfer cache addition. [#5874](https://github.com/opentripplanner/OpenTripPlanner/pull/5874) -- Fix handling of missing aimed departure time [#5865](https://github.com/opentripplanner/OpenTripPlanner/pull/5865) - Add OTP request timeout GraphQL instrumentation [#5881](https://github.com/opentripplanner/OpenTripPlanner/pull/5881) - Add feed publisher name and url to GTFS GraphQL API [#5835](https://github.com/opentripplanner/OpenTripPlanner/pull/5835) -- Add support for query parameter to enforce booking time in trip search for flexible services [#5606](https://github.com/opentripplanner/OpenTripPlanner/pull/5606) - Fix parsing of GBFS feeds [#5891](https://github.com/opentripplanner/OpenTripPlanner/pull/5891) -- Fix SIRI update travel back in time [#5867](https://github.com/opentripplanner/OpenTripPlanner/pull/5867) -- Limit result size and execution time in TransModel GraphQL API [#5883](https://github.com/opentripplanner/OpenTripPlanner/pull/5883) -- Fix real-time added patterns persistence with DIFFERENTIAL updates [#5726](https://github.com/opentripplanner/OpenTripPlanner/pull/5726) -- Add plan query that follows the relay connection specification [#5185](https://github.com/opentripplanner/OpenTripPlanner/pull/5185) -- Fix debug client after breaking change in dependency graphql-request [#5899](https://github.com/opentripplanner/OpenTripPlanner/pull/5899) +- Limit result size and execution time in Transmodel GraphQL API [#5883](https://github.com/opentripplanner/OpenTripPlanner/pull/5883) - Remove TravelTime API [#5890](https://github.com/opentripplanner/OpenTripPlanner/pull/5890) -- Improve cancellation of large response in TransModel API [#5908](https://github.com/opentripplanner/OpenTripPlanner/pull/5908) -- Refactor SIRI-ET updaters [#5904](https://github.com/opentripplanner/OpenTripPlanner/pull/5904) -- Update Google Pubsub updater configuration [#5927](https://github.com/opentripplanner/OpenTripPlanner/pull/5927) -- Make new debug client the default, move old one to `classic-debug` [#5924](https://github.com/opentripplanner/OpenTripPlanner/pull/5924) +- Improve cancellation of large response in Transmodel API [#5908](https://github.com/opentripplanner/OpenTripPlanner/pull/5908) - Require valid polygons for AreaStop [#5915](https://github.com/opentripplanner/OpenTripPlanner/pull/5915) - Fix NullPointerException in stop transfer priority cost vector generation [#5943](https://github.com/opentripplanner/OpenTripPlanner/pull/5943) - Convert transferSlack configuration to duration [#5897](https://github.com/opentripplanner/OpenTripPlanner/pull/5897) - Expose stop transfer priority in Transmodel API [#5942](https://github.com/opentripplanner/OpenTripPlanner/pull/5942) -- Add rental system to GraphQL API [#5909](https://github.com/opentripplanner/OpenTripPlanner/pull/5909) -- Improve handling of SIRI added trip with unresolvable agency [#5931](https://github.com/opentripplanner/OpenTripPlanner/pull/5931) -- Fix copy-on-write in TimetableSnapshot [#5941](https://github.com/opentripplanner/OpenTripPlanner/pull/5941) +- Add rental system to GTFS GraphQL API [#5909](https://github.com/opentripplanner/OpenTripPlanner/pull/5909) - Generate documentation for OSM tag mappers [#5929](https://github.com/opentripplanner/OpenTripPlanner/pull/5929) -- Disable Legacy REST API by default [#5948](https://github.com/opentripplanner/OpenTripPlanner/pull/5948) - Enforce non-null coordinates on multimodal station [#5971](https://github.com/opentripplanner/OpenTripPlanner/pull/5971) - Add car rental to Transmodel street mode options [#5977](https://github.com/opentripplanner/OpenTripPlanner/pull/5977) -- Add debug information for stop/quay ID and stay-seated transfers [#5962](https://github.com/opentripplanner/OpenTripPlanner/pull/5962) - Handle NeTEx `any` version [#5983](https://github.com/opentripplanner/OpenTripPlanner/pull/5983) - Keep at least one result for min-transfers and each transit-group in itinerary-group-filter [#5919](https://github.com/opentripplanner/OpenTripPlanner/pull/5919) - Extract parking lots from NeTEx feeds [#5946](https://github.com/opentripplanner/OpenTripPlanner/pull/5946) - Filter routes and patterns by service date in GTFS GraphQL API [#5869](https://github.com/opentripplanner/OpenTripPlanner/pull/5869) - SIRI-FM vehicle parking updates [#5979](https://github.com/opentripplanner/OpenTripPlanner/pull/5979) - Take realtime patterns into account when storing realtime vehicles [#5994](https://github.com/opentripplanner/OpenTripPlanner/pull/5994) -- Debug client itinerary list style improvements [#6012](https://github.com/opentripplanner/OpenTripPlanner/pull/6012) - Developer Decision Records [#5932](https://github.com/opentripplanner/OpenTripPlanner/pull/5932) -[](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) +- Allow NeTEx ServiceJourneyPatterns with stopUse=passthrough [#6037](https://github.com/opentripplanner/OpenTripPlanner/pull/6037) +- Use correct HEAD request when fetching HTTP headers [#6063](https://github.com/opentripplanner/OpenTripPlanner/pull/6063) +- Fix incorrect mapping of flex safe duration offset [#6059](https://github.com/opentripplanner/OpenTripPlanner/pull/6059) ## 2.5.0 (2024-03-13) diff --git a/doc/user/Configuration.md b/doc/user/Configuration.md index 8e366e476a9..58cc31c0213 100644 --- a/doc/user/Configuration.md +++ b/doc/user/Configuration.md @@ -173,7 +173,7 @@ text inserted is valid JSON (starts with `{` and ends with `}`). Variable substitution is performed on configuration file after the include file directive; Hence variable substitution is also performed on the text in the injected file. -Here is an example including variable substitution, assuming version 2.5.0 of OTP: +Here is an example including variable substitution, assuming version 2.6.0 of OTP: ```JSON // build-config.json @@ -197,7 +197,7 @@ The result will look like this: { "transitFeeds": [ { - "source": "netex-v2.5.0.obj" + "source": "netex-v2.6.0.obj" } ] } @@ -226,6 +226,7 @@ Here is a list of all features which can be toggled on/off and their default val | `APIUpdaterStatus` | Enable endpoint for graph updaters status. | ✓️ | | | `ConsiderPatternsForDirectTransfers` | Enable limiting transfers so that there is only a single transfer to each pattern. | ✓️ | | | `DebugUi` | Enable the debug GraphQL client and web UI and located at the root of the web server as well as the debug map tiles it uses. Be aware that the map tiles are not a stable API and can change without notice. Use the [vector tiles feature if](sandbox/MapboxVectorTilesApi.md) you want a stable map tiles API. | ✓️ | | +| `ExtraTransferLegOnSameStop` | Should there be a transfer leg when transferring on the very same stop. Note that for in-seat/interlined transfers no transfer leg will be generated. | | | | `FloatingBike` | Enable floating bike routing. | ✓️ | | | `GtfsGraphQlApi` | Enable the [GTFS GraphQL API](apis/GTFS-GraphQL-API.md). | ✓️ | | | `GtfsGraphQlApiRentalStationFuzzyMatching` | Does vehicleRentalStation query also allow ids that are not feed scoped. | | | diff --git a/doc/user/Deployments.md b/doc/user/Deployments.md index 56ff23c113c..919ae331aaf 100644 --- a/doc/user/Deployments.md +++ b/doc/user/Deployments.md @@ -79,6 +79,7 @@ The following are known deployments of OTP in a government- or agency-sponsored * **Lower Saxony, Germany** The [VBN](https://www.vbn.de/en/) transportation authority offers an [OTP instance](https://www.vbn.de/en/service/developer-information/opendata-and-openservice) as alternative to the [Hafas](https://www.hacon.de/en/portfolio/information-ticketing/#section_8294) passenger information system. * **Leipzig, Germany** As of summer 2020 [Leipzig Move](https://leipzig-move.de/) has been using OpenTripPlanner. +* **Iceland (nationwide)** – [Strætó](https://www.straeto.is/en) has used OTP from 2015. ## Independent Production diff --git a/doc/user/Getting-OTP.md b/doc/user/Getting-OTP.md index a71bc02b5d8..cfb0102c2a3 100644 --- a/doc/user/Getting-OTP.md +++ b/doc/user/Getting-OTP.md @@ -9,8 +9,8 @@ the [release pages on GitHub](https://github.com/opentripplanner/OpenTripPlanner or [the OTP directory at Maven Central](https://repo1.maven.org/maven2/org/opentripplanner/otp/), navigate to the highest version number, and download the file whose name ends with `shaded.jar`. -Note that version numbers like `v2.1.0-rc1` or `v2.5.0-SNAPSHOT` refer to development builds _ -before_ the release version `v2.5.0`. The existence of a build `vX.Y.Z-SNAPSHOT` does not mean +Note that version numbers like `v2.1.0-rc1` or `v2.6.0-SNAPSHOT` refer to development builds _ +before_ the release version `v2.6.0`. The existence of a build `vX.Y.Z-SNAPSHOT` does not mean that `vX.Y.Z` has been released yet. We use the [Github Actions CI system](https://github.com/opentripplanner/OpenTripPlanner/actions) to @@ -87,7 +87,7 @@ For example, you could do the following: ```bash cd OpenTripPlanner -git checkout v2.5.0 +git checkout v2.6.0 git clean -df mvn clean package -DskipTests ``` @@ -110,8 +110,8 @@ file) to the Maven repository, from which it can be automatically included in ot This repository is machine-readable (by Maven or other build systems) and also provides human readable directory listings via HTTP. You can fetch an OTP JAR from this repository by constructing -the proper URL for the release you want. For example, release 2.5.0 will be found -at `https://repo1.maven.org/maven2/org/opentripplanner/otp/2.5.0/otp-2.5.0-shaded.jar`. +the proper URL for the release you want. For example, release 2.6.0 will be found +at `https://repo1.maven.org/maven2/org/opentripplanner/otp/2.6.0/otp-2.6.0-shaded.jar`. To make use of OTP in another Maven project, you must specify it as a dependency in that project's `pom.xml`: @@ -120,6 +120,6 @@ project's `pom.xml`: org.opentripplanner otp - 2.5.0 + 2.6.0 ``` diff --git a/doc/user/index.md b/doc/user/index.md index a8f00a3184c..89af4f31e2b 100644 --- a/doc/user/index.md +++ b/doc/user/index.md @@ -26,7 +26,8 @@ the selector in the upper left of the published documentation. **Releases** -- [Latest](http://docs.opentripplanner.org/en/latest) - Version 2.5 (the git master branch) +- [Latest](http://docs.opentripplanner.org/en/latest) - Version 2.6 (the git master branch) +- [v2.5.0](http://docs.opentripplanner.org/en/v2.5.0) - Version 2.5 - [v2.4.0](http://docs.opentripplanner.org/en/v2.4.0) - Version 2.4 - [v2.3.0](http://docs.opentripplanner.org/en/v2.3.0) - Version 2.3 - [v2.2.0](http://docs.opentripplanner.org/en/v2.2.0) - Version 2.2 diff --git a/pom.xml b/pom.xml index 398432f7303..146633fd222 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ https://opentripplanner.org org.opentripplanner otp - 2.6.0-SNAPSHOT + 2.7.0-SNAPSHOT jar @@ -56,14 +56,14 @@ - 156 + 158 - 31.3 + 32.0 2.52 2.17.2 3.1.8 5.11.0 - 1.13.2 + 1.13.4 5.6.0 1.5.7 9.11.1 @@ -247,7 +247,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.3.1 + 3.5.0 me.fabriciorby @@ -540,11 +540,6 @@ Open Source Geospatial Foundation Repository https://repo.osgeo.org/repository/release/ - - onebusaway-releases - Onebusaway Releases Repo - https://repo.camsys-apps.com/releases/ - @@ -553,7 +548,7 @@ com.google.cloud libraries-bom - 26.44.0 + 26.45.0 pom import @@ -713,7 +708,7 @@ org.mockito mockito-core - 5.12.0 + 5.13.0 test @@ -838,13 +833,7 @@ org.onebusaway onebusaway-gtfs - 1.4.17 - - - org.slf4j - slf4j-simple - - + 3.2.3 @@ -873,7 +862,7 @@ com.graphql-java graphql-java - 22.2 + 22.3 com.graphql-java @@ -937,7 +926,7 @@ org.apache.commons commons-compress - 1.27.0 + 1.27.1 test diff --git a/renovate.json5 b/renovate.json5 index b672797d48f..045bbe07d22 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -150,6 +150,14 @@ "com.google.dagger:" ], "minimumReleaseAge": "1 week" + }, + { + "description": "Geotools takes a while to publish a changelog and since it pulls in JTS it can change the serialization of the graph", + "matchPackagePrefixes": [ + "org.geotools:" + ], + "minimumReleaseAge": "1 week", + "labels": ["skip changelog", "bump serialization id"] } ], "timezone": "Europe/Berlin" diff --git a/src/client/index.html b/src/client/index.html index aa609bb0696..5b3dd90b341 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -5,8 +5,8 @@ OTP Debug Client - - + +
diff --git a/src/ext-test/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolverTest.java b/src/ext-test/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolverTest.java index af415b5e883..21d5a7f1696 100644 --- a/src/ext-test/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolverTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/realtimeresolver/RealtimeResolverTest.java @@ -176,8 +176,9 @@ private static TransitService makeTransitService( transitModel.updateCalendarServiceData(true, calendarServiceData, DataImportIssueStore.NOOP); transitModel.index(); - var alertService = new TransitAlertServiceImpl(transitModel); return new DefaultTransitService(transitModel) { + final TransitAlertService alertService = new TransitAlertServiceImpl(transitModel); + @Override public TransitAlertService getTransitAlertService() { return alertService; diff --git a/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java b/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java index 59995062eb7..364d6215252 100644 --- a/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/siri/AddedTripBuilderTest.java @@ -32,7 +32,7 @@ import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.timetable.RealTimeState; import org.opentripplanner.transit.model.timetable.Trip; -import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitEditorService; @@ -135,15 +135,16 @@ void testAddedTrip() { assertTrue(addedTrip.isSuccess(), "Trip creation should succeed"); + TripUpdate tripUpdate = addedTrip.successValue(); // Assert trip - Trip trip = addedTrip.successValue().tripTimes().getTrip(); + Trip trip = tripUpdate.tripTimes().getTrip(); assertEquals(TRIP_ID, trip.getId(), "Trip should be mapped"); assertEquals(OPERATOR, trip.getOperator(), "operator should be mapped"); assertEquals(TRANSIT_MODE, trip.getMode(), "transitMode should be mapped"); assertEquals(SubMode.of(SUB_MODE), trip.getNetexSubMode(), "submode should be mapped"); assertNotNull(trip.getHeadsign(), "Headsign should be mapped"); assertEquals(HEADSIGN, trip.getHeadsign().toString(), "Headsign should be mapped"); - assertEquals(SERVICE_DATE, addedTrip.successValue().serviceDate()); + assertEquals(SERVICE_DATE, tripUpdate.serviceDate()); // Assert route Route route = trip.getRoute(); @@ -154,17 +155,11 @@ void testAddedTrip() { assertEquals(SubMode.of(SUB_MODE), route.getNetexSubmode(), "submode should be mapped"); assertNotEquals(REPLACED_ROUTE, route, "Should not re-use replaced route"); - assertEquals( - route, - transitService.getRouteForId(TransitModelForTest.id(LINE_REF)), - "Route should be added to transit index" - ); - assertEquals( - trip, - transitService.getTripForId(TRIP_ID), - "Route should be added to transit index" - ); - var pattern = transitService.getPatternForTrip(trip); + assertTrue(tripUpdate.routeCreation(), "The route is marked as created by real time updater"); + + assertTrue(tripUpdate.tripCreation(), "The trip is marked as created by real time updater"); + + TripPattern pattern = tripUpdate.addedTripPattern(); assertNotNull(pattern); assertEquals(route, pattern.getRoute()); assertTrue( @@ -173,19 +168,11 @@ void testAddedTrip() { .contains(TRANSIT_MODEL.getServiceCodes().get(trip.getServiceId())), "serviceId should be running on service date" ); - assertNotNull( - transitService.getTripOnServiceDateById(TRIP_ID), - "TripOnServiceDate should be added to transit index by id" - ); - assertNotNull( - transitService.getTripOnServiceDateForTripAndDay( - new TripIdAndServiceDate(TRIP_ID, SERVICE_DATE) - ), - "TripOnServiceDate should be added to transit index for trip and day" - ); + TripOnServiceDate tripOnServiceDate = tripUpdate.addedTripOnServiceDate(); + assertNotNull(tripOnServiceDate, "The TripUpdate should contain a new TripOnServiceDate"); // Assert stop pattern - var stopPattern = addedTrip.successValue().stopPattern(); + var stopPattern = tripUpdate.stopPattern(); assertEquals(stopPattern, pattern.getStopPattern()); assertEquals(3, stopPattern.getSize()); assertEquals(STOP_A, stopPattern.getStop(0)); @@ -215,7 +202,7 @@ void testAddedTrip() { assertEquals(0, scheduledTimes.getArrivalDelay(2)); // Assert updated trip times - var times = addedTrip.successValue().tripTimes(); + var times = tripUpdate.tripTimes(); assertEquals(trip, times.getTrip()); assertEquals(RealTimeState.ADDED, times.getRealTimeState()); assertFalse(times.isScheduled()); @@ -259,6 +246,8 @@ void testAddedTripOnAddedRoute() { .build(); assertTrue(firstAddedTrip.isSuccess(), "Trip creation should succeed"); + assertTrue(firstAddedTrip.successValue().routeCreation()); + var firstTrip = firstAddedTrip.successValue().tripTimes().getTrip(); var tripId2 = TransitModelForTest.id("TRIP_ID_2"); @@ -291,12 +280,6 @@ void testAddedTripOnAddedRoute() { assertEquals(tripId2, secondTrip.getId(), "Trip should be mapped"); assertNotEquals(firstTrip, secondTrip); - // Assert route - Route route = secondTrip.getRoute(); - assertSame(firstTrip.getRoute(), route, "route be reused from the first trip"); - - assertEquals(2, transitService.getPatternsForRoute(route).size()); - // Assert trip times var times = secondAddedTrip.successValue().tripTimes(); assertEquals(secondTrip, times.getTrip()); @@ -338,6 +321,9 @@ void testAddedTripOnExistingRoute() { Trip trip = addedTrip.successValue().tripTimes().getTrip(); assertEquals(TRIP_ID, trip.getId(), "Trip should be mapped"); assertSame(REPLACED_ROUTE, trip.getRoute()); + + // Assert route + assertFalse(addedTrip.successValue().routeCreation(), "The existing route should be reused"); } @Test @@ -370,6 +356,7 @@ void testAddedTripWithoutReplacedRoute() { assertEquals(TRIP_ID, trip.getId(), "Trip should be mapped"); // Assert route + assertTrue(addedTrip.successValue().routeCreation(), "A new route should be created"); Route route = trip.getRoute(); assertEquals(LINE_REF, route.getId().getId(), "route should be mapped"); assertEquals(AGENCY, route.getAgency(), "Agency should be taken from replaced route"); diff --git a/src/ext-test/java/org/opentripplanner/ext/siri/SiriAlertsUpdateHandlerTest.java b/src/ext-test/java/org/opentripplanner/ext/siri/SiriAlertsUpdateHandlerTest.java index 7c603ea7b2e..e861ca81c95 100644 --- a/src/ext-test/java/org/opentripplanner/ext/siri/SiriAlertsUpdateHandlerTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/siri/SiriAlertsUpdateHandlerTest.java @@ -31,7 +31,9 @@ import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.TransitService; +import org.opentripplanner.updater.DefaultRealTimeUpdateContext; import org.opentripplanner.updater.GraphUpdaterManager; +import org.opentripplanner.updater.RealTimeUpdateContext; import uk.org.ifopt.siri20.StopPlaceRef; import uk.org.siri.siri20.AffectedLineStructure; import uk.org.siri.siri20.AffectedRouteStructure; @@ -66,6 +68,8 @@ public class SiriAlertsUpdateHandlerTest extends GtfsTest { TransitService transitService; + private RealTimeUpdateContext realTimeUpdateContext; + @Override public String getFeedName() { return "gtfs/interlining"; @@ -76,22 +80,17 @@ public String getFeedName() { public void setUp() throws Exception { super.setUp(); + realTimeUpdateContext = new DefaultRealTimeUpdateContext(graph, transitModel); if (transitService == null) { transitService = new DefaultTransitService(transitModel); - transitModel.setUpdaterManager(new GraphUpdaterManager(graph, transitModel, List.of())); + transitModel.setUpdaterManager(new GraphUpdaterManager(realTimeUpdateContext, List.of())); } else { transitAlertService.getAllAlerts().clear(); } if (alertsUpdateHandler == null) { transitAlertService = new TransitAlertServiceImpl(transitModel); alertsUpdateHandler = - new SiriAlertsUpdateHandler( - FEED_ID, - transitModel, - transitAlertService, - SiriFuzzyTripMatcher.of(transitService), - Duration.ZERO - ); + new SiriAlertsUpdateHandler(FEED_ID, transitAlertService, Duration.ZERO); } } @@ -132,7 +131,7 @@ public void testSiriSxUpdateForStop() { ptSituation.setSeverity(SeverityEnumeration.SEVERE); final ServiceDelivery serviceDelivery = createServiceDelivery(ptSituation); - alertsUpdateHandler.update(serviceDelivery); + alertsUpdateHandler.update(serviceDelivery, realTimeUpdateContext); assertFalse(transitAlertService.getAllAlerts().isEmpty()); @@ -242,7 +241,7 @@ public void testSiriSxUpdateForStopMultipleValidityPeriods() { ptSituation.setSeverity(SeverityEnumeration.SEVERE); final ServiceDelivery serviceDelivery = createServiceDelivery(ptSituation); - alertsUpdateHandler.update(serviceDelivery); + alertsUpdateHandler.update(serviceDelivery, realTimeUpdateContext); assertFalse(transitAlertService.getAllAlerts().isEmpty()); @@ -282,7 +281,7 @@ public void testSiriSxUpdateForMultipleStops() { ptSituation.setSeverity(severity); final ServiceDelivery serviceDelivery = createServiceDelivery(ptSituation); - alertsUpdateHandler.update(serviceDelivery); + alertsUpdateHandler.update(serviceDelivery, realTimeUpdateContext); assertFalse(transitAlertService.getAllAlerts().isEmpty()); @@ -360,7 +359,7 @@ public void testSiriSxUpdateForTrip() { createAffectsFramedVehicleJourney(tripId.getId(), "2014-01-01", null) ); - alertsUpdateHandler.update(createServiceDelivery(ptSituation)); + alertsUpdateHandler.update(createServiceDelivery(ptSituation), realTimeUpdateContext); assertFalse(transitAlertService.getAllAlerts().isEmpty()); @@ -410,7 +409,7 @@ public void testSiriSxUpdateForTripWithoutSpecificDate() { createAffectsFramedVehicleJourney(tripId.getId(), null, null) ); - alertsUpdateHandler.update(createServiceDelivery(ptSituation)); + alertsUpdateHandler.update(createServiceDelivery(ptSituation), realTimeUpdateContext); assertFalse(transitAlertService.getAllAlerts().isEmpty()); @@ -470,7 +469,7 @@ public void testSiriSxUpdateForTripByVehicleJourney() { createAffectsVehicleJourney(tripId.getId(), startTime, null) ); - alertsUpdateHandler.update(createServiceDelivery(ptSituation)); + alertsUpdateHandler.update(createServiceDelivery(ptSituation), realTimeUpdateContext); assertFalse(transitAlertService.getAllAlerts().isEmpty()); @@ -509,7 +508,7 @@ public void testSiriSxUpdateForTripAndStopByVehicleJourney() { createAffectsVehicleJourney(tripId.getId(), startTime, stopId0.getId(), stopId1.getId()) ); - alertsUpdateHandler.update(createServiceDelivery(ptSituation)); + alertsUpdateHandler.update(createServiceDelivery(ptSituation), realTimeUpdateContext); assertFalse(transitAlertService.getAllAlerts().isEmpty()); @@ -554,7 +553,7 @@ public void testSiriSxUpdateForTripByDatedVehicleJourney() { createAffectsDatedVehicleJourney(tripId.getId(), null) ); - alertsUpdateHandler.update(createServiceDelivery(ptSituation)); + alertsUpdateHandler.update(createServiceDelivery(ptSituation), realTimeUpdateContext); assertFalse(transitAlertService.getAllAlerts().isEmpty()); @@ -591,7 +590,7 @@ public void testSiriSxUpdateForLine() { ); final ServiceDelivery serviceDelivery = createServiceDelivery(ptSituation); - alertsUpdateHandler.update(serviceDelivery); + alertsUpdateHandler.update(serviceDelivery, realTimeUpdateContext); assertFalse(transitAlertService.getAllAlerts().isEmpty()); @@ -636,7 +635,7 @@ public void testSiriSxUpdateForLineThenExpiry() { createAffectsLine(lineRef.getId(), null) ); - alertsUpdateHandler.update(createServiceDelivery(ptSituation)); + alertsUpdateHandler.update(createServiceDelivery(ptSituation), realTimeUpdateContext); assertFalse(transitAlertService.getAllAlerts().isEmpty()); @@ -659,7 +658,7 @@ public void testSiriSxUpdateForLineThenExpiry() { ptSituation.setProgress(WorkflowStatusEnumeration.CLOSED); - alertsUpdateHandler.update(createServiceDelivery(ptSituation)); + alertsUpdateHandler.update(createServiceDelivery(ptSituation), realTimeUpdateContext); tripPatches = transitAlertService.getRouteAlerts(lineRef); @@ -690,7 +689,7 @@ public void testSiriSxUpdateForTripAndStop() { ); final ServiceDelivery serviceDelivery = createServiceDelivery(ptSituation); - alertsUpdateHandler.update(serviceDelivery); + alertsUpdateHandler.update(serviceDelivery, realTimeUpdateContext); assertFalse(transitAlertService.getAllAlerts().isEmpty()); @@ -740,7 +739,7 @@ public void testSiriSxUpdateForLineAndStop() { ); final ServiceDelivery serviceDelivery = createServiceDelivery(ptSituation); - alertsUpdateHandler.update(serviceDelivery); + alertsUpdateHandler.update(serviceDelivery, realTimeUpdateContext); assertFalse(transitAlertService.getAllAlerts().isEmpty()); @@ -768,7 +767,7 @@ public void testSiriSxUpdateForLineAndExternallyDefinedStopPoint() { ); final ServiceDelivery serviceDelivery = createServiceDelivery(ptSituation); - alertsUpdateHandler.update(serviceDelivery); + alertsUpdateHandler.update(serviceDelivery, realTimeUpdateContext); assertFalse(transitAlertService.getAllAlerts().isEmpty()); @@ -802,7 +801,7 @@ public void testSiriSxWithOpenEndedValidity() { ptSituation.getValidityPeriods().add(period_2); final ServiceDelivery serviceDelivery = createServiceDelivery(ptSituation); - alertsUpdateHandler.update(serviceDelivery); + alertsUpdateHandler.update(serviceDelivery, realTimeUpdateContext); assertFalse(transitAlertService.getAllAlerts().isEmpty()); @@ -839,7 +838,7 @@ public void testSiriSxUpdateForLineAndExternallyDefinedStopPlace() { ); final ServiceDelivery serviceDelivery = createServiceDelivery(ptSituation); - alertsUpdateHandler.update(serviceDelivery); + alertsUpdateHandler.update(serviceDelivery, realTimeUpdateContext); assertFalse(transitAlertService.getAllAlerts().isEmpty()); assertSeparateLineAndStopAlerts(situationNumber, routeId, stopId0, stopId1); @@ -858,7 +857,7 @@ public void testSiriSxUpdateForUnknownEntity() { ); final ServiceDelivery serviceDelivery = createServiceDelivery(ptSituation); - alertsUpdateHandler.update(serviceDelivery); + alertsUpdateHandler.update(serviceDelivery, realTimeUpdateContext); Collection alerts = transitAlertService.getAllAlerts(); assertEquals(1, alerts.size()); diff --git a/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java b/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java index 45e7979b353..cf29eec8f49 100644 --- a/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java @@ -1,14 +1,22 @@ package org.opentripplanner.ext.siri; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.opentripplanner.updater.spi.UpdateResultAssertions.assertFailure; +import static org.opentripplanner.updater.trip.RealtimeTestEnvironment.SERVICE_DATE; -import java.util.Set; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.opentripplanner.transit.model._data.TransitModelForTest; +import org.opentripplanner.transit.model.framework.FeedScopedId; +import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.timetable.RealTimeState; +import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; +import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.updater.spi.UpdateError; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; +import uk.org.siri.siri20.EstimatedTimetableDeliveryStructure; class SiriTimetableSnapshotSourceTest { @@ -30,18 +38,51 @@ void testCancelTrip() { } @Test - void testAddJourney() { + void testAddJourneyWithExistingRoute() { var env = RealtimeTestEnvironment.siri(); - var updates = new SiriEtBuilder(env.getDateTimeHelper()) - .withEstimatedVehicleJourneyCode("newJourney") - .withIsExtraJourney(true) - .withOperatorRef(env.operator1Id.getId()) - .withLineRef(env.route1Id.getId()) - .withRecordedCalls(builder -> builder.call(env.stopC1).departAimedActual("00:01", "00:02")) - .withEstimatedCalls(builder -> builder.call(env.stopD1).arriveAimedExpected("00:03", "00:04")) + Route route = env.getTransitService().getRouteForId(env.route1Id); + int numPatternForRoute = env.getTransitService().getPatternsForRoute(route).size(); + + String newJourneyId = "newJourney"; + var updates = createValidAddedJourney(env).buildEstimatedTimetableDeliveries(); + + var result = env.applyEstimatedTimetable(updates); + + assertEquals(1, result.successful()); + assertEquals("ADDED | C1 [R] 0:02 0:02 | D1 0:04 0:04", env.getRealtimeTimetable(newJourneyId)); + assertEquals( + "SCHEDULED | C1 0:01 0:01 | D1 0:03 0:03", + env.getScheduledTimetable(newJourneyId) + ); + FeedScopedId tripId = TransitModelForTest.id(newJourneyId); + TransitService transitService = env.getTransitService(); + Trip trip = transitService.getTripForId(tripId); + assertNotNull(trip); + assertNotNull(transitService.getPatternForTrip(trip)); + assertNotNull(transitService.getTripOnServiceDateById(tripId)); + assertNotNull( + transitService.getTripOnServiceDateForTripAndDay( + new TripIdAndServiceDate(tripId, SERVICE_DATE) + ) + ); + assertEquals( + numPatternForRoute + 1, + transitService.getPatternsForRoute(route).size(), + "The added trip should use a new pattern for this route" + ); + } + + @Test + void testAddJourneyWithNewRoute() { + var env = RealtimeTestEnvironment.siri(); + + String newRouteRef = "new route ref"; + var updates = createValidAddedJourney(env) + .withLineRef(newRouteRef) .buildEstimatedTimetableDeliveries(); + int numRoutes = env.getTransitService().getAllRoutes().size(); var result = env.applyEstimatedTimetable(updates); assertEquals(1, result.successful()); @@ -50,6 +91,26 @@ void testAddJourney() { "SCHEDULED | C1 0:01 0:01 | D1 0:03 0:03", env.getScheduledTimetable("newJourney") ); + TransitService transitService = env.getTransitService(); + assertEquals(numRoutes + 1, transitService.getAllRoutes().size()); + FeedScopedId newRouteId = TransitModelForTest.id(newRouteRef); + Route newRoute = transitService.getRouteForId(newRouteId); + assertNotNull(newRoute); + assertEquals(1, transitService.getPatternsForRoute(newRoute).size()); + } + + @Test + void testAddJourneyMultipleTimes() { + var env = RealtimeTestEnvironment.siri(); + var updates = createValidAddedJourney(env).buildEstimatedTimetableDeliveries(); + + int numTrips = env.getTransitService().getAllTrips().size(); + var result1 = env.applyEstimatedTimetable(updates); + assertEquals(1, result1.successful()); + assertEquals(numTrips + 1, env.getTransitService().getAllTrips().size()); + var result2 = env.applyEstimatedTimetable(updates); + assertEquals(1, result2.successful()); + assertEquals(numTrips + 1, env.getTransitService().getAllTrips().size()); } @Test @@ -159,9 +220,7 @@ void testUpdateJourneyWithFramedVehicleJourneyRef() { var updates = updatedJourneyBuilder(env) .withFramedVehicleJourneyRef(builder -> - builder - .withServiceDate(RealtimeTestEnvironment.SERVICE_DATE) - .withVehicleJourneyRef(env.trip1.getId().getId()) + builder.withServiceDate(SERVICE_DATE).withVehicleJourneyRef(env.trip1.getId().getId()) ) .buildEstimatedTimetableDeliveries(); var result = env.applyEstimatedTimetable(updates); @@ -415,6 +474,17 @@ void testExtraUnknownStop() { assertFailure(UpdateError.UpdateErrorType.INVALID_STOP_SEQUENCE, result); } + private static SiriEtBuilder createValidAddedJourney(RealtimeTestEnvironment env) { + return new SiriEtBuilder(env.getDateTimeHelper()) + .withEstimatedVehicleJourneyCode("newJourney") + .withIsExtraJourney(true) + .withOperatorRef(env.operator1Id.getId()) + .withLineRef(env.route1Id.getId()) + .withRecordedCalls(builder -> builder.call(env.stopC1).departAimedActual("00:01", "00:02")) + .withEstimatedCalls(builder -> builder.call(env.stopD1).arriveAimedExpected("00:03", "00:04") + ); + } + private static SiriEtBuilder updatedJourneyBuilder(RealtimeTestEnvironment env) { return new SiriEtBuilder(env.getDateTimeHelper()) .withEstimatedCalls(builder -> 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 index bb258beb76f..796b2a204da 100644 --- 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 @@ -65,8 +65,9 @@ void realtimeStopLayer() { var transitModel = new TransitModel(new StopModel(), deduplicator); transitModel.initTimeZone(ZoneIds.HELSINKI); transitModel.index(); - var alertService = new TransitAlertServiceImpl(transitModel); var transitService = new DefaultTransitService(transitModel) { + final TransitAlertService alertService = new TransitAlertServiceImpl(transitModel); + @Override public TransitAlertService getTransitAlertService() { return alertService; diff --git a/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java b/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java index be5b30b38b9..cdbaa8f3d4c 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java +++ b/src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java @@ -33,7 +33,6 @@ import org.opentripplanner.transit.model.timetable.RealTimeState; import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; import org.opentripplanner.transit.model.timetable.Trip; -import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.TripTimesFactory; import org.opentripplanner.transit.service.TransitEditorService; @@ -173,6 +172,7 @@ Result build() { return UpdateError.result(tripId, NO_START_DATE); } + boolean isAddedRoute = false; Route route = entityResolver.resolveRoute(lineRef); if (route == null) { Agency agency = resolveAgency(); @@ -180,8 +180,8 @@ Result build() { return UpdateError.result(tripId, CANNOT_RESOLVE_AGENCY); } route = createRoute(agency); + isAddedRoute = true; LOG.info("Adding route {} to transitModel.", route); - transitService.addRoutes(route); } Trip trip = createTrip(route, calServiceId); @@ -265,18 +265,16 @@ Result build() { .withReplacementFor(replacedTrips) .build(); - // Adding trip to index necessary to include values in graphql-queries - // TODO - SIRI: should more data be added to index? - transitService.addTripForId(tripId, trip); - transitService.addPatternForTrip(trip, pattern); - transitService.addPatternsForRoute(route, pattern); - transitService.addTripOnServiceDateById(tripOnServiceDate.getId(), tripOnServiceDate); - transitService.addTripOnServiceDateForTripAndDay( - new TripIdAndServiceDate(tripId, serviceDate), - tripOnServiceDate + return Result.success( + new TripUpdate( + stopPattern, + updatedTripTimes, + serviceDate, + tripOnServiceDate, + pattern, + isAddedRoute + ) ); - - return Result.success(new TripUpdate(stopPattern, updatedTripTimes, serviceDate)); } /** diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriAlertsUpdateHandler.java b/src/ext/java/org/opentripplanner/ext/siri/SiriAlertsUpdateHandler.java index 8a747e765da..1a2d82df843 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriAlertsUpdateHandler.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriAlertsUpdateHandler.java @@ -20,9 +20,7 @@ import org.opentripplanner.routing.alertpatch.TransitAlertBuilder; import org.opentripplanner.routing.services.TransitAlertService; import org.opentripplanner.transit.model.framework.FeedScopedId; -import org.opentripplanner.transit.service.DefaultTransitService; -import org.opentripplanner.transit.service.TransitModel; -import org.opentripplanner.transit.service.TransitService; +import org.opentripplanner.updater.RealTimeUpdateContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.org.siri.siri20.DefaultedTextStructure; @@ -56,31 +54,20 @@ public class SiriAlertsUpdateHandler { private final TransitAlertService transitAlertService; private final Duration earlyStart; - /** - * This takes the parts of the SIRI SX message saying which transit entities are affected and - * maps them to the corresponding OTP internal model entity or entities. - */ - private final AffectsMapper affectsMapper; - /** * @param earlyStart display the alerts to users this long before their activePeriod begins */ public SiriAlertsUpdateHandler( String feedId, - TransitModel transitModel, TransitAlertService transitAlertService, - SiriFuzzyTripMatcher siriFuzzyTripMatcher, Duration earlyStart ) { this.feedId = feedId; this.transitAlertService = transitAlertService; this.earlyStart = earlyStart; - - TransitService transitService = new DefaultTransitService(transitModel); - this.affectsMapper = new AffectsMapper(feedId, siriFuzzyTripMatcher, transitService); } - public void update(ServiceDelivery delivery) { + public void update(ServiceDelivery delivery, RealTimeUpdateContext context) { for (SituationExchangeDeliveryStructure sxDelivery : delivery.getSituationExchangeDeliveries()) { SituationExchangeDeliveryStructure.Situations situations = sxDelivery.getSituations(); if (situations != null) { @@ -106,7 +93,7 @@ public void update(ServiceDelivery delivery) { } else { TransitAlert alert = null; try { - alert = mapSituationToAlert(sxElement); + alert = mapSituationToAlert(sxElement, context); addedCounter++; } catch (Exception e) { LOG.info( @@ -141,7 +128,10 @@ public void update(ServiceDelivery delivery) { * May return null if the header, description, and detail text are all empty or missing in the * SIRI message. In all other cases it will return a valid TransitAlert instance. */ - private TransitAlert mapSituationToAlert(PtSituationElement situation) { + private TransitAlert mapSituationToAlert( + PtSituationElement situation, + RealTimeUpdateContext context + ) { TransitAlertBuilder alert = createAlertWithTexts(situation); if ( @@ -194,7 +184,10 @@ private TransitAlert mapSituationToAlert(PtSituationElement situation) { alert.withPriority(situation.getPriority().intValue()); } - alert.addEntites(affectsMapper.mapAffects(situation.getAffects())); + alert.addEntites( + new AffectsMapper(feedId, context.siriFuzzyTripMatcher(), context.transitService()) + .mapAffects(situation.getAffects()) + ); if (alert.entities().isEmpty()) { LOG.info( diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriFuzzyTripMatcher.java b/src/ext/java/org/opentripplanner/ext/siri/SiriFuzzyTripMatcher.java index cc76ad3ac4f..ed5e9818b32 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriFuzzyTripMatcher.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriFuzzyTripMatcher.java @@ -45,29 +45,10 @@ public class SiriFuzzyTripMatcher { private static final Logger LOG = LoggerFactory.getLogger(SiriFuzzyTripMatcher.class); - private static SiriFuzzyTripMatcher instance; - private final Map> internalPlanningCodeCache = new HashMap<>(); private final Map> startStopTripCache = new HashMap<>(); private final TransitService transitService; - private boolean initialized = false; - - /** - * Factory method used to create only one instance. - *

- * THIS METHOD IS NOT THREAD-SAFE AND SHOULD BE CALLED DURING THE - * INITIALIZATION PROCESS. - */ - public static SiriFuzzyTripMatcher of(TransitService transitService) { - if (instance == null) { - instance = new SiriFuzzyTripMatcher(transitService); - } - return instance; - } - /** - * Constructor with public access for tests only. - */ public SiriFuzzyTripMatcher(TransitService transitService) { this.transitService = transitService; initCache(this.transitService); @@ -181,38 +162,34 @@ public List getTripIdForInternalPlanningCodeServiceDate( } private void initCache(TransitService index) { - if (!initialized) { - for (Trip trip : index.getAllTrips()) { - TripPattern tripPattern = index.getPatternForTrip(trip); + for (Trip trip : index.getAllTrips()) { + TripPattern tripPattern = index.getPatternForTrip(trip); - if (tripPattern == null) { - continue; - } + if (tripPattern == null) { + continue; + } - if (tripPattern.getRoute().getMode().equals(TransitMode.RAIL)) { - String internalPlanningCode = trip.getNetexInternalPlanningCode(); - if (internalPlanningCode != null) { - internalPlanningCodeCache - .computeIfAbsent(internalPlanningCode, key -> new HashSet<>()) - .add(trip); - } + if (tripPattern.getRoute().getMode().equals(TransitMode.RAIL)) { + String internalPlanningCode = trip.getNetexInternalPlanningCode(); + if (internalPlanningCode != null) { + internalPlanningCodeCache + .computeIfAbsent(internalPlanningCode, key -> new HashSet<>()) + .add(trip); } - String lastStopId = tripPattern.lastStop().getId().getId(); + } + String lastStopId = tripPattern.lastStop().getId().getId(); - TripTimes tripTimes = tripPattern.getScheduledTimetable().getTripTimes(trip); - if (tripTimes != null) { - int arrivalTime = tripTimes.getArrivalTime(tripTimes.getNumStops() - 1); + TripTimes tripTimes = tripPattern.getScheduledTimetable().getTripTimes(trip); + if (tripTimes != null) { + int arrivalTime = tripTimes.getArrivalTime(tripTimes.getNumStops() - 1); - String key = createStartStopKey(lastStopId, arrivalTime); - startStopTripCache.computeIfAbsent(key, k -> new HashSet<>()).add(trip); - } + String key = createStartStopKey(lastStopId, arrivalTime); + startStopTripCache.computeIfAbsent(key, k -> new HashSet<>()).add(trip); } - - LOG.info("Built internalPlanningCode-cache [{}].", internalPlanningCodeCache.size()); - LOG.info("Built start-stop-cache [{}].", startStopTripCache.size()); } - initialized = true; + LOG.info("Built internalPlanningCode-cache [{}].", internalPlanningCodeCache.size()); + LOG.info("Built start-stop-cache [{}].", startStopTripCache.size()); } private static String createStartStopKey(String lastStopId, int lastStopArrivalTime) { diff --git a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java index 9e2dfaff76b..7746df7d02f 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.List; import javax.annotation.Nullable; +import org.opentripplanner.model.RealTimeTripUpdate; import org.opentripplanner.model.Timetable; import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.model.TimetableSnapshotProvider; @@ -133,7 +134,13 @@ public TimetableSnapshot getTimetableSnapshot() { return snapshotManager.getTimetableSnapshot(); } - private TimetableSnapshot getTimetableSnapshotBuffer() { + /** + * @return the current timetable snapshot buffer that contains pending changes (not yet published + * in a snapshot). + * This should be used in the context of an updater to build a TransitEditorService that sees all + * the changes applied so far by real-time updates. + */ + public TimetableSnapshot getTimetableSnapshotBuffer() { return snapshotManager.getTimetableSnapshotBuffer(); } @@ -292,14 +299,25 @@ private Result addTripToGraphAndBuffer(TripUpdate tr Trip trip = tripUpdate.tripTimes().getTrip(); LocalDate serviceDate = tripUpdate.serviceDate(); - // Get cached trip pattern or create one if it doesn't exist yet - final TripPattern pattern = tripPatternCache.getOrCreateTripPattern( - tripUpdate.stopPattern(), - trip, - serviceDate - ); + final TripPattern pattern; + if (tripUpdate.tripPatternCreation()) { + pattern = tripUpdate.addedTripPattern(); + } else { + // Get cached trip pattern or create one if it doesn't exist yet + pattern = + tripPatternCache.getOrCreateTripPattern(tripUpdate.stopPattern(), trip, serviceDate); + } + // Add new trip times to buffer, making protective copies as needed. Bubble success/error up. - var result = snapshotManager.updateBuffer(pattern, tripUpdate.tripTimes(), serviceDate); + RealTimeTripUpdate realTimeTripUpdate = new RealTimeTripUpdate( + pattern, + tripUpdate.tripTimes(), + serviceDate, + tripUpdate.addedTripOnServiceDate(), + tripUpdate.tripCreation(), + tripUpdate.routeCreation() + ); + var result = snapshotManager.updateBuffer(realTimeTripUpdate); LOG.debug("Applied real-time data for trip {} on {}", trip, serviceDate); return result; } @@ -324,7 +342,7 @@ private boolean markScheduledTripAsDeleted(Trip trip, final LocalDate serviceDat } else { final RealTimeTripTimes newTripTimes = tripTimes.copyScheduledTimes(); newTripTimes.deleteTrip(); - snapshotManager.updateBuffer(pattern, newTripTimes, serviceDate); + snapshotManager.updateBuffer(new RealTimeTripUpdate(pattern, newTripTimes, serviceDate)); success = true; } } diff --git a/src/ext/java/org/opentripplanner/ext/siri/TripUpdate.java b/src/ext/java/org/opentripplanner/ext/siri/TripUpdate.java index 3c351f4596c..9874e9d0825 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/TripUpdate.java +++ b/src/ext/java/org/opentripplanner/ext/siri/TripUpdate.java @@ -1,12 +1,59 @@ package org.opentripplanner.ext.siri; import java.time.LocalDate; -import javax.annotation.Nonnull; +import java.util.Objects; +import javax.annotation.Nullable; import org.opentripplanner.transit.model.network.StopPattern; +import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.timetable.RealTimeTripTimes; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; +/** + * Represents the SIRI real-time update of a single trip. + * @param stopPattern the stop pattern to which belongs the updated trip. + * @param tripTimes the new trip times for the updated trip. + * @param serviceDate the service date for which this update applies (updates are valid only for one service date) + * @param addedTripOnServiceDate optionally if this trip update adds a new trip, the TripOnServiceDate corresponding to this new trip. + * @param addedTripPattern optionally if this trip update adds a new trip pattern , the new trip pattern to this new trip. + * @param routeCreation true if an added trip cannot be registered under an existing route and a new route must be created. + */ record TripUpdate( - @Nonnull StopPattern stopPattern, - @Nonnull TripTimes tripTimes, - @Nonnull LocalDate serviceDate -) {} + StopPattern stopPattern, + TripTimes tripTimes, + LocalDate serviceDate, + @Nullable TripOnServiceDate addedTripOnServiceDate, + @Nullable TripPattern addedTripPattern, + boolean routeCreation +) { + public TripUpdate { + Objects.requireNonNull(stopPattern); + Objects.requireNonNull(tripTimes); + Objects.requireNonNull(serviceDate); + } + + /** + * Create a trip update for an existing trip. + */ + public TripUpdate( + StopPattern stopPattern, + RealTimeTripTimes updatedTripTimes, + LocalDate serviceDate + ) { + this(stopPattern, updatedTripTimes, serviceDate, null, null, false); + } + + /** + * Return true if this trip update creates a new trip pattern. + */ + public boolean tripPatternCreation() { + return addedTripPattern != null; + } + + /** + * Return true if this trip update creates a new trip. + */ + public boolean tripCreation() { + return addedTripOnServiceDate != null; + } +} diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/AsyncEstimatedTimetableProcessor.java b/src/ext/java/org/opentripplanner/ext/siri/updater/AsyncEstimatedTimetableProcessor.java index 81643c476ab..db36936afd9 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/AsyncEstimatedTimetableProcessor.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/AsyncEstimatedTimetableProcessor.java @@ -33,11 +33,12 @@ public AsyncEstimatedTimetableProcessor( * @return a future indicating when the changes are applied. */ public Future processSiriData(ServiceDelivery serviceDelivery) { - return saveResultOnGraph.execute((graph, transitModel) -> + return saveResultOnGraph.execute(context -> updateResultConsumer.accept( estimatedTimetableHandler.applyUpdate( serviceDelivery.getEstimatedTimetableDeliveries(), - UpdateIncrementality.DIFFERENTIAL + UpdateIncrementality.DIFFERENTIAL, + context ) ) ); diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/EstimatedTimetableHandler.java b/src/ext/java/org/opentripplanner/ext/siri/updater/EstimatedTimetableHandler.java index 961d6a10282..aa2882d8fe2 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/EstimatedTimetableHandler.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/EstimatedTimetableHandler.java @@ -1,10 +1,8 @@ package org.opentripplanner.ext.siri.updater; import java.util.List; -import org.opentripplanner.ext.siri.EntityResolver; -import org.opentripplanner.ext.siri.SiriFuzzyTripMatcher; import org.opentripplanner.ext.siri.SiriTimetableSnapshotSource; -import org.opentripplanner.transit.service.TransitService; +import org.opentripplanner.updater.RealTimeUpdateContext; import org.opentripplanner.updater.spi.UpdateResult; import org.opentripplanner.updater.trip.UpdateIncrementality; import uk.org.siri.siri20.EstimatedTimetableDeliveryStructure; @@ -15,8 +13,7 @@ public class EstimatedTimetableHandler { private final SiriTimetableSnapshotSource snapshotSource; - private final SiriFuzzyTripMatcher fuzzyTripMatcher; - private final EntityResolver entityResolver; + private final boolean fuzzyTripMatching; /** * The ID for the static feed to which these real time updates are applied */ @@ -24,30 +21,11 @@ public class EstimatedTimetableHandler { public EstimatedTimetableHandler( SiriTimetableSnapshotSource snapshotSource, - boolean fuzzyMatching, - TransitService transitService, - String feedId - ) { - this( - snapshotSource, - fuzzyMatching ? SiriFuzzyTripMatcher.of(transitService) : null, - transitService, - feedId - ); - } - - /** - * Constructor for tests only. - */ - public EstimatedTimetableHandler( - SiriTimetableSnapshotSource snapshotSource, - SiriFuzzyTripMatcher siriFuzzyTripMatcher, - TransitService transitService, + boolean fuzzyTripMatching, String feedId ) { this.snapshotSource = snapshotSource; - this.fuzzyTripMatcher = siriFuzzyTripMatcher; - this.entityResolver = new EntityResolver(transitService, feedId); + this.fuzzyTripMatching = fuzzyTripMatching; this.feedId = feedId; } @@ -56,11 +34,12 @@ public EstimatedTimetableHandler( */ public UpdateResult applyUpdate( List estimatedTimetableDeliveries, - UpdateIncrementality updateMode + UpdateIncrementality updateMode, + RealTimeUpdateContext context ) { return snapshotSource.applyEstimatedTimetable( - fuzzyTripMatcher, - entityResolver, + fuzzyTripMatching ? context.siriFuzzyTripMatcher() : null, + context.entityResolver(feedId), feedId, updateMode, estimatedTimetableDeliveries diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETUpdater.java b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETUpdater.java index c811d3ee5d8..3b2ec5984d3 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETUpdater.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriETUpdater.java @@ -3,8 +3,6 @@ import java.util.List; import java.util.function.Consumer; import org.opentripplanner.ext.siri.SiriTimetableSnapshotSource; -import org.opentripplanner.transit.service.DefaultTransitService; -import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.updater.spi.PollingGraphUpdater; import org.opentripplanner.updater.spi.ResultLogger; import org.opentripplanner.updater.spi.UpdateResult; @@ -41,7 +39,6 @@ public class SiriETUpdater extends PollingGraphUpdater { public SiriETUpdater( SiriETUpdaterParameters config, - TransitModel transitModel, SiriTimetableSnapshotSource timetableSnapshotSource ) { super(config); @@ -59,12 +56,7 @@ public SiriETUpdater( ); estimatedTimetableHandler = - new EstimatedTimetableHandler( - timetableSnapshotSource, - config.fuzzyTripMatching(), - new DefaultTransitService(transitModel), - feedId - ); + new EstimatedTimetableHandler(timetableSnapshotSource, config.fuzzyTripMatching(), feedId); recordMetrics = TripUpdateMetrics.streaming(config); } @@ -92,8 +84,8 @@ public void runPolling() { final boolean markPrimed = !moreData; List etds = serviceDelivery.getEstimatedTimetableDeliveries(); if (etds != null) { - saveResultOnGraph.execute((graph, transitModel) -> { - var result = estimatedTimetableHandler.applyUpdate(etds, incrementality); + saveResultOnGraph.execute(context -> { + var result = estimatedTimetableHandler.applyUpdate(etds, incrementality, context); ResultLogger.logUpdateResult(feedId, "siri-et", result); recordMetrics.accept(result); if (markPrimed) { diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriSXUpdater.java b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriSXUpdater.java index 5ededbb3bf0..6ef7f504e85 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/SiriSXUpdater.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/SiriSXUpdater.java @@ -5,13 +5,11 @@ import java.util.Optional; import java.util.UUID; import org.opentripplanner.ext.siri.SiriAlertsUpdateHandler; -import org.opentripplanner.ext.siri.SiriFuzzyTripMatcher; import org.opentripplanner.framework.io.OtpHttpClientException; import org.opentripplanner.framework.retry.OtpRetry; import org.opentripplanner.framework.retry.OtpRetryBuilder; import org.opentripplanner.routing.impl.TransitAlertServiceImpl; import org.opentripplanner.routing.services.TransitAlertService; -import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.updater.alert.TransitAlertProvider; import org.opentripplanner.updater.spi.PollingGraphUpdater; @@ -60,13 +58,7 @@ public SiriSXUpdater(SiriSXUpdaterParameters config, TransitModel transitModel) this.blockReadinessUntilInitialized = config.blockReadinessUntilInitialized(); this.transitAlertService = new TransitAlertServiceImpl(transitModel); this.updateHandler = - new SiriAlertsUpdateHandler( - config.feedId(), - transitModel, - transitAlertService, - SiriFuzzyTripMatcher.of(new DefaultTransitService(transitModel)), - config.earlyStart() - ); + new SiriAlertsUpdateHandler(config.feedId(), transitAlertService, config.earlyStart()); siriHttpLoader = new SiriHttpLoader(url, config.timeout(), config.requestHeaders()); retry = @@ -149,8 +141,8 @@ private void updateSiri() { // All that said, out of all the update types, Alerts (and SIRI SX) are probably the ones // that would be most tolerant of non-versioned application-wide storage since they don't // participate in routing and are tacked on to already-completed routing responses. - writeToGraphCallback.execute((graph, transitModel) -> { - updateHandler.update(serviceDelivery); + writeToGraphCallback.execute(context -> { + updateHandler.update(serviceDelivery, context); if (markPrimed) { primed = true; } diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdater.java b/src/ext/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdater.java index a72c1797d38..7a1b32d36e5 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdater.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/azure/AbstractAzureSiriUpdater.java @@ -22,13 +22,8 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import org.apache.hc.client5.http.classic.methods.HttpGet; -import org.opentripplanner.ext.siri.EntityResolver; -import org.opentripplanner.ext.siri.SiriFuzzyTripMatcher; import org.opentripplanner.framework.application.ApplicationShutdownSupport; import org.opentripplanner.framework.io.OtpHttpClientFactory; -import org.opentripplanner.transit.service.DefaultTransitService; -import org.opentripplanner.transit.service.TransitModel; -import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.updater.spi.GraphUpdater; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.spi.WriteToGraphCallback; @@ -44,8 +39,7 @@ public abstract class AbstractAzureSiriUpdater implements GraphUpdater { private final String fullyQualifiedNamespace; private final String configRef; private final String serviceBusUrl; - private final SiriFuzzyTripMatcher fuzzyTripMatcher; - private final EntityResolver entityResolver; + private final boolean fuzzyTripMatching; private final Consumer messageConsumer = this::messageConsumer; private final Consumer errorConsumer = this::errorConsumer; private final String topicName; @@ -69,7 +63,7 @@ public abstract class AbstractAzureSiriUpdater implements GraphUpdater { */ protected final int timeout; - public AbstractAzureSiriUpdater(SiriAzureUpdaterParameters config, TransitModel transitModel) { + public AbstractAzureSiriUpdater(SiriAzureUpdaterParameters config) { this.configRef = config.configRef(); this.authenticationType = config.getAuthenticationType(); this.fullyQualifiedNamespace = config.getFullyQualifiedNamespace(); @@ -80,10 +74,7 @@ public AbstractAzureSiriUpdater(SiriAzureUpdaterParameters config, TransitModel this.feedId = config.feedId(); this.autoDeleteOnIdle = config.getAutoDeleteOnIdle(); this.prefetchCount = config.getPrefetchCount(); - TransitService transitService = new DefaultTransitService(transitModel); - this.entityResolver = new EntityResolver(transitService, feedId); - this.fuzzyTripMatcher = - config.isFuzzyTripMatching() ? SiriFuzzyTripMatcher.of(transitService) : null; + this.fuzzyTripMatching = config.isFuzzyTripMatching(); } /** @@ -226,12 +217,8 @@ protected Optional fetchInitialSiriData(URI uri) { } } - SiriFuzzyTripMatcher fuzzyTripMatcher() { - return fuzzyTripMatcher; - } - - EntityResolver entityResolver() { - return entityResolver; + boolean fuzzyTripMatching() { + return fuzzyTripMatching; } /** diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/azure/SiriAzureETUpdater.java b/src/ext/java/org/opentripplanner/ext/siri/updater/azure/SiriAzureETUpdater.java index 2b8c48dce11..009bc3a7f0e 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/azure/SiriAzureETUpdater.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/azure/SiriAzureETUpdater.java @@ -16,7 +16,6 @@ import javax.xml.stream.XMLStreamException; import org.apache.hc.core5.net.URIBuilder; import org.opentripplanner.ext.siri.SiriTimetableSnapshotSource; -import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.updater.spi.ResultLogger; import org.opentripplanner.updater.spi.UpdateResult; import org.opentripplanner.updater.trip.UpdateIncrementality; @@ -40,10 +39,9 @@ public class SiriAzureETUpdater extends AbstractAzureSiriUpdater { public SiriAzureETUpdater( SiriAzureETUpdaterParameters config, - TransitModel transitModel, SiriTimetableSnapshotSource snapshotSource ) { - super(config, transitModel); + super(config); this.fromDateTime = config.getFromDateTime(); this.snapshotSource = snapshotSource; this.recordMetrics = TripUpdateMetrics.streaming(config); @@ -98,10 +96,10 @@ protected void initializeData(String url, Consumer processMessage(List updates) { - return super.saveResultOnGraph.execute((graph, transitModel) -> { + return super.saveResultOnGraph.execute(context -> { var result = snapshotSource.applyEstimatedTimetable( - fuzzyTripMatcher(), - entityResolver(), + fuzzyTripMatching() ? context.siriFuzzyTripMatcher() : null, + context.entityResolver(feedId), feedId, UpdateIncrementality.DIFFERENTIAL, updates diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/azure/SiriAzureSXUpdater.java b/src/ext/java/org/opentripplanner/ext/siri/updater/azure/SiriAzureSXUpdater.java index b04149a44bf..bcd2c500c40 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/azure/SiriAzureSXUpdater.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/azure/SiriAzureSXUpdater.java @@ -36,18 +36,11 @@ public class SiriAzureSXUpdater extends AbstractAzureSiriUpdater implements Tran private final LocalDate toDateTime; public SiriAzureSXUpdater(SiriAzureSXUpdaterParameters config, TransitModel transitModel) { - super(config, transitModel); + super(config); this.fromDateTime = config.getFromDateTime(); this.toDateTime = config.getToDateTime(); this.transitAlertService = new TransitAlertServiceImpl(transitModel); - this.updateHandler = - new SiriAlertsUpdateHandler( - feedId, - transitModel, - transitAlertService, - fuzzyTripMatcher(), - Duration.ZERO - ); + this.updateHandler = new SiriAlertsUpdateHandler(feedId, transitAlertService, Duration.ZERO); } @Override @@ -123,7 +116,7 @@ private Optional parseSiriSx(String xmlMessage, String id) } private Future processMessage(ServiceDelivery siriSx) { - return super.saveResultOnGraph.execute((graph, transitModel) -> updateHandler.update(siriSx)); + return super.saveResultOnGraph.execute(context -> updateHandler.update(siriSx, context)); } private void processHistory(ServiceDelivery siri) { diff --git a/src/ext/java/org/opentripplanner/ext/siri/updater/google/SiriETGooglePubsubUpdater.java b/src/ext/java/org/opentripplanner/ext/siri/updater/google/SiriETGooglePubsubUpdater.java index 58c01815230..7680e535bfa 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/updater/google/SiriETGooglePubsubUpdater.java +++ b/src/ext/java/org/opentripplanner/ext/siri/updater/google/SiriETGooglePubsubUpdater.java @@ -5,8 +5,6 @@ import org.opentripplanner.ext.siri.updater.AsyncEstimatedTimetableProcessor; import org.opentripplanner.ext.siri.updater.AsyncEstimatedTimetableSource; import org.opentripplanner.ext.siri.updater.EstimatedTimetableHandler; -import org.opentripplanner.transit.service.DefaultTransitService; -import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.updater.spi.GraphUpdater; import org.opentripplanner.updater.spi.UpdateResult; import org.opentripplanner.updater.spi.WriteToGraphCallback; @@ -27,7 +25,6 @@ public class SiriETGooglePubsubUpdater implements GraphUpdater { public SiriETGooglePubsubUpdater( SiriETGooglePubsubUpdaterParameters config, - TransitModel transitModel, SiriTimetableSnapshotSource timetableSnapshotSource ) { configRef = config.configRef(); @@ -46,7 +43,6 @@ public SiriETGooglePubsubUpdater( new EstimatedTimetableHandler( timetableSnapshotSource, config.fuzzyTripMatching(), - new DefaultTransitService(transitModel), config.feedId() ); diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/TripTimeShortHelper.java b/src/main/java/org/opentripplanner/apis/transmodel/model/TripTimeOnDateHelper.java similarity index 91% rename from src/main/java/org/opentripplanner/apis/transmodel/model/TripTimeShortHelper.java rename to src/main/java/org/opentripplanner/apis/transmodel/model/TripTimeOnDateHelper.java index 2f2b38785bf..67e23adec5e 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/TripTimeShortHelper.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/TripTimeOnDateHelper.java @@ -12,16 +12,16 @@ import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.TripTimes; -public class TripTimeShortHelper { +public class TripTimeOnDateHelper { /** Utility class with private constructor to prevent instantiation. */ - private TripTimeShortHelper() {} + private TripTimeOnDateHelper() {} /** * Find trip time short for the from place in transit leg, or null. */ @Nullable - public static TripTimeOnDate getTripTimeShortForFromPlace(Leg leg) { + public static TripTimeOnDate getTripTimeOnDateForFromPlace(Leg leg) { if (!leg.isScheduledTransitLeg()) { return null; } @@ -49,7 +49,7 @@ public static TripTimeOnDate getTripTimeShortForFromPlace(Leg leg) { * Find trip time short for the to place in transit leg, or null. */ @Nullable - public static TripTimeOnDate getTripTimeShortForToPlace(Leg leg) { + public static TripTimeOnDate getTripTimeOnDateForToPlace(Leg leg) { if (!leg.isScheduledTransitLeg()) { return null; } @@ -77,7 +77,7 @@ public static TripTimeOnDate getTripTimeShortForToPlace(Leg leg) { /** * Find trip time shorts for all stops for the full trip of a leg. */ - public static List getAllTripTimeShortsForLegsTrip(Leg leg) { + public static List getAllTripTimeOnDatesForLegsTrip(Leg leg) { if (!leg.isScheduledTransitLeg()) { return List.of(); } @@ -96,7 +96,7 @@ public static List getAllTripTimeShortsForLegsTrip(Leg leg) { /** * Find trip time shorts for all intermediate stops for a leg. */ - public static List getIntermediateTripTimeShortsForLeg(Leg leg) { + public static List getIntermediateTripTimeOnDatesForLeg(Leg leg) { if (!leg.isScheduledTransitLeg()) { return List.of(); } diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/plan/JourneyWhiteListed.java b/src/main/java/org/opentripplanner/apis/transmodel/model/plan/JourneyWhiteListed.java index 6d747ccb164..75c1fe918dd 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/plan/JourneyWhiteListed.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/plan/JourneyWhiteListed.java @@ -60,10 +60,10 @@ public static Stream whiteListAuthoritiesAndOrLines( if (authorityIds.isEmpty() && lineIds.isEmpty()) { return stream; } - return stream.filter(it -> isTripTimeShortAcceptable(it, authorityIds, lineIds)); + return stream.filter(it -> isTripTimeOnDateAcceptable(it, authorityIds, lineIds)); } - private static boolean isTripTimeShortAcceptable( + private static boolean isTripTimeOnDateAcceptable( TripTimeOnDate tts, Collection authorityIds, Collection lineIds diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java b/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java index be05d00e16d..867d08e3933 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/plan/LegType.java @@ -21,7 +21,7 @@ import java.util.stream.Collectors; import org.opentripplanner.apis.transmodel.model.EnumTypes; import org.opentripplanner.apis.transmodel.model.TransmodelTransportSubmode; -import org.opentripplanner.apis.transmodel.model.TripTimeShortHelper; +import org.opentripplanner.apis.transmodel.model.TripTimeOnDateHelper; import org.opentripplanner.apis.transmodel.support.GqlUtil; import org.opentripplanner.framework.geometry.EncodedPolyline; import org.opentripplanner.model.plan.Leg; @@ -271,7 +271,7 @@ public static GraphQLObjectType create( .withDirective(gqlUtil.timingData) .description("EstimatedCall for the quay where the leg originates.") .type(estimatedCallType) - .dataFetcher(env -> TripTimeShortHelper.getTripTimeShortForFromPlace(env.getSource())) + .dataFetcher(env -> TripTimeOnDateHelper.getTripTimeOnDateForFromPlace(env.getSource())) .build() ) .field( @@ -281,7 +281,7 @@ public static GraphQLObjectType create( .withDirective(gqlUtil.timingData) .description("EstimatedCall for the quay where the leg ends.") .type(estimatedCallType) - .dataFetcher(env -> TripTimeShortHelper.getTripTimeShortForToPlace(env.getSource())) + .dataFetcher(env -> TripTimeOnDateHelper.getTripTimeOnDateForToPlace(env.getSource())) .build() ) .field( @@ -358,7 +358,7 @@ public static GraphQLObjectType create( ) .type(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(estimatedCallType)))) .dataFetcher(env -> - TripTimeShortHelper.getIntermediateTripTimeShortsForLeg(env.getSource()) + TripTimeOnDateHelper.getIntermediateTripTimeOnDatesForLeg(env.getSource()) ) .build() ) @@ -371,7 +371,8 @@ public static GraphQLObjectType create( "For ride legs, all estimated calls for the service journey. For non-ride legs, empty list." ) .type(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(estimatedCallType)))) - .dataFetcher(env -> TripTimeShortHelper.getAllTripTimeShortsForLegsTrip(env.getSource())) + .dataFetcher(env -> TripTimeOnDateHelper.getAllTripTimeOnDatesForLegsTrip(env.getSource()) + ) .build() ) // .field(GraphQLFieldDefinition.newFieldDefinition() diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/siri/et/EstimatedCallType.java b/src/main/java/org/opentripplanner/apis/transmodel/model/siri/et/EstimatedCallType.java index 0c47fe36ba8..3587b3dc0f6 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/siri/et/EstimatedCallType.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/siri/et/EstimatedCallType.java @@ -355,7 +355,7 @@ public static GraphQLObjectType create( } /** - * Resolves all AlertPatches that are relevant for the supplied TripTimeShort. + * Resolves all AlertPatches that are relevant for the supplied TripTimeOnDate. */ private static Collection getAllRelevantAlerts( TripTimeOnDate tripTimeOnDate, diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/DatedServiceJourneyType.java b/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/DatedServiceJourneyType.java index ed06996301e..faeccf930c6 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/DatedServiceJourneyType.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/DatedServiceJourneyType.java @@ -15,7 +15,7 @@ import java.util.Optional; import org.opentripplanner.apis.transmodel.model.EnumTypes; import org.opentripplanner.apis.transmodel.support.GqlUtil; -import org.opentripplanner.routing.TripTimesShortHelper; +import org.opentripplanner.routing.TripTimeOnDateHelper; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.timetable.TripOnServiceDate; @@ -149,7 +149,7 @@ public static GraphQLObjectType create( ) .dataFetcher(environment -> { TripOnServiceDate tripOnServiceDate = tripOnServiceDate(environment); - return TripTimesShortHelper.getTripTimesShort( + return TripTimeOnDateHelper.getTripTimeOnDates( GqlUtil.getTransitService(environment), tripOnServiceDate.getTrip(), tripOnServiceDate.getServiceDate() diff --git a/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/ServiceJourneyType.java b/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/ServiceJourneyType.java index 0122cdbca5d..c0c96b1e393 100644 --- a/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/ServiceJourneyType.java +++ b/src/main/java/org/opentripplanner/apis/transmodel/model/timetable/ServiceJourneyType.java @@ -20,7 +20,7 @@ import org.opentripplanner.apis.transmodel.support.GqlUtil; import org.opentripplanner.framework.geometry.EncodedPolyline; import org.opentripplanner.model.TripTimeOnDate; -import org.opentripplanner.routing.TripTimesShortHelper; +import org.opentripplanner.routing.TripTimeOnDateHelper; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.model.timetable.Trip; @@ -273,7 +273,7 @@ public static GraphQLObjectType create( .ofNullable(environment.getArgument("date")) .map(LocalDate.class::cast) .orElse(LocalDate.now(GqlUtil.getTransitService(environment).getTimeZone())); - return TripTimesShortHelper.getTripTimesShort( + return TripTimeOnDateHelper.getTripTimeOnDates( GqlUtil.getTransitService(environment), trip(environment), serviceDate diff --git a/src/main/java/org/opentripplanner/framework/application/OTPFeature.java b/src/main/java/org/opentripplanner/framework/application/OTPFeature.java index 749772fae12..29c2a452448 100644 --- a/src/main/java/org/opentripplanner/framework/application/OTPFeature.java +++ b/src/main/java/org/opentripplanner/framework/application/OTPFeature.java @@ -31,6 +31,11 @@ public enum OTPFeature { Use the [vector tiles feature if](sandbox/MapboxVectorTilesApi.md) you want a stable map tiles API. """ ), + ExtraTransferLegOnSameStop( + false, + false, + "Should there be a transfer leg when transferring on the very same stop. Note that for in-seat/interlined transfers no transfer leg will be generated." + ), FloatingBike(true, false, "Enable floating bike routing."), GtfsGraphQlApi(true, false, "Enable the [GTFS GraphQL API](apis/GTFS-GraphQL-API.md)."), GtfsGraphQlApiRentalStationFuzzyMatching( diff --git a/src/main/java/org/opentripplanner/framework/collection/CollectionUtils.java b/src/main/java/org/opentripplanner/framework/collection/CollectionUtils.java index b34db13e270..1e86f49770f 100644 --- a/src/main/java/org/opentripplanner/framework/collection/CollectionUtils.java +++ b/src/main/java/org/opentripplanner/framework/collection/CollectionUtils.java @@ -1,6 +1,7 @@ package org.opentripplanner.framework.collection; import java.util.Collection; +import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.stream.Collectors; @@ -45,4 +46,18 @@ public static String toString(@Nullable Collection c, String nullText) { public static boolean isEmpty(@Nullable Collection c) { return c == null || c.isEmpty(); } + + /** + * Look up the given key in a Map, return null if the key is null. + * This prevents a NullPointerException if the underlying implementation of the map does not + * accept querying with null keys (e.g. ImmutableMap). + * + **/ + @Nullable + public static V getByNullableKey(K key, Map map) { + if (key == null) { + return null; + } + return map.get(key); + } } diff --git a/src/main/java/org/opentripplanner/framework/io/OtpHttpClient.java b/src/main/java/org/opentripplanner/framework/io/OtpHttpClient.java index 678eb4754bd..ebfeb3ff496 100644 --- a/src/main/java/org/opentripplanner/framework/io/OtpHttpClient.java +++ b/src/main/java/org/opentripplanner/framework/io/OtpHttpClient.java @@ -18,6 +18,7 @@ import java.util.Optional; import java.util.stream.Collectors; import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.classic.methods.HttpHead; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; import org.apache.hc.client5.http.config.RequestConfig; @@ -76,7 +77,7 @@ public List

getHeaders( Map requestHeaderValues ) { return executeAndMapWithResponseHandler( - new HttpGet(uri), + new HttpHead(uri), timeout, requestHeaderValues, response -> { @@ -90,9 +91,6 @@ public List
getHeaders( return Collections.emptyList(); } - if (response.getEntity() == null || response.getEntity().getContent() == null) { - throw new OtpHttpClientException("HTTP request failed: empty response"); - } return Arrays.stream(response.getHeaders()).toList(); } ); diff --git a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmDatabase.java b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmDatabase.java index df40e2cb647..60525b3cb6c 100644 --- a/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmDatabase.java +++ b/src/main/java/org/opentripplanner/graph_builder/module/osm/OsmDatabase.java @@ -651,10 +651,12 @@ private void applyLevelsForWay(OSMWithTags way) { OSMLevel level = OSMLevel.DEFAULT; if (way.hasTag("level")) { // TODO: floating-point levels &c. levelName = way.getTag("level"); - level = OSMLevel.fromString(levelName, OSMLevel.Source.LEVEL_TAG, noZeroLevels, issueStore); + level = + OSMLevel.fromString(levelName, OSMLevel.Source.LEVEL_TAG, noZeroLevels, issueStore, way); } else if (way.hasTag("layer")) { levelName = way.getTag("layer"); - level = OSMLevel.fromString(levelName, OSMLevel.Source.LAYER_TAG, noZeroLevels, issueStore); + level = + OSMLevel.fromString(levelName, OSMLevel.Source.LAYER_TAG, noZeroLevels, issueStore, way); } if (level == null || (!level.reliable)) { issueStore.add(new LevelAmbiguous(levelName, way)); @@ -980,7 +982,8 @@ private void processLevelMap(OSMRelation relation) { levelsTag, Source.LEVEL_MAP, true, - issueStore + issueStore, + relation ); for (OSMRelationMember member : relation.getMembers()) { if (member.hasTypeWay() && waysById.containsKey(member.getRef())) { diff --git a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java index 3a62ba2b269..593c73f58d8 100644 --- a/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java +++ b/src/main/java/org/opentripplanner/gtfs/mapping/TripMapper.java @@ -81,7 +81,7 @@ private Optional mapSafeTimePenalty(org.onebusaway.gtfs.model.Trip if (rhs.getSafeDurationFactor() == null && rhs.getSafeDurationOffset() == null) { return Optional.empty(); } else { - var offset = Duration.ofSeconds(rhs.getSafeDurationOffset().longValue()); + var offset = Duration.ofMinutes(rhs.getSafeDurationOffset().longValue()); return Optional.of(TimePenalty.of(offset, rhs.getSafeDurationFactor().doubleValue())); } } diff --git a/src/main/java/org/opentripplanner/model/RealTimeTripUpdate.java b/src/main/java/org/opentripplanner/model/RealTimeTripUpdate.java new file mode 100644 index 00000000000..e5bcc6c0322 --- /dev/null +++ b/src/main/java/org/opentripplanner/model/RealTimeTripUpdate.java @@ -0,0 +1,43 @@ +package org.opentripplanner.model; + +import java.time.LocalDate; +import java.util.Objects; +import javax.annotation.Nullable; +import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; +import org.opentripplanner.transit.model.timetable.TripTimes; + +/** + * Represents the real-time update of a single trip. + * @param pattern the pattern to which belongs the updated trip. This can be a new pattern created in real-time. + * @param updatedTripTimes the new trip times for the updated trip. + * @param serviceDate the service date for which this update applies (updates are valid only for one service date) + * @param addedTripOnServiceDate optionally if this trip update adds a new trip, the TripOnServiceDate corresponding to this new trip. + * @param tripCreation true if this update creates a new trip, not present in scheduled data. + * @param routeCreation true if an added trip cannot be registered under an existing route and a new route must be created. + */ +public record RealTimeTripUpdate( + TripPattern pattern, + TripTimes updatedTripTimes, + LocalDate serviceDate, + @Nullable TripOnServiceDate addedTripOnServiceDate, + boolean tripCreation, + boolean routeCreation +) { + public RealTimeTripUpdate { + Objects.requireNonNull(pattern); + Objects.requireNonNull(updatedTripTimes); + Objects.requireNonNull(serviceDate); + } + + /** + * Create a real-time update for an existing trip. + */ + public RealTimeTripUpdate( + TripPattern pattern, + TripTimes updatedTripTimes, + LocalDate serviceDate + ) { + this(pattern, updatedTripTimes, serviceDate, null, false, false); + } +} diff --git a/src/main/java/org/opentripplanner/model/StopTimesInPattern.java b/src/main/java/org/opentripplanner/model/StopTimesInPattern.java index c75295d288d..159bba5b6e5 100644 --- a/src/main/java/org/opentripplanner/model/StopTimesInPattern.java +++ b/src/main/java/org/opentripplanner/model/StopTimesInPattern.java @@ -5,7 +5,7 @@ import org.opentripplanner.transit.model.network.TripPattern; /** - * Some stopTimes all in the same pattern. TripTimeShort should probably be renamed StopTimeShort + * Some stopTimes all in the same pattern. */ public class StopTimesInPattern { diff --git a/src/main/java/org/opentripplanner/model/TimetableSnapshot.java b/src/main/java/org/opentripplanner/model/TimetableSnapshot.java index c0a7737abce..d076bf9f1f0 100644 --- a/src/main/java/org/opentripplanner/model/TimetableSnapshot.java +++ b/src/main/java/org/opentripplanner/model/TimetableSnapshot.java @@ -1,11 +1,15 @@ package org.opentripplanner.model; +import static org.opentripplanner.framework.collection.CollectionUtils.getByNullableKey; + import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.Multimap; import com.google.common.collect.SetMultimap; import java.time.LocalDate; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.ConcurrentModificationException; import java.util.HashMap; @@ -13,7 +17,6 @@ import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; -import java.util.Objects; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; @@ -21,9 +24,12 @@ import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.TransitLayerUpdater; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.framework.Result; +import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.StopLocation; +import org.opentripplanner.transit.model.timetable.Trip; import org.opentripplanner.transit.model.timetable.TripIdAndServiceDate; +import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.updater.spi.UpdateError; import org.opentripplanner.updater.spi.UpdateSuccess; @@ -118,6 +124,13 @@ public class TimetableSnapshot { */ private final SetMultimap patternsForStop; + private final Map realtimeAddedRoutes; + private final Map realTimeAddedTrips; + private final Map realTimeAddedPatternForTrip; + private final Multimap realTimeAddedPatternsForRoute; + private final Map realTimeAddedTripOnServiceDateById; + private final Map realTimeAddedTripOnServiceDateForTripAndDay; + /** * Boolean value indicating that timetable snapshot is read only if true. Once it is true, it * shouldn't be possible to change it to false anymore. @@ -131,17 +144,40 @@ public class TimetableSnapshot { private boolean dirty = false; public TimetableSnapshot() { - this(new HashMap<>(), new HashMap<>(), HashMultimap.create(), false); + this( + new HashMap<>(), + new HashMap<>(), + new HashMap<>(), + new HashMap<>(), + new HashMap<>(), + HashMultimap.create(), + new HashMap<>(), + new HashMap<>(), + HashMultimap.create(), + false + ); } private TimetableSnapshot( Map> timetables, Map realtimeAddedTripPattern, + Map realtimeAddedRoutes, + Map realtimeAddedTrips, + Map realTimeAddedPatternForTrip, + Multimap realTimeAddedPatternsForRoute, + Map realTimeAddedTripOnServiceDateById, + Map realTimeAddedTripOnServiceDateForTripAndDay, SetMultimap patternsForStop, boolean readOnly ) { this.timetables = timetables; this.realtimeAddedTripPattern = realtimeAddedTripPattern; + this.realtimeAddedRoutes = realtimeAddedRoutes; + this.realTimeAddedTrips = realtimeAddedTrips; + this.realTimeAddedPatternForTrip = realTimeAddedPatternForTrip; + this.realTimeAddedPatternsForRoute = realTimeAddedPatternsForRoute; + this.realTimeAddedTripOnServiceDateById = realTimeAddedTripOnServiceDateById; + this.realTimeAddedTripOnServiceDateForTripAndDay = realTimeAddedTripOnServiceDateForTripAndDay; this.patternsForStop = patternsForStop; this.readOnly = readOnly; } @@ -183,23 +219,79 @@ public boolean hasRealtimeAddedTripPatterns() { return !realtimeAddedTripPattern.isEmpty(); } + /** + * Return the route created by the updater for the given id. + */ + @Nullable + public Route getRealtimeAddedRoute(FeedScopedId id) { + return getByNullableKey(id, realtimeAddedRoutes); + } + + public Collection getAllRealTimeAddedRoutes() { + return Collections.unmodifiableCollection(realtimeAddedRoutes.values()); + } + + /** + * Return the trip created by the updater for the given id. + */ + @Nullable + public Trip getRealTimeAddedTrip(FeedScopedId id) { + return getByNullableKey(id, realTimeAddedTrips); + } + + public Collection getAllRealTimeAddedTrips() { + return Collections.unmodifiableCollection(realTimeAddedTrips.values()); + } + + /** + * Return the trip pattern created by the updater for the given trip. + */ + @Nullable + public TripPattern getRealTimeAddedPatternForTrip(Trip trip) { + return getByNullableKey(trip, realTimeAddedPatternForTrip); + } + + /** + * Return the trip patterns created by the updater for the given route. + */ + public Collection getRealTimeAddedPatternForRoute(Route route) { + return realTimeAddedPatternsForRoute.get(route); + } + + /** + * Return the trip on service date created by the updater for the given id. + */ + @Nullable + public TripOnServiceDate getRealTimeAddedTripOnServiceDateById(FeedScopedId id) { + return getByNullableKey(id, realTimeAddedTripOnServiceDateById); + } + + /** + * Return the trip on service date created by the updater for the given trip and service date. + */ + @Nullable + public TripOnServiceDate getRealTimeAddedTripOnServiceDateForTripAndDay( + TripIdAndServiceDate tripIdAndServiceDate + ) { + return getByNullableKey(tripIdAndServiceDate, realTimeAddedTripOnServiceDateForTripAndDay); + } + + public Collection getAllRealTimeAddedTripOnServiceDate() { + return Collections.unmodifiableCollection(realTimeAddedTripOnServiceDateForTripAndDay.values()); + } + /** * Update the TripTimes of one Trip in a Timetable of a TripPattern. If the Trip of the TripTimes - * does not exist yet in the Timetable, add it. This method will make a protective copy - * of the Timetable if such a copy has not already been made while building up this snapshot, - * handling both cases where patterns were pre-existing in static data or created by realtime data. + * does not exist yet in the Timetable, add it. This method will make a protective copy of the + * Timetable if such a copy has not already been made while building up this snapshot, handling + * both cases where patterns were pre-existing in static data or created by realtime data. * - * @param serviceDate service day for which this update is valid * @return whether the update was actually applied */ - public Result update( - TripPattern pattern, - TripTimes updatedTripTimes, - LocalDate serviceDate - ) { - // Preconditions - Objects.requireNonNull(pattern); - Objects.requireNonNull(serviceDate); + public Result update(RealTimeTripUpdate realTimeTripUpdate) { + TripPattern pattern = realTimeTripUpdate.pattern(); + LocalDate serviceDate = realTimeTripUpdate.serviceDate(); + TripTimes updatedTripTimes = realTimeTripUpdate.updatedTripTimes(); if (readOnly) { throw new ConcurrentModificationException("This TimetableSnapshot is read-only."); @@ -212,7 +304,8 @@ public Result update( // Assume all trips in a pattern are from the same feed, which should be the case. // Find trip index - int tripIndex = tt.getTripIndex(updatedTripTimes.getTrip().getId()); + Trip trip = updatedTripTimes.getTrip(); + int tripIndex = tt.getTripIndex(trip.getId()); if (tripIndex == -1) { // Trip not found, add it tt.addTripTimes(updatedTripTimes); @@ -223,7 +316,7 @@ public Result update( if (pattern.isCreatedByRealtimeUpdater()) { // Remember this pattern for the added trip id and service date - FeedScopedId tripId = updatedTripTimes.getTrip().getId(); + FeedScopedId tripId = trip.getId(); TripIdAndServiceDate tripIdAndServiceDate = new TripIdAndServiceDate(tripId, serviceDate); realtimeAddedTripPattern.put(tripIdAndServiceDate, pattern); } @@ -231,6 +324,27 @@ public Result update( // To make these trip patterns visible for departureRow searches. addPatternToIndex(pattern); + Route route = trip.getRoute(); + + if (realTimeTripUpdate.routeCreation()) { + realtimeAddedRoutes.put(route.getId(), route); + } + if (realTimeTripUpdate.tripCreation()) { + FeedScopedId tripId = trip.getId(); + realTimeAddedTrips.put(tripId, trip); + realTimeAddedPatternForTrip.put(trip, pattern); + realTimeAddedPatternsForRoute.put(route, pattern); + TripOnServiceDate tripOnServiceDate = realTimeTripUpdate.addedTripOnServiceDate(); + + if (tripOnServiceDate != null) { + realTimeAddedTripOnServiceDateById.put(tripOnServiceDate.getId(), tripOnServiceDate); + realTimeAddedTripOnServiceDateForTripAndDay.put( + new TripIdAndServiceDate(tripId, serviceDate), + tripOnServiceDate + ); + } + } + // The time tables are finished during the commit return Result.success(UpdateSuccess.noWarnings()); @@ -262,6 +376,12 @@ public TimetableSnapshot commit(TransitLayerUpdater transitLayerUpdater, boolean TimetableSnapshot ret = new TimetableSnapshot( Map.copyOf(timetables), Map.copyOf(realtimeAddedTripPattern), + Map.copyOf(realtimeAddedRoutes), + Map.copyOf(realTimeAddedTrips), + Map.copyOf(realTimeAddedPatternForTrip), + ImmutableSetMultimap.copyOf(realTimeAddedPatternsForRoute), + Map.copyOf(realTimeAddedTripOnServiceDateById), + Map.copyOf(realTimeAddedTripOnServiceDateForTripAndDay), ImmutableSetMultimap.copyOf(patternsForStop), true ); diff --git a/src/main/java/org/opentripplanner/model/TripTimeOnDate.java b/src/main/java/org/opentripplanner/model/TripTimeOnDate.java index 1bfb0184138..71aaa734f0e 100644 --- a/src/main/java/org/opentripplanner/model/TripTimeOnDate.java +++ b/src/main/java/org/opentripplanner/model/TripTimeOnDate.java @@ -220,6 +220,7 @@ public I18NString getHeadsign() { return tripTimes.getHeadsign(stopIndex); } + /** @return a list of via names visible at this stop, or an empty list if there are no vias. */ public List getHeadsignVias() { return tripTimes.getHeadsignVias(stopIndex); } diff --git a/src/main/java/org/opentripplanner/netex/validation/JourneyPatternSJMismatch.java b/src/main/java/org/opentripplanner/netex/validation/JourneyPatternSJMismatch.java index 10c990a31d0..28fc710a6ae 100644 --- a/src/main/java/org/opentripplanner/netex/validation/JourneyPatternSJMismatch.java +++ b/src/main/java/org/opentripplanner/netex/validation/JourneyPatternSJMismatch.java @@ -1,9 +1,19 @@ package org.opentripplanner.netex.validation; +import java.util.function.Predicate; import org.opentripplanner.graph_builder.issue.api.DataImportIssue; import org.rutebanken.netex.model.JourneyPattern_VersionStructure; +import org.rutebanken.netex.model.PointInLinkSequence_VersionedChildStructure; import org.rutebanken.netex.model.ServiceJourney; +import org.rutebanken.netex.model.StopPointInJourneyPattern; +import org.rutebanken.netex.model.StopUseEnumeration; +/** + * Validates that the number of passing times in the journey and the number of stop points in the + * pattern are equal. + * It also takes into account that some points in the pattern can be set to stopUse=passthrough + * which means that those must not be referenced in the journey. + */ class JourneyPatternSJMismatch extends AbstractHMapValidationRule { @Override @@ -12,16 +22,29 @@ public Status validate(ServiceJourney sj) { .getJourneyPatternsById() .lookup(getPatternId(sj)); - int nStopPointsInJourneyPattern = journeyPattern + int nStopPointsInJourneyPattern = (int) journeyPattern .getPointsInSequence() .getPointInJourneyPatternOrStopPointInJourneyPatternOrTimingPointInJourneyPattern() - .size(); + .stream() + .filter(Predicate.not(JourneyPatternSJMismatch::isPassThrough)) + .count(); int nTimetablePassingTimes = sj.getPassingTimes().getTimetabledPassingTime().size(); return nStopPointsInJourneyPattern != nTimetablePassingTimes ? Status.DISCARD : Status.OK; } + /** + * Does the stop point in the sequence represent a stop where the vehicle passes through without + * stopping? + */ + private static boolean isPassThrough(PointInLinkSequence_VersionedChildStructure point) { + return ( + point instanceof StopPointInJourneyPattern spijp && + spijp.getStopUse() == StopUseEnumeration.PASSTHROUGH + ); + } + @Override public DataImportIssue logMessage(String key, ServiceJourney sj) { return new StopPointsMismatch(sj.getId(), getPatternId(sj)); diff --git a/src/main/java/org/opentripplanner/openstreetmap/issues/FloorNumberUnknownAssumedGroundLevel.java b/src/main/java/org/opentripplanner/openstreetmap/issues/FloorNumberUnknownAssumedGroundLevel.java index f037cf1a01b..bc7adeb07bd 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/issues/FloorNumberUnknownAssumedGroundLevel.java +++ b/src/main/java/org/opentripplanner/openstreetmap/issues/FloorNumberUnknownAssumedGroundLevel.java @@ -1,22 +1,23 @@ package org.opentripplanner.openstreetmap.issues; import org.opentripplanner.graph_builder.issue.api.DataImportIssue; +import org.opentripplanner.openstreetmap.model.OSMWithTags; -public class FloorNumberUnknownAssumedGroundLevel implements DataImportIssue { +public record FloorNumberUnknownAssumedGroundLevel(String layer, OSMWithTags entity) + implements DataImportIssue { + private static final String FMT = + "%s : could not determine floor number for layer %s, assumed to be ground-level."; - public static final String FMT = - "Could not determine floor number for layer %s, assumed to be ground-level."; + private static final String HTMLFMT = + "'%s' : could not determine floor number for layer %s, assumed to be ground-level."; - final String layer; - final Integer floorNumber; - - public FloorNumberUnknownAssumedGroundLevel(String layer, Integer floorNumber) { - this.layer = layer; - this.floorNumber = floorNumber; + @Override + public String getMessage() { + return String.format(FMT, entity.getId(), layer); } @Override - public String getMessage() { - return String.format(FMT, layer, floorNumber); + public String getHTMLMessage() { + return String.format(HTMLFMT, entity.url(), entity.getId(), layer); } } diff --git a/src/main/java/org/opentripplanner/openstreetmap/issues/FloorNumberUnknownGuessedFromAltitude.java b/src/main/java/org/opentripplanner/openstreetmap/issues/FloorNumberUnknownGuessedFromAltitude.java index 3adf7c70263..74e7cff3fda 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/issues/FloorNumberUnknownGuessedFromAltitude.java +++ b/src/main/java/org/opentripplanner/openstreetmap/issues/FloorNumberUnknownGuessedFromAltitude.java @@ -1,22 +1,27 @@ package org.opentripplanner.openstreetmap.issues; import org.opentripplanner.graph_builder.issue.api.DataImportIssue; +import org.opentripplanner.openstreetmap.model.OSMWithTags; -public class FloorNumberUnknownGuessedFromAltitude implements DataImportIssue { +public record FloorNumberUnknownGuessedFromAltitude( + String layer, + Integer floorNumber, + OSMWithTags entity +) + implements DataImportIssue { + private static final String FMT = + "%s : could not determine floor number for layer %s. Guessed %s (0-based) from altitude."; - public static final String FMT = - "Could not determine floor number for layer %s. Guessed %s (0-based) from altitude."; + private static final String HTMLFMT = + "'%s' : could not determine floor number for layer %s. Guessed %s (0-based) from altitude."; - final String layer; - final Integer floorNumber; - - public FloorNumberUnknownGuessedFromAltitude(String layer, Integer floorNumber) { - this.layer = layer; - this.floorNumber = floorNumber; + @Override + public String getMessage() { + return String.format(FMT, entity.getId(), layer, floorNumber); } @Override - public String getMessage() { - return String.format(FMT, layer, floorNumber); + public String getHTMLMessage() { + return String.format(HTMLFMT, entity.url(), entity.getId(), layer, floorNumber); } } diff --git a/src/main/java/org/opentripplanner/openstreetmap/model/OSMLevel.java b/src/main/java/org/opentripplanner/openstreetmap/model/OSMLevel.java index 9aef9e4bd01..6f121ed0e13 100644 --- a/src/main/java/org/opentripplanner/openstreetmap/model/OSMLevel.java +++ b/src/main/java/org/opentripplanner/openstreetmap/model/OSMLevel.java @@ -53,7 +53,8 @@ public static OSMLevel fromString( String spec, Source source, boolean incrementNonNegative, - DataImportIssueStore issueStore + DataImportIssueStore issueStore, + OSMWithTags osmObj ) { /* extract any altitude information after the @ character */ Double altitude = null; @@ -111,7 +112,7 @@ public static OSMLevel fromString( /* fall back on altitude when necessary */ if (floorNumber == null && altitude != null) { floorNumber = (int) (altitude / METERS_PER_FLOOR); - issueStore.add(new FloorNumberUnknownGuessedFromAltitude(spec, floorNumber)); + issueStore.add(new FloorNumberUnknownGuessedFromAltitude(spec, floorNumber, osmObj)); reliable = false; } @@ -122,7 +123,7 @@ public static OSMLevel fromString( /* signal failure to extract any useful level information */ if (floorNumber == null) { floorNumber = 0; - issueStore.add(new FloorNumberUnknownAssumedGroundLevel(spec, floorNumber)); + issueStore.add(new FloorNumberUnknownAssumedGroundLevel(spec, osmObj)); reliable = false; } return new OSMLevel(floorNumber, altitude, shortName, longName, source, reliable); @@ -132,7 +133,8 @@ public static List fromSpecList( String specList, Source source, boolean incrementNonNegative, - DataImportIssueStore issueStore + DataImportIssueStore issueStore, + OSMWithTags osmObj ) { List levelSpecs = new ArrayList<>(); @@ -153,7 +155,7 @@ public static List fromSpecList( /* build an OSMLevel for each level spec in the list */ List levels = new ArrayList<>(); for (String spec : levelSpecs) { - levels.add(fromString(spec, source, incrementNonNegative, issueStore)); + levels.add(fromString(spec, source, incrementNonNegative, issueStore, osmObj)); } return levels; } @@ -162,10 +164,17 @@ public static Map mapFromSpecList( String specList, Source source, boolean incrementNonNegative, - DataImportIssueStore issueStore + DataImportIssueStore issueStore, + OSMWithTags osmObj ) { Map map = new HashMap<>(); - for (OSMLevel level : fromSpecList(specList, source, incrementNonNegative, issueStore)) { + for (OSMLevel level : fromSpecList( + specList, + source, + incrementNonNegative, + issueStore, + osmObj + )) { map.put(level.shortName, level); } return map; diff --git a/src/main/java/org/opentripplanner/routing/TripTimesShortHelper.java b/src/main/java/org/opentripplanner/routing/TripTimeOnDateHelper.java similarity index 89% rename from src/main/java/org/opentripplanner/routing/TripTimesShortHelper.java rename to src/main/java/org/opentripplanner/routing/TripTimeOnDateHelper.java index d13b84da7f0..72665edf657 100644 --- a/src/main/java/org/opentripplanner/routing/TripTimesShortHelper.java +++ b/src/main/java/org/opentripplanner/routing/TripTimeOnDateHelper.java @@ -14,11 +14,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class TripTimesShortHelper { +public class TripTimeOnDateHelper { - private static final Logger LOG = LoggerFactory.getLogger(TripTimesShortHelper.class); + private static final Logger LOG = LoggerFactory.getLogger(TripTimeOnDateHelper.class); - public static List getTripTimesShort( + public static List getTripTimeOnDates( TransitService transitService, Trip trip, LocalDate serviceDate @@ -37,7 +37,7 @@ public static List getTripTimesShort( timetable = transitService.getTimetableForTripPattern(pattern, serviceDate); } - // This check is made here to avoid changing TripTimeShort.fromTripTimes + // This check is made here to avoid changing TripTimeOnDate.fromTripTimes TripTimes times = timetable.getTripTimes(trip); if ( !transitService.getServiceCodesRunningForDate(serviceDate).contains(times.getServiceCode()) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java b/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java index d7098c20661..155394376ae 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapper.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Objects; import org.opentripplanner.astar.model.GraphPath; +import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.framework.geometry.GeometryUtils; import org.opentripplanner.framework.i18n.NonLocalizedString; import org.opentripplanner.model.GenericLocation; @@ -105,9 +106,16 @@ public Itinerary createItinerary(RaptorPath path) { Leg transitLeg = null; + PathLeg previousLeg = null; while (!pathLeg.isEgressLeg()) { // Map transit leg if (pathLeg.isTransitLeg()) { + if ( + OTPFeature.ExtraTransferLegOnSameStop.isOn() && + isPathTransferAtSameStop(previousLeg, pathLeg) + ) { + legs.add(createTransferLegAtSameStop(previousLeg, pathLeg)); + } transitLeg = mapTransitLeg(transitLeg, pathLeg.asTransitLeg()); legs.add(transitLeg); } @@ -123,6 +131,7 @@ else if (pathLeg.isTransferLeg()) { } } + previousLeg = pathLeg; pathLeg = pathLeg.nextLeg(); } @@ -164,6 +173,19 @@ else if (pathLeg.isTransferLeg()) { return itinerary; } + private static boolean isPathTransferAtSameStop( + PathLeg previousLeg, + PathLeg currentLeg + ) { + return ( + previousLeg != null && + previousLeg.isTransitLeg() && + currentLeg.isTransitLeg() && + !previousLeg.asTransitLeg().isStaySeatedOntoNextLeg() && + (previousLeg.asTransitLeg().toStop() == currentLeg.asTransitLeg().fromStop()) + ); + } + private List mapAccessLeg(AccessPathLeg accessPathLeg) { if (accessPathLeg.access().isFree()) { return List.of(); @@ -264,6 +286,27 @@ private boolean isFree(EgressPathLeg egressPathLeg) { return egressPathLeg.egress().isFree(); } + /** + * If a routing result transfers at the very same stop, RAPTOR doesn't add a path leg. However, + * sometimes we want to create a zero distance leg so a UI can show a transfer. Since it would + * be considered backwards-incompatible, this is an opt-in feature. + */ + private Leg createTransferLegAtSameStop(PathLeg previousLeg, PathLeg nextLeg) { + var transferStop = Place.forStop(transitLayer.getStopByIndex(previousLeg.toStop())); + return StreetLeg + .create() + .withMode(TraverseMode.WALK) + .withStartTime(createZonedDateTime(previousLeg.toTime())) + .withEndTime(createZonedDateTime(nextLeg.fromTime())) + .withFrom(transferStop) + .withTo(transferStop) + .withDistanceMeters(0) + .withGeneralizedCost(0) + .withGeometry(GeometryUtils.makeLineString(transferStop.coordinate, transferStop.coordinate)) + .withWalkSteps(List.of()) + .build(); + } + private List mapTransferLeg(TransferPathLeg pathLeg, TraverseMode transferMode) { var transferFromStop = transitLayer.getStopByIndex(pathLeg.fromStop()); var transferToStop = transitLayer.getStopByIndex(pathLeg.toStop()); diff --git a/src/main/java/org/opentripplanner/routing/stoptimes/StopTimesHelper.java b/src/main/java/org/opentripplanner/routing/stoptimes/StopTimesHelper.java index 109bf7f53c0..4d4abf577db 100644 --- a/src/main/java/org/opentripplanner/routing/stoptimes/StopTimesHelper.java +++ b/src/main/java/org/opentripplanner/routing/stoptimes/StopTimesHelper.java @@ -62,7 +62,7 @@ public static List stopTimesForStop( Collection patterns = transitService.getPatternsForStop(stop, true); for (TripPattern pattern : patterns) { - Queue pq = listTripTimeShortsForPatternAtStop( + Queue pq = listTripTimeOnDatesForPatternAtStop( transitService, stop, pattern, @@ -154,7 +154,7 @@ public static List stopTimesForPatternAtStop( ArrivalDeparture arrivalDeparture, boolean includeCancellations ) { - Queue pq = listTripTimeShortsForPatternAtStop( + Queue pq = listTripTimeOnDatesForPatternAtStop( transitService, stop, pattern, @@ -184,7 +184,7 @@ private static List getStopTimesInPattern( return result; } - private static Queue listTripTimeShortsForPatternAtStop( + private static Queue listTripTimeOnDatesForPatternAtStop( TransitService transitService, StopLocation stop, TripPattern pattern, diff --git a/src/main/java/org/opentripplanner/standalone/config/routerequest/ItineraryFiltersConfig.java b/src/main/java/org/opentripplanner/standalone/config/routerequest/ItineraryFiltersConfig.java index 82e2c0839f9..3a58bfe9bcd 100644 --- a/src/main/java/org/opentripplanner/standalone/config/routerequest/ItineraryFiltersConfig.java +++ b/src/main/java/org/opentripplanner/standalone/config/routerequest/ItineraryFiltersConfig.java @@ -11,13 +11,9 @@ import org.opentripplanner.routing.api.request.preference.ItineraryFilterDebugProfile; import org.opentripplanner.routing.api.request.preference.ItineraryFilterPreferences; import org.opentripplanner.standalone.config.framework.json.NodeAdapter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class ItineraryFiltersConfig { - private static final Logger LOG = LoggerFactory.getLogger(ItineraryFiltersConfig.class); - public static void mapItineraryFilterParams( String parameterName, NodeAdapter root, diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java index 711eb1c221c..0ce120ab1eb 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/RealTimeTripTimes.java @@ -98,13 +98,6 @@ public I18NString getHeadsign(final int stop) { : scheduledTripTimes.getHeadsign(stop); } - /** - * Return list of via names per particular stop. This field provides info about intermediate stops - * between current stop and final trip destination. Mapped from NeTEx DestinationDisplay.vias. No - * GTFS mapping at the moment. - * - * @return Empty list if there are no vias registered for a stop. - */ @Override public List getHeadsignVias(final int stop) { return scheduledTripTimes.getHeadsignVias(stop); diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java index d367932d24d..3cae0c1678b 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimes.java @@ -34,14 +34,15 @@ public final class ScheduledTripTimes implements TripTimes { * When time-shifting from one time-zone to another negative times may occur. */ private static final int MIN_TIME = DurationUtils.durationInSeconds("-12h"); + /** * We allow a trip to last for maximum 20 days. In Norway the longest trip is 6 days. */ private static final int MAX_TIME = DurationUtils.durationInSeconds("20d"); /** - * Implementation notes: This allows re-using the same scheduled arrival and departure time - * arrays for many ScheduledTripTimes. It is also used in materializing frequency-based + * Implementation notes: This timeShift allows re-using the same scheduled arrival and departure + * time arrays for many ScheduledTripTimes. It is also used in materializing frequency-based * ScheduledTripTimes. */ private final int timeShift; @@ -53,19 +54,24 @@ public final class ScheduledTripTimes implements TripTimes { private final List dropOffBookingInfos; private final List pickupBookingInfos; + /** + * Any number of array elements may point to the same I18NString instance if the headsign remains + * unchanged between stops. + */ @Nullable private final I18NString[] headsigns; /** - * Implementation notes: This is 2D array since there can be more than one via name/stop per each - * record in stop sequence). Outer array may be null if there are no vias in stop sequence. Inner - * array may be null if there are no vias for particular stop. This is done in order to save - * space. + * A 2D array of String containing zero or more Via messages displayed at each stop in the + * stop sequence. This reference be null if no stop in the entire sequence of stops has any via + * strings. Any subarray may also be null or empty if no Via strings are displayed at that + * particular stop. These nulls are allowed to conserve memory in the common case where there are + * few or no via messages. */ @Nullable private final String[][] headsignVias; - private final int[] originalGtfsStopSequence; + private final int[] gtfsSequenceOfStopIndex; ScheduledTripTimes(ScheduledTripTimesBuilder builder) { this.timeShift = builder.timeShift(); @@ -78,7 +84,7 @@ public final class ScheduledTripTimes implements TripTimes { this.dropOffBookingInfos = Objects.requireNonNull(builder.dropOffBookingInfos()); this.headsigns = builder.headsigns(); this.headsignVias = builder.headsignVias(); - this.originalGtfsStopSequence = builder.originalGtfsStopSequence(); + this.gtfsSequenceOfStopIndex = builder.gtfsSequenceOfStopIndex(); validate(); } @@ -107,7 +113,7 @@ public ScheduledTripTimesBuilder copyOf(Deduplicator deduplicator) { pickupBookingInfos, headsigns, headsignVias, - originalGtfsStopSequence, + gtfsSequenceOfStopIndex, deduplicator ); } @@ -272,16 +278,16 @@ public OccupancyStatus getOccupancyStatus(int ignore) { @Override public int gtfsSequenceOfStopIndex(final int stop) { - return originalGtfsStopSequence[stop]; + return gtfsSequenceOfStopIndex[stop]; } @Override public OptionalInt stopIndexOfGtfsSequence(int stopSequence) { - if (originalGtfsStopSequence == null) { + if (gtfsSequenceOfStopIndex == null) { return OptionalInt.empty(); } - for (int i = 0; i < originalGtfsStopSequence.length; i++) { - var sequence = originalGtfsStopSequence[i]; + for (int i = 0; i < gtfsSequenceOfStopIndex.length; i++) { + var sequence = gtfsSequenceOfStopIndex[i]; if (sequence == stopSequence) { return OptionalInt.of(i); } diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesBuilder.java b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesBuilder.java index ce142fd7628..200afc27e8d 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesBuilder.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/ScheduledTripTimesBuilder.java @@ -23,7 +23,7 @@ public class ScheduledTripTimesBuilder { private List pickupBookingInfos; private I18NString[] headsigns; private String[][] headsignVias; - private int[] originalGtfsStopSequence; + private int[] gtfsSequenceOfStopIndex; private final DeduplicatorService deduplicator; ScheduledTripTimesBuilder(@Nullable DeduplicatorService deduplicator) { @@ -41,7 +41,7 @@ public class ScheduledTripTimesBuilder { List pickupBookingInfos, I18NString[] headsigns, String[][] headsignVias, - int[] originalGtfsStopSequence, + int[] gtfsSequenceOfStopIndex, DeduplicatorService deduplicator ) { this.timeShift = timeShift; @@ -54,7 +54,7 @@ public class ScheduledTripTimesBuilder { this.pickupBookingInfos = pickupBookingInfos; this.headsigns = headsigns; this.headsignVias = headsignVias; - this.originalGtfsStopSequence = originalGtfsStopSequence; + this.gtfsSequenceOfStopIndex = gtfsSequenceOfStopIndex; this.deduplicator = deduplicator == null ? DeduplicatorService.NOOP : deduplicator; } @@ -169,12 +169,12 @@ public ScheduledTripTimesBuilder withHeadsignVias(String[][] headsignVias) { return this; } - public int[] originalGtfsStopSequence() { - return originalGtfsStopSequence; + public int[] gtfsSequenceOfStopIndex() { + return gtfsSequenceOfStopIndex; } - public ScheduledTripTimesBuilder withOriginalGtfsStopSequence(int[] originalGtfsStopSequence) { - this.originalGtfsStopSequence = deduplicator.deduplicateIntArray(originalGtfsStopSequence); + public ScheduledTripTimesBuilder withGtfsSequenceOfStopIndex(int[] gtfsSequenceOfStopIndex) { + this.gtfsSequenceOfStopIndex = deduplicator.deduplicateIntArray(gtfsSequenceOfStopIndex); return this; } diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/StopTimeToScheduledTripTimesMapper.java b/src/main/java/org/opentripplanner/transit/model/timetable/StopTimeToScheduledTripTimesMapper.java index 0779575aee5..7481d8a88f5 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/StopTimeToScheduledTripTimesMapper.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/StopTimeToScheduledTripTimesMapper.java @@ -4,6 +4,7 @@ import java.util.BitSet; import java.util.Collection; import java.util.List; +import javax.annotation.Nullable; import org.opentripplanner.framework.i18n.I18NString; import org.opentripplanner.model.StopTime; import org.opentripplanner.transit.model.framework.DeduplicatorService; @@ -57,7 +58,7 @@ private ScheduledTripTimes doMap(Collection stopTimes) { builder .withDepartureTimes(departures) .withArrivalTimes(arrivals) - .withOriginalGtfsStopSequence(sequences) + .withGtfsSequenceOfStopIndex(sequences) .withHeadsigns(makeHeadsignsArray(stopTimes)) .withHeadsignVias(makeHeadsignViasArray(stopTimes)) .withDropOffBookingInfos(dropOffBookingInfos) @@ -103,10 +104,13 @@ private I18NString[] makeHeadsignsArray(final Collection stopTimes) { } /** - * Create 2D String array for via names for each stop in sequence. - * - * @return May be null if no vias are present in stop sequence. + * Create 2D array of String containing zero or more Via messages displayed at each stop in the + * stop sequence. + * @return May be null if no stop in the entire sequence of stops has any via strings. Any + * subarray may also be null or empty if no Via strings are displayed at that particular stop. + * @see org.opentripplanner.transit.model.timetable.TripTimes#getHeadsignVias(int) */ + @Nullable private String[][] makeHeadsignViasArray(final Collection stopTimes) { if ( stopTimes diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/Trip.java b/src/main/java/org/opentripplanner/transit/model/timetable/Trip.java index 5a1e9150e78..2d81735e9f3 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/Trip.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/Trip.java @@ -20,6 +20,19 @@ import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.organization.Operator; +/** + * A Trip represents the movement of a public transport vehicle on a given {@link Route}, using a + * given {@link TransitMode}, on a given sequence of stops served at given passing times. + *

A scheduled Trip can run at most once per service date, + * while a frequency-based Trip runs several times on a given service date. + *

A Trip can run on multiple service dates. + *

The service dates on which a trip is running are identified + * by its service id and can be looked up with + * {@link org.opentripplanner.model.calendar.CalendarService}. + *

Trips that follow the same sequence of stops are grouped under a {@link org.opentripplanner.transit.model.network.TripPattern} + * via a {@link org.opentripplanner.model.Timetable} + *

A Trip is equivalent to the TransModel concept of SERVICE JOURNEY. + */ public final class Trip extends AbstractTransitEntity implements LogInfo { private final Operator operator; diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java index e5cd1f1ff28..062a7c17344 100644 --- a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java +++ b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimes.java @@ -17,7 +17,8 @@ * trip times should allow updates and more info, frequency-based trips can use a more compact * implementation, and Flex may expose part of the trip as a "scheduled/regular" stop-to-stop * trip using this interface. All times are expressed as seconds since midnight (as in - * GTFS). + * GTFS). Unless stated otherwise, accessor methods which take an integer stop parameter refer to + * the position within the trip's TripPattern (not its GTFS stop sequence for example). */ public interface TripTimes extends Serializable, Comparable { /** @@ -129,6 +130,7 @@ default int compareTo(TripTimes other) { I18NString getTripHeadsign(); /** + * The headsign displayed by the vehicle, which may change at each stop along the trip. * Both trip_headsign and stop_headsign (per stop on a particular trip) are optional GTFS fields. * A trip may not have a headsign, in which case we should fall back on a Timetable or * Pattern-level headsign. Such a string will be available when we give TripPatterns or @@ -139,11 +141,11 @@ default int compareTo(TripTimes other) { I18NString getHeadsign(int stop); /** - * Return list of via names per particular stop. This field provides info about intermediate stops - * between current stop and final trip destination. Mapped from NeTEx DestinationDisplay.vias. No - * GTFS mapping at the moment. - * - * @return Empty list if there are no vias registered for a stop. + * Vias are an additional intermediate destinations between the given stop and the terminus, which + * are displayed alongside the terminus headsign. Vias often change or are displayed only at + * certain stops along the way. While the concept of Headsigns exists in both GTFS (Headsign) and + * Netex (DestinationDisplay), the Via concept is only present in Transmodel. + * @return a list of via names visible at the given stop, or an empty list if there are no vias. */ List getHeadsignVias(int stop); @@ -157,7 +159,7 @@ default int compareTo(TripTimes other) { OccupancyStatus getOccupancyStatus(int stop); /** - * Returns the GTFS sequence number of the given 0-based stop position. + * Returns the GTFS sequence number of the given 0-based stop position within the pattern. *

* These are the GTFS stop sequence numbers, which show the order in which the vehicle visits the * stops. Despite the fact that the StopPattern or TripPattern enclosing this class provides an diff --git a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java index b3d68b6ffa5..8fa18443bab 100644 --- a/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/DefaultTransitService.java @@ -22,6 +22,7 @@ import org.locationtech.jts.geom.Envelope; import org.opentripplanner.ext.flex.FlexIndex; import org.opentripplanner.framework.application.OTPRequestTimeoutException; +import org.opentripplanner.framework.collection.CollectionsView; import org.opentripplanner.model.FeedInfo; import org.opentripplanner.model.PathTransfer; import org.opentripplanner.model.StopTimesInPattern; @@ -190,12 +191,19 @@ public RegularStop getRegularStop(FeedScopedId id) { @Override public Route getRouteForId(FeedScopedId id) { - return this.transitModelIndex.getRouteForId(id); + TimetableSnapshot currentSnapshot = lazyGetTimeTableSnapShot(); + if (currentSnapshot != null) { + Route realtimeAddedRoute = currentSnapshot.getRealtimeAddedRoute(id); + if (realtimeAddedRoute != null) { + return realtimeAddedRoute; + } + } + return transitModelIndex.getRouteForId(id); } /** - * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix - * this when doing the issue #3030. + * Add a route to the transit model. + * Used only in unit tests. */ @Override public void addRoutes(Route route) { @@ -272,44 +280,60 @@ public StopLocationsGroup getStopLocationsGroup(FeedScopedId id) { @Override public Trip getTripForId(FeedScopedId id) { - return this.transitModelIndex.getTripForId().get(id); + TimetableSnapshot currentSnapshot = lazyGetTimeTableSnapShot(); + if (currentSnapshot != null) { + Trip trip = currentSnapshot.getRealTimeAddedTrip(id); + if (trip != null) { + return trip; + } + } + return getScheduledTripForId(id); } - /** - * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix - * this when doing the issue #3030. - */ + @Nullable @Override - public void addTripForId(FeedScopedId tripId, Trip trip) { - transitModelIndex.getTripForId().put(tripId, trip); + public Trip getScheduledTripForId(FeedScopedId id) { + return this.transitModelIndex.getTripForId().get(id); } @Override public Collection getAllTrips() { OTPRequestTimeoutException.checkForTimeout(); - return transitModelIndex.getTripForId().values(); + TimetableSnapshot currentSnapshot = lazyGetTimeTableSnapShot(); + if (currentSnapshot != null) { + return new CollectionsView<>( + transitModelIndex.getTripForId().values(), + currentSnapshot.getAllRealTimeAddedTrips() + ); + } + return Collections.unmodifiableCollection(transitModelIndex.getTripForId().values()); } @Override public Collection getAllRoutes() { OTPRequestTimeoutException.checkForTimeout(); - return this.transitModelIndex.getAllRoutes(); + TimetableSnapshot currentSnapshot = lazyGetTimeTableSnapShot(); + if (currentSnapshot != null) { + return new CollectionsView<>( + transitModelIndex.getAllRoutes(), + currentSnapshot.getAllRealTimeAddedRoutes() + ); + } + return Collections.unmodifiableCollection(transitModelIndex.getAllRoutes()); } @Override public TripPattern getPatternForTrip(Trip trip) { + TimetableSnapshot currentSnapshot = lazyGetTimeTableSnapShot(); + if (currentSnapshot != null) { + TripPattern realtimeAddedTripPattern = currentSnapshot.getRealTimeAddedPatternForTrip(trip); + if (realtimeAddedTripPattern != null) { + return realtimeAddedTripPattern; + } + } return this.transitModelIndex.getPatternForTrip().get(trip); } - /** - * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix - * this when doing the issue #3030. - */ - @Override - public void addPatternForTrip(Trip trip, TripPattern pattern) { - transitModelIndex.getPatternForTrip().put(trip, pattern); - } - @Override public TripPattern getPatternForTrip(Trip trip, LocalDate serviceDate) { TripPattern realtimePattern = getRealtimeAddedTripPattern(trip.getId(), serviceDate); @@ -322,16 +346,17 @@ public TripPattern getPatternForTrip(Trip trip, LocalDate serviceDate) { @Override public Collection getPatternsForRoute(Route route) { OTPRequestTimeoutException.checkForTimeout(); - return this.transitModelIndex.getPatternsForRoute().get(route); - } - - /** - * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix - * this when doing the issue #3030. - */ - @Override - public void addPatternsForRoute(Route route, TripPattern pattern) { - transitModelIndex.getPatternsForRoute().put(route, pattern); + Collection tripPatterns = new HashSet<>( + transitModelIndex.getPatternsForRoute().get(route) + ); + TimetableSnapshot currentSnapshot = lazyGetTimeTableSnapShot(); + if (currentSnapshot != null) { + Collection realTimeAddedPatternForRoute = currentSnapshot.getRealTimeAddedPatternForRoute( + route + ); + tripPatterns.addAll(realTimeAddedPatternForRoute); + } + return tripPatterns; } @Override @@ -524,44 +549,48 @@ private TimetableSnapshot lazyGetTimeTableSnapShot() { @Override public TripOnServiceDate getTripOnServiceDateById(FeedScopedId datedServiceJourneyId) { + TimetableSnapshot currentSnapshot = lazyGetTimeTableSnapShot(); + if (currentSnapshot != null) { + TripOnServiceDate tripOnServiceDate = currentSnapshot.getRealTimeAddedTripOnServiceDateById( + datedServiceJourneyId + ); + if (tripOnServiceDate != null) { + return tripOnServiceDate; + } + } return transitModelIndex.getTripOnServiceDateById().get(datedServiceJourneyId); } - /** - * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix - * this when doing the issue #3030. - */ - @Override - public void addTripOnServiceDateById(FeedScopedId id, TripOnServiceDate tripOnServiceDate) { - transitModelIndex.getTripOnServiceDateById().put(id, tripOnServiceDate); - } - @Override public Collection getAllTripOnServiceDates() { - return transitModelIndex.getTripOnServiceDateForTripAndDay().values(); + TimetableSnapshot currentSnapshot = lazyGetTimeTableSnapShot(); + if (currentSnapshot != null) { + return new CollectionsView<>( + transitModelIndex.getTripOnServiceDateForTripAndDay().values(), + currentSnapshot.getAllRealTimeAddedTripOnServiceDate() + ); + } + return Collections.unmodifiableCollection( + transitModelIndex.getTripOnServiceDateForTripAndDay().values() + ); } @Override public TripOnServiceDate getTripOnServiceDateForTripAndDay( TripIdAndServiceDate tripIdAndServiceDate ) { + TimetableSnapshot currentSnapshot = lazyGetTimeTableSnapShot(); + if (currentSnapshot != null) { + TripOnServiceDate tripOnServiceDate = currentSnapshot.getRealTimeAddedTripOnServiceDateForTripAndDay( + tripIdAndServiceDate + ); + if (tripOnServiceDate != null) { + return tripOnServiceDate; + } + } return transitModelIndex.getTripOnServiceDateForTripAndDay().get(tripIdAndServiceDate); } - /** - * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix - * this when doing the issue #3030. - */ - @Override - public void addTripOnServiceDateForTripAndDay( - TripIdAndServiceDate tripIdAndServiceDate, - TripOnServiceDate tripOnServiceDate - ) { - transitModelIndex - .getTripOnServiceDateForTripAndDay() - .put(tripIdAndServiceDate, tripOnServiceDate); - } - /** * TODO OTP2 - This is NOT THREAD-SAFE and is used in the real-time updaters, we need to fix * this when doing the issue #3030. diff --git a/src/main/java/org/opentripplanner/transit/service/TransitEditorService.java b/src/main/java/org/opentripplanner/transit/service/TransitEditorService.java index 150d1749272..567cb245af5 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitEditorService.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitEditorService.java @@ -20,23 +20,10 @@ public interface TransitEditorService extends TransitService { void addFeedInfo(FeedInfo info); - void addPatternForTrip(Trip trip, TripPattern pattern); - - void addPatternsForRoute(Route route, TripPattern pattern); - void addRoutes(Route route); void addTransitMode(TransitMode mode); - void addTripForId(FeedScopedId tripId, Trip trip); - - void addTripOnServiceDateById(FeedScopedId id, TripOnServiceDate tripOnServiceDate); - - void addTripOnServiceDateForTripAndDay( - TripIdAndServiceDate tripIdAndServiceDate, - TripOnServiceDate tripOnServiceDate - ); - FeedScopedId getOrCreateServiceIdForDate(LocalDate serviceDate); /** diff --git a/src/main/java/org/opentripplanner/transit/service/TransitService.java b/src/main/java/org/opentripplanner/transit/service/TransitService.java index 94870643f71..1836b5612d2 100644 --- a/src/main/java/org/opentripplanner/transit/service/TransitService.java +++ b/src/main/java/org/opentripplanner/transit/service/TransitService.java @@ -74,8 +74,16 @@ public interface TransitService { Collection getNoticesByEntity(AbstractTransitEntity entity); + /** + * Return a trip pattern by id, not including patterns created by real-time updates. + */ TripPattern getTripPatternForId(FeedScopedId id); + /** + * Return all scheduled trip patterns, not including real-time created trip patterns. + * TODO: verify this is the intended behavior and possibly change the method name to + * getAllScheduledTripPatterns + */ Collection getAllTripPatterns(); Collection getNotices(); @@ -92,8 +100,15 @@ public interface TransitService { Agency getAgencyForId(FeedScopedId id); + /** + * Return a route for a given id, including routes created by real-time updates. + * + */ Route getRouteForId(FeedScopedId id); + /** + * Return the routes using the given stop, not including real-time updates. + */ Set getRoutesForStop(StopLocation stop); /** @@ -132,24 +147,43 @@ public interface TransitService { AreaStop getAreaStop(FeedScopedId id); + /** + * Return the trip for the given id, including trips created in real time. + */ + @Nullable Trip getTripForId(FeedScopedId id); + /** + * Return the trip for the given id, not including trips created in real time. + */ + @Nullable + Trip getScheduledTripForId(FeedScopedId id); + + /** + * Return all trips, including those created by real-time updates. + */ Collection getAllTrips(); + /** + * Return all routes, including those created by real-time updates. + */ Collection getAllRoutes(); /** - * Return the scheduled trip pattern for a given trip (not taking into account real-time updates) + * Return the scheduled trip pattern for a given trip. + * If the trip is an added trip (extra journey), return the initial trip pattern for this trip. */ TripPattern getPatternForTrip(Trip trip); /** * Return the trip pattern for a given trip on a service date. The real-time updated version * is returned if it exists, otherwise the scheduled trip pattern is returned. - * */ TripPattern getPatternForTrip(Trip trip, LocalDate serviceDate); + /** + * Return all the trip patterns used in the given route, including those added by real-time updates + */ Collection getPatternsForRoute(Route route); MultiModalStation getMultiModalStationForStation(Station station); @@ -193,12 +227,24 @@ List stopTimesForPatternAtStop( @Nullable Timetable getTimetableForTripPattern(TripPattern tripPattern, LocalDate serviceDate); + /** + * Return the real-time added pattern for a given tripId and a given service date. + * Return null if the trip does not exist or if the trip has no real-time added pattern for + * this date (that is: it is still using its scheduled trip pattern for this date). + */ + @Nullable TripPattern getRealtimeAddedTripPattern(FeedScopedId tripId, LocalDate serviceDate); + /** + * Return true if at least one trip pattern has been created by a real-time update. + */ boolean hasRealtimeAddedTripPatterns(); TripOnServiceDate getTripOnServiceDateForTripAndDay(TripIdAndServiceDate tripIdAndServiceDate); + /** + * Return the TripOnServiceDate for a given id, including real-time updates. + */ TripOnServiceDate getTripOnServiceDateById(FeedScopedId datedServiceJourneyId); Collection getAllTripOnServiceDates(); diff --git a/src/main/java/org/opentripplanner/updater/DefaultRealTimeUpdateContext.java b/src/main/java/org/opentripplanner/updater/DefaultRealTimeUpdateContext.java new file mode 100644 index 00000000000..0921ed5a30d --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/DefaultRealTimeUpdateContext.java @@ -0,0 +1,60 @@ +package org.opentripplanner.updater; + +import org.opentripplanner.ext.siri.EntityResolver; +import org.opentripplanner.ext.siri.SiriFuzzyTripMatcher; +import org.opentripplanner.model.TimetableSnapshot; +import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.transit.service.DefaultTransitService; +import org.opentripplanner.transit.service.TransitModel; +import org.opentripplanner.transit.service.TransitService; + +public class DefaultRealTimeUpdateContext implements RealTimeUpdateContext { + + private final Graph graph; + private final TransitService transitService; + private SiriFuzzyTripMatcher siriFuzzyTripMatcher; + + public DefaultRealTimeUpdateContext( + Graph graph, + TransitModel transitModel, + TimetableSnapshot timetableSnapshotBuffer + ) { + this.graph = graph; + this.transitService = new DefaultTransitService(transitModel, timetableSnapshotBuffer); + } + + /** + * Constructor for unit tests only. + */ + public DefaultRealTimeUpdateContext(Graph graph, TransitModel transitModel) { + this(graph, transitModel, null); + } + + @Override + public Graph graph() { + return graph; + } + + @Override + public TransitService transitService() { + return transitService; + } + + @Override + public synchronized SiriFuzzyTripMatcher siriFuzzyTripMatcher() { + if (siriFuzzyTripMatcher == null) { + siriFuzzyTripMatcher = new SiriFuzzyTripMatcher(transitService); + } + return siriFuzzyTripMatcher; + } + + @Override + public GtfsRealtimeFuzzyTripMatcher gtfsRealtimeFuzzyTripMatcher() { + return new GtfsRealtimeFuzzyTripMatcher(transitService); + } + + @Override + public EntityResolver entityResolver(String feedId) { + return new EntityResolver(transitService, feedId); + } +} diff --git a/src/main/java/org/opentripplanner/updater/GraphUpdaterManager.java b/src/main/java/org/opentripplanner/updater/GraphUpdaterManager.java index 0dad35bfcd8..c97e099a1f2 100644 --- a/src/main/java/org/opentripplanner/updater/GraphUpdaterManager.java +++ b/src/main/java/org/opentripplanner/updater/GraphUpdaterManager.java @@ -12,8 +12,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.stream.Collectors; -import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.updater.spi.GraphUpdater; import org.opentripplanner.updater.spi.PollingGraphUpdater; import org.opentripplanner.updater.spi.WriteToGraphCallback; @@ -66,17 +64,14 @@ public class GraphUpdaterManager implements WriteToGraphCallback, GraphUpdaterSt /** * The Graph that will be updated. */ - private final Graph graph; - private final TransitModel transitModel; + private final RealTimeUpdateContext realtimeUpdateContext; /** * Constructor. * - * @param transitModel is the Graph that will be updated. */ - public GraphUpdaterManager(Graph graph, TransitModel transitModel, List updaters) { - this.graph = graph; - this.transitModel = transitModel; + public GraphUpdaterManager(RealTimeUpdateContext context, List updaters) { + this.realtimeUpdateContext = context; // Thread factories used to create new threads, giving them more human-readable names. var graphWriterThreadFactory = new ThreadFactoryBuilder().setNameFormat("graph-writer").build(); this.scheduler = Executors.newSingleThreadScheduledExecutor(graphWriterThreadFactory); @@ -189,7 +184,7 @@ public void stop(boolean cancelRunningTasks) { public Future execute(GraphWriterRunnable runnable) { return scheduler.submit(() -> { try { - runnable.run(graph, transitModel); + runnable.run(realtimeUpdateContext); } catch (Exception e) { LOG.error("Error while running graph writer {}:", runnable.getClass().getName(), e); } diff --git a/src/main/java/org/opentripplanner/updater/GraphWriterRunnable.java b/src/main/java/org/opentripplanner/updater/GraphWriterRunnable.java index cd9860d1145..b72425ac931 100644 --- a/src/main/java/org/opentripplanner/updater/GraphWriterRunnable.java +++ b/src/main/java/org/opentripplanner/updater/GraphWriterRunnable.java @@ -1,8 +1,5 @@ package org.opentripplanner.updater; -import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.transit.service.TransitModel; - /** * The graph should only be modified by a runnable implementing this interface, executed by the * GraphUpdaterManager. A few notes: - Don't spend more time in this runnable than necessary, it @@ -16,5 +13,5 @@ public interface GraphWriterRunnable { /** * This function is executed to modify the graph. */ - void run(Graph graph, TransitModel transitModel); + void run(RealTimeUpdateContext context); } diff --git a/src/main/java/org/opentripplanner/updater/RealTimeUpdateContext.java b/src/main/java/org/opentripplanner/updater/RealTimeUpdateContext.java new file mode 100644 index 00000000000..5a95d01562d --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/RealTimeUpdateContext.java @@ -0,0 +1,45 @@ +package org.opentripplanner.updater; + +import org.opentripplanner.ext.siri.EntityResolver; +import org.opentripplanner.ext.siri.SiriFuzzyTripMatcher; +import org.opentripplanner.routing.graph.Graph; +import org.opentripplanner.transit.service.TransitService; + +/** + * Give access to the transit data and street model in the context of a real-time updater. + * The services exposed should be used only from the GraphWriter thread. + */ +public interface RealTimeUpdateContext { + /** + * Return the street model (graph). + */ + Graph graph(); + + /** + * Return a transit service that can look up both scheduled and real-time data. + * The transit service has access to all real-time updates applied so far, + * including those not yet committed in a published snapshot. + */ + TransitService transitService(); + + /** + * Return a SIRI fuzzy trip matcher that can look up both scheduled and real-time data. + * The SIRI fuzzy trip matcher has access to all real-time updates applied so far, + * including those not yet committed in a published snapshot. + */ + SiriFuzzyTripMatcher siriFuzzyTripMatcher(); + + /** + * Return a GTFS-RT fuzzy trip matcher that can look up both scheduled and real-time data. + * The GTFS-RT fuzzy trip matcher has access to all real-time updates applied so far, + * including those not yet committed in a published snapshot. + */ + GtfsRealtimeFuzzyTripMatcher gtfsRealtimeFuzzyTripMatcher(); + + /** + * Return an entity resolver that can look up both scheduled and real-time data. + * The entity resolver has access to all real-time updates applied so far, + * including those not yet committed in a published snapshot. + */ + EntityResolver entityResolver(String feedId); +} diff --git a/src/main/java/org/opentripplanner/updater/alert/AlertsUpdateHandler.java b/src/main/java/org/opentripplanner/updater/alert/AlertsUpdateHandler.java index 948eac0bc41..648a3d57b09 100644 --- a/src/main/java/org/opentripplanner/updater/alert/AlertsUpdateHandler.java +++ b/src/main/java/org/opentripplanner/updater/alert/AlertsUpdateHandler.java @@ -40,12 +40,16 @@ public class AlertsUpdateHandler { private long earlyStart; /** Set only if we should attempt to match the trip_id from other data in TripDescriptor */ - private GtfsRealtimeFuzzyTripMatcher fuzzyTripMatcher; + private final boolean fuzzyTripMatching; // TODO: replace this with a runtime solution private final DirectionMapper directionMapper = new DirectionMapper(DataImportIssueStore.NOOP); - public void update(FeedMessage message) { + public AlertsUpdateHandler(boolean fuzzyTripMatching) { + this.fuzzyTripMatching = fuzzyTripMatching; + } + + public void update(FeedMessage message, GtfsRealtimeFuzzyTripMatcher fuzzyTripMatcher) { Collection alerts = new ArrayList<>(); for (FeedEntity entity : message.getEntityList()) { if (!entity.hasAlert()) { @@ -53,7 +57,7 @@ public void update(FeedMessage message) { } GtfsRealtime.Alert alert = entity.getAlert(); String id = entity.getId(); - alerts.add(mapAlert(id, alert)); + alerts.add(mapAlert(id, alert, fuzzyTripMatcher)); } transitAlertService.setAlerts(alerts); } @@ -70,11 +74,11 @@ public void setEarlyStart(long earlyStart) { this.earlyStart = earlyStart; } - public void setFuzzyTripMatcher(GtfsRealtimeFuzzyTripMatcher fuzzyTripMatcher) { - this.fuzzyTripMatcher = fuzzyTripMatcher; - } - - private TransitAlert mapAlert(String id, GtfsRealtime.Alert alert) { + private TransitAlert mapAlert( + String id, + GtfsRealtime.Alert alert, + GtfsRealtimeFuzzyTripMatcher fuzzyTripMatcher + ) { TransitAlertBuilder alertBuilder = TransitAlert .of(new FeedScopedId(feedId, id)) .withDescriptionText(deBuffer(alert.getDescriptionText())) @@ -99,7 +103,7 @@ private TransitAlert mapAlert(String id, GtfsRealtime.Alert alert) { alertBuilder.addTimePeriods(periods); for (GtfsRealtime.EntitySelector informed : alert.getInformedEntityList()) { - if (fuzzyTripMatcher != null && informed.hasTrip()) { + if (fuzzyTripMatching && informed.hasTrip()) { TripDescriptor trip = fuzzyTripMatcher.match(feedId, informed.getTrip()); informed = informed.toBuilder().setTrip(trip).build(); } diff --git a/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java b/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java index 79e248a22cf..728c07690ab 100644 --- a/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java +++ b/src/main/java/org/opentripplanner/updater/alert/GtfsRealtimeAlertsUpdater.java @@ -7,9 +7,7 @@ import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.routing.impl.TransitAlertServiceImpl; import org.opentripplanner.routing.services.TransitAlertService; -import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.TransitModel; -import org.opentripplanner.updater.GtfsRealtimeFuzzyTripMatcher; import org.opentripplanner.updater.spi.HttpHeaders; import org.opentripplanner.updater.spi.PollingGraphUpdater; import org.opentripplanner.updater.spi.WriteToGraphCallback; @@ -40,17 +38,12 @@ public GtfsRealtimeAlertsUpdater( this.headers = HttpHeaders.of().acceptProtobuf().add(config.headers()).build(); TransitAlertService transitAlertService = new TransitAlertServiceImpl(transitModel); - var fuzzyTripMatcher = config.fuzzyTripMatching() - ? new GtfsRealtimeFuzzyTripMatcher(new DefaultTransitService(transitModel)) - : null; - this.transitAlertService = transitAlertService; - this.updateHandler = new AlertsUpdateHandler(); + this.updateHandler = new AlertsUpdateHandler(config.fuzzyTripMatching()); this.updateHandler.setEarlyStart(config.earlyStartSec()); this.updateHandler.setFeedId(config.feedId()); this.updateHandler.setTransitAlertService(transitAlertService); - this.updateHandler.setFuzzyTripMatcher(fuzzyTripMatcher); this.otpHttpClient = new OtpHttpClientFactory().create(LOG); LOG.info("Creating real-time alert updater running every {}: {}", pollingPeriod(), url); } @@ -89,7 +82,9 @@ protected void runPolling() { } // Handle update in graph writer runnable - saveResultOnGraph.execute((graph, transitModel) -> updateHandler.update(feed)); + saveResultOnGraph.execute(context -> + updateHandler.update(feed, context.gtfsRealtimeFuzzyTripMatcher()) + ); lastTimestamp = feedTimestamp; } catch (Exception e) { diff --git a/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java b/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java index 83e0bd0fe85..aed4ef13ece 100644 --- a/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java +++ b/src/main/java/org/opentripplanner/updater/configure/UpdaterConfigurator.java @@ -12,11 +12,13 @@ import org.opentripplanner.ext.vehiclerentalservicedirectory.VehicleRentalServiceDirectoryFetcher; import org.opentripplanner.ext.vehiclerentalservicedirectory.api.VehicleRentalServiceDirectoryFetcherParameters; import org.opentripplanner.framework.io.OtpHttpClientFactory; +import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.model.calendar.openinghours.OpeningHoursCalendarService; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.service.realtimevehicles.RealtimeVehicleRepository; import org.opentripplanner.service.vehiclerental.VehicleRentalRepository; import org.opentripplanner.transit.service.TransitModel; +import org.opentripplanner.updater.DefaultRealTimeUpdateContext; import org.opentripplanner.updater.GraphUpdaterManager; import org.opentripplanner.updater.UpdatersParameters; import org.opentripplanner.updater.alert.GtfsRealtimeAlertsUpdater; @@ -93,7 +95,16 @@ private void configure() { ) ); - GraphUpdaterManager updaterManager = new GraphUpdaterManager(graph, transitModel, updaters); + TimetableSnapshot timetableSnapshotBuffer = null; + if (siriTimetableSnapshotSource != null) { + timetableSnapshotBuffer = siriTimetableSnapshotSource.getTimetableSnapshotBuffer(); + } else if (gtfsTimetableSnapshotSource != null) { + timetableSnapshotBuffer = gtfsTimetableSnapshotSource.getTimetableSnapshotBuffer(); + } + GraphUpdaterManager updaterManager = new GraphUpdaterManager( + new DefaultRealTimeUpdateContext(graph, transitModel, timetableSnapshotBuffer), + updaters + ); configureTimetableSnapshotFlush(updaterManager); @@ -159,30 +170,22 @@ private List createUpdatersFromConfig() { updaters.add(new GtfsRealtimeAlertsUpdater(configItem, transitModel)); } for (var configItem : updatersParameters.getPollingStoptimeUpdaterParameters()) { - updaters.add( - new PollingTripUpdater(configItem, transitModel, provideGtfsTimetableSnapshot()) - ); + updaters.add(new PollingTripUpdater(configItem, provideGtfsTimetableSnapshot())); } for (var configItem : updatersParameters.getVehiclePositionsUpdaterParameters()) { - updaters.add( - new PollingVehiclePositionUpdater(configItem, realtimeVehicleRepository, transitModel) - ); + updaters.add(new PollingVehiclePositionUpdater(configItem, realtimeVehicleRepository)); } for (var configItem : updatersParameters.getSiriETUpdaterParameters()) { - updaters.add(new SiriETUpdater(configItem, transitModel, provideSiriTimetableSnapshot())); + updaters.add(new SiriETUpdater(configItem, provideSiriTimetableSnapshot())); } for (var configItem : updatersParameters.getSiriETGooglePubsubUpdaterParameters()) { - updaters.add( - new SiriETGooglePubsubUpdater(configItem, transitModel, provideSiriTimetableSnapshot()) - ); + updaters.add(new SiriETGooglePubsubUpdater(configItem, provideSiriTimetableSnapshot())); } for (var configItem : updatersParameters.getSiriSXUpdaterParameters()) { updaters.add(new SiriSXUpdater(configItem, transitModel)); } for (var configItem : updatersParameters.getMqttGtfsRealtimeUpdaterParameters()) { - updaters.add( - new MqttGtfsRealtimeUpdater(configItem, transitModel, provideGtfsTimetableSnapshot()) - ); + updaters.add(new MqttGtfsRealtimeUpdater(configItem, provideGtfsTimetableSnapshot())); } for (var configItem : updatersParameters.getVehicleParkingUpdaterParameters()) { switch (configItem.updateType()) { @@ -213,9 +216,7 @@ private List createUpdatersFromConfig() { } } for (var configItem : updatersParameters.getSiriAzureETUpdaterParameters()) { - updaters.add( - new SiriAzureETUpdater(configItem, transitModel, provideSiriTimetableSnapshot()) - ); + updaters.add(new SiriAzureETUpdater(configItem, provideSiriTimetableSnapshot())); } for (var configItem : updatersParameters.getSiriAzureSXUpdaterParameters()) { updaters.add(new SiriAzureSXUpdater(configItem, transitModel)); diff --git a/src/main/java/org/opentripplanner/updater/images/snapshot-manager-1.excalidraw b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-1.excalidraw new file mode 100644 index 00000000000..9594a13efca --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-1.excalidraw @@ -0,0 +1,1055 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 878, + "versionNonce": 871059208, + "index": "a3", + "isDeleted": false, + "id": "0pJJ0P3IcmodwiKkdR2kq", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 689.7500000000001, + "y": 346.767333984375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 280, + "height": 35, + "seed": 1718390863, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Rsqx3K7Zier_pluAIttuF" + }, + { + "id": "uaZ9cZoHWJ7RQE9WQPtl_", + "type": "arrow" + } + ], + "updated": 1719560874581, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 846, + "versionNonce": 1490487304, + "index": "a3V", + "isDeleted": false, + "id": "Rsqx3K7Zier_pluAIttuF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 738.3099975585939, + "y": 351.767333984375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 182.8800048828125, + "height": 25, + "seed": 1841444161, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719556143254, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshot", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "0pJJ0P3IcmodwiKkdR2kq", + "originalText": "TimetableSnapshot", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1252, + "versionNonce": 264816392, + "index": "a6", + "isDeleted": false, + "id": "FYdjRq1ZxZRKW71xenQPk", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 852.7500000000001, + "y": 457.61216157058186, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 395, + "height": 35, + "seed": 1714663023, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "QKOKv1iEnscsKH2KpNiIM" + }, + { + "id": "Y9Qpdb9Axyp6RdbhZhFJG", + "type": "arrow" + }, + { + "id": "eEB94w4lmv7yE_xEghxha", + "type": "arrow" + }, + { + "id": "3huuRNhb5Un8r4E2lJUDZ", + "type": "arrow" + } + ], + "updated": 1719556143254, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1260, + "versionNonce": 2118714120, + "index": "a7", + "isDeleted": false, + "id": "QKOKv1iEnscsKH2KpNiIM", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 863.9199981689454, + "y": 462.61216157058186, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 372.6600036621094, + "height": 25, + "seed": 2030252975, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719556143254, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Scheduled/RealTimeTripTimes per Trip", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "FYdjRq1ZxZRKW71xenQPk", + "originalText": "Scheduled/RealTimeTripTimes per Trip", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1055, + "versionNonce": 1262707208, + "index": "a8", + "isDeleted": false, + "id": "CusrFkXELH9Cjjcvg8Hnq", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 929.7500000000001, + "y": 505.5114000635349, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 320, + "height": 35, + "seed": 934414479, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "vTgJ0m4aQadBbPLk0OitF", + "type": "text" + }, + { + "id": "Y9Qpdb9Axyp6RdbhZhFJG", + "type": "arrow" + } + ], + "updated": 1719556143254, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 794, + "versionNonce": 1388871688, + "index": "a8V", + "isDeleted": false, + "id": "vTgJ0m4aQadBbPLk0OitF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 984.0499877929689, + "y": 510.5114000635349, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 211.4000244140625, + "height": 25, + "seed": 679236687, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719556143254, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "arrivalTimes per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "CusrFkXELH9Cjjcvg8Hnq", + "originalText": "arrivalTimes per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1095, + "versionNonce": 1019952904, + "index": "a9", + "isDeleted": false, + "id": "MTFaZJib6cYVpQ6DLyS5o", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 932.7500000000001, + "y": 545.767333984375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 314.99999999999994, + "height": 35, + "seed": 413146799, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "7gcSXXHKJD4Uob2QZR1nS", + "type": "text" + }, + { + "id": "Y9Qpdb9Axyp6RdbhZhFJG", + "type": "arrow" + }, + { + "id": "3huuRNhb5Un8r4E2lJUDZ", + "type": "arrow" + } + ], + "updated": 1719556143254, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 727, + "versionNonce": 1606229256, + "index": "a9G", + "isDeleted": false, + "id": "7gcSXXHKJD4Uob2QZR1nS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 968.3799896240236, + "y": 550.767333984375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 243.74002075195312, + "height": 25, + "seed": 1192793903, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719556143254, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "departureTimes per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "MTFaZJib6cYVpQ6DLyS5o", + "originalText": "departureTimes per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 927, + "versionNonce": 1529101320, + "index": "aF", + "isDeleted": false, + "id": "HPxB5t7bbywsYcTM412DR", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 764.7500000000001, + "y": 404.767333984375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 274, + "height": 35, + "seed": 1784512239, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "B0teZHtNYXfEFNm303Oyn" + }, + { + "id": "eEB94w4lmv7yE_xEghxha", + "type": "arrow" + }, + { + "id": "uaZ9cZoHWJ7RQE9WQPtl_", + "type": "arrow" + }, + { + "id": "VaujyLQAE-M_FpUK_EYrU", + "type": "arrow" + } + ], + "updated": 1719556143254, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 835, + "versionNonce": 700577800, + "index": "aG", + "isDeleted": false, + "id": "B0teZHtNYXfEFNm303Oyn", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 854.219997406006, + "y": 409.767333984375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 95.06000518798828, + "height": 25, + "seed": 184319055, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719556143254, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "HPxB5t7bbywsYcTM412DR", + "originalText": "Timetable", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 4832, + "versionNonce": 200277000, + "index": "aH", + "isDeleted": false, + "id": "eEB94w4lmv7yE_xEghxha", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 814.5426226427703, + "y": 441.5259546740301, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 36.24530716338654, + "height": 34.70858134957689, + "seed": 659409167, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "BJIpIGdtso6mDq1miGabC" + } + ], + "updated": 1719556143376, + "link": null, + "locked": false, + "startBinding": { + "elementId": "HPxB5t7bbywsYcTM412DR", + "focus": 0.6473379217340073, + "gap": 1.7586206896551175 + }, + "endBinding": { + "elementId": "FYdjRq1ZxZRKW71xenQPk", + "focus": -0.8277618335111296, + "gap": 1.9620701938432035 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 4.254036935820466, + 22.82629723543232 + ], + [ + 36.24530716338654, + 34.70858134957689 + ] + ] + }, + { + "type": "text", + "version": 37, + "versionNonce": 119117320, + "index": "aHV", + "isDeleted": false, + "id": "BJIpIGdtso6mDq1miGabC", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 809.540659059792, + "y": 454.35225190946244, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 18.512001037597656, + "height": 20, + "seed": 1898592129, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719556143254, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:N", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "eEB94w4lmv7yE_xEghxha", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 3478, + "versionNonce": 1888918024, + "index": "aJ", + "isDeleted": false, + "id": "Y9Qpdb9Axyp6RdbhZhFJG", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 878.260908629102, + "y": 495.61216157058186, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 50.489091370897995, + "height": 28.84649005477206, + "seed": 267374383, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1719556143376, + "link": null, + "locked": false, + "startBinding": { + "elementId": "FYdjRq1ZxZRKW71xenQPk", + "focus": 0.8764519102761051, + "gap": 3 + }, + "endBinding": { + "elementId": "CusrFkXELH9Cjjcvg8Hnq", + "focus": -0.4892657821445202, + "gap": 1.0000000000000568 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 5.369091370898104, + 24.965517241379303 + ], + [ + 50.489091370897995, + 28.84649005477206 + ] + ] + }, + { + "type": "arrow", + "version": 3424, + "versionNonce": 917354504, + "index": "aK", + "isDeleted": false, + "id": "3huuRNhb5Un8r4E2lJUDZ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 877.2618147936422, + "y": 495.25009260506465, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 56, + "height": 69.36040923698579, + "seed": 1051464015, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1719556143377, + "link": null, + "locked": false, + "startBinding": { + "elementId": "FYdjRq1ZxZRKW71xenQPk", + "focus": 0.8643694949208631, + "gap": 2.63793103448279 + }, + "endBinding": { + "elementId": "MTFaZJib6cYVpQ6DLyS5o", + "focus": -0.7418501122386174, + "gap": 2.999999999999943 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -3.5118147936420883, + 54.431034482758605 + ], + [ + 52.48818520635791, + 69.36040923698579 + ] + ] + }, + { + "type": "arrow", + "version": 3488, + "versionNonce": 471160328, + "index": "aS", + "isDeleted": false, + "id": "uaZ9cZoHWJ7RQE9WQPtl_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 719.6635021772845, + "y": 382.767333984375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 40.0864978227155, + "height": 38.83755827970606, + "seed": 1171021807, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "3kK4yY_F9HXkvjUIPAvYs" + } + ], + "updated": 1719556143377, + "link": null, + "locked": false, + "startBinding": { + "elementId": "0pJJ0P3IcmodwiKkdR2kq", + "focus": 0.7910196781309997, + "gap": 1 + }, + "endBinding": { + "elementId": "HPxB5t7bbywsYcTM412DR", + "focus": -0.6942679857817844, + "gap": 5.000000000000057 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 4.0864978227155575, + 29 + ], + [ + 40.0864978227155, + 38.83755827970606 + ] + ] + }, + { + "type": "text", + "version": 18, + "versionNonce": 188371464, + "index": "aT", + "isDeleted": false, + "id": "3kK4yY_F9HXkvjUIPAvYs", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 714.4939994812012, + "y": 401.767333984375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 18.512001037597656, + "height": 20, + "seed": 2087772257, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719556143254, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:N", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "uaZ9cZoHWJ7RQE9WQPtl_", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1030, + "versionNonce": 1798492424, + "index": "af", + "isDeleted": false, + "id": "bCE3oAnKMBNphTsURvC_x", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 370.36879264853746, + "y": 348.083251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 280, + "height": 35, + "seed": 1269209720, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "pHYLGgXyL5DktnwUlDm2Y" + }, + { + "id": "VaujyLQAE-M_FpUK_EYrU", + "type": "arrow" + } + ], + "updated": 1719556143254, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1000, + "versionNonce": 48770824, + "index": "ag", + "isDeleted": false, + "id": "pHYLGgXyL5DktnwUlDm2Y", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 416.26878654502184, + "y": 353.083251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 188.20001220703125, + "height": 25, + "seed": 702319480, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719556143254, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshot'", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "bCE3oAnKMBNphTsURvC_x", + "originalText": "TimetableSnapshot'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 4361, + "versionNonce": 1722643464, + "index": "at", + "isDeleted": false, + "id": "VaujyLQAE-M_FpUK_EYrU", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 454.28229482582196, + "y": 385.08325195312506, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 306.0864978227155, + "height": 33.99999999999994, + "seed": 872179832, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "CbjCskm5sLx7MaiQjlCtN" + } + ], + "updated": 1719556143377, + "link": null, + "locked": false, + "startBinding": { + "elementId": "bCE3oAnKMBNphTsURvC_x", + "focus": 0.45645262871487474, + "gap": 2.000000000000057 + }, + "endBinding": { + "elementId": "HPxB5t7bbywsYcTM412DR", + "focus": 0.19499101416337822, + "gap": 4.381207351462592 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 23.086497822715558, + 33.99999999999994 + ], + [ + 306.0864978227155, + 33.837558279706 + ] + ] + }, + { + "type": "text", + "version": 22, + "versionNonce": 392039688, + "index": "au", + "isDeleted": false, + "id": "CbjCskm5sLx7MaiQjlCtN", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 468.1127921297387, + "y": 409.083251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 18.512001037597656, + "height": 20, + "seed": 1648925048, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719556143254, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:N", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "VaujyLQAE-M_FpUK_EYrU", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 309, + "versionNonce": 1361521672, + "index": "av", + "isDeleted": false, + "id": "QKxDGG85ubTkINH8XjgIa", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 667.6187926485376, + "y": 300.583251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1.1368683772161603e-13, + "height": 290.25, + "seed": 368319496, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1719556143254, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 1.1368683772161603e-13, + 290.25 + ] + ] + }, + { + "type": "text", + "version": 88, + "versionNonce": 1383361288, + "index": "aw", + "isDeleted": false, + "id": "dejHNXgVswNESnUz0k6-i", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 756.6187926485376, + "y": 310.583251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 129.48800659179688, + "height": 20, + "seed": 370696824, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719556143254, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "LIVE SNAPSHOT", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "LIVE SNAPSHOT", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 104, + "versionNonce": 1438763528, + "index": "ax", + "isDeleted": false, + "id": "_BgErnciQ7lWarclR8Rby", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 463.6187926485376, + "y": 308.583251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 63.05599594116211, + "height": 20, + "seed": 1420847480, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719556143254, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "BUFFER", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "BUFFER", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 142, + "versionNonce": 1360491384, + "index": "ay", + "isDeleted": false, + "id": "txVbAIcDWZgTt_tjhf3Y8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 542.6187926485376, + "y": 262.083251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 263.7799987792969, + "height": 25, + "seed": 123881592, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719560589026, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshotManager", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "TimetableSnapshotManager", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/snapshot-manager-1.svg b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-1.svg new file mode 100644 index 00000000000..9148b020375 --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-1.svg @@ -0,0 +1,21 @@ + + + + + + + + TimetableSnapshotScheduled/RealTimeTripTimes per TriparrivalTimes per stopdepartureTimes per stopTimetable1:N1:NTimetableSnapshot'1:NLIVE SNAPSHOTBUFFERTimetableSnapshotManager \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/snapshot-manager-2.excalidraw b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-2.excalidraw new file mode 100644 index 00000000000..a2f412156bc --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-2.excalidraw @@ -0,0 +1,1408 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 1113, + "versionNonce": 2040053428, + "index": "Zx", + "isDeleted": false, + "id": "SreuBnOmNO22n_OuKeKFa", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 685.434396324269, + "y": 758.2510795965791, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 238.75, + "height": 101.25000000000001, + "seed": 391053320, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "FsVTEGtkP8-9PGe0Zy07s", + "type": "arrow" + }, + { + "id": "VsUsyXLCTLEbbdW5CVCwm", + "type": "text" + } + ], + "updated": 1721888248338, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1086, + "versionNonce": 517440820, + "index": "Zy", + "isDeleted": false, + "id": "VsUsyXLCTLEbbdW5CVCwm", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 713.3694854355971, + "y": 763.2510795965791, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 182.87982177734375, + "height": 25, + "seed": 746700552, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888248338, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshot", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "SreuBnOmNO22n_OuKeKFa", + "originalText": "TimetableSnapshot", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 703, + "versionNonce": 229534988, + "index": "az", + "isDeleted": false, + "id": "M-19ig2T14SjZyCKR0PpI", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 699.8687926485376, + "y": 797.083251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 212.0000000000001, + "height": 50, + "seed": 978977800, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "Ea8QYvy_d-TC4c1YEMEi-", + "type": "text" + }, + { + "id": "FsVTEGtkP8-9PGe0Zy07s", + "type": "arrow" + } + ], + "updated": 1721888248338, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 602, + "versionNonce": 720816180, + "index": "b00", + "isDeleted": false, + "id": "Ea8QYvy_d-TC4c1YEMEi-", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 705.8928557588893, + "y": 802.083251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 199.95187377929688, + "height": 40, + "seed": 446124920, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888248338, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "M-19ig2T14SjZyCKR0PpI", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1720, + "versionNonce": 422851596, + "index": "b03", + "isDeleted": false, + "id": "1xQ5UI5NY7snt2ZuXK7S-", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 843.434396324269, + "y": 970.3459071827859, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 159.99999999999994, + "height": 35, + "seed": 721722888, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "nYuRkCuHpwQmUuIkj89Tr", + "type": "text" + }, + { + "id": "5h2Tk0yITMlM3pP0nEHD1", + "type": "arrow" + }, + { + "id": "j91c5Cmvg3yCzuTEpoL-l", + "type": "arrow" + }, + { + "id": "UVr6dV_o4fzc4ZkDKXj-F", + "type": "arrow" + }, + { + "id": "rLKdiPHecss0pR5EGhTM7", + "type": "arrow" + } + ], + "updated": 1721888248338, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1688, + "versionNonce": 1952533300, + "index": "b04", + "isDeleted": false, + "id": "nYuRkCuHpwQmUuIkj89Tr", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 876.5144515610854, + "y": 975.3459071827859, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 93.83988952636719, + "height": 25, + "seed": 869506312, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888248338, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "1xQ5UI5NY7snt2ZuXK7S-", + "originalText": "TripTimes", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1514, + "versionNonce": 1619342260, + "index": "b05", + "isDeleted": false, + "id": "4gVSIlkoc59scEVQeVCOX", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 920.434396324269, + "y": 1018.245145675739, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 277.5, + "height": 35, + "seed": 1751825416, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "hWkGBimb0iaRpNDCnm68J", + "type": "text" + }, + { + "id": "j91c5Cmvg3yCzuTEpoL-l", + "type": "arrow" + } + ], + "updated": 1721888248338, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1233, + "versionNonce": 289869364, + "index": "b06", + "isDeleted": false, + "id": "hWkGBimb0iaRpNDCnm68J", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 953.4844985581557, + "y": 1023.245145675739, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 211.39979553222656, + "height": 25, + "seed": 1522924296, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888248338, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "arrivalTimes per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "4gVSIlkoc59scEVQeVCOX", + "originalText": "arrivalTimes per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1573, + "versionNonce": 451598004, + "index": "b07", + "isDeleted": false, + "id": "1EM7F76lddpreFqAiRriH", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 921.785958824269, + "y": 1059.501079596579, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 275.66406249999994, + "height": 35, + "seed": 2103532040, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "j91c5Cmvg3yCzuTEpoL-l", + "type": "arrow" + }, + { + "id": "OM47KwTWGz-BFxBPWcWoR", + "type": "text" + }, + { + "id": "UVr6dV_o4fzc4ZkDKXj-F", + "type": "arrow" + } + ], + "updated": 1721888248338, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1186, + "versionNonce": 1272451380, + "index": "b08", + "isDeleted": false, + "id": "OM47KwTWGz-BFxBPWcWoR", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 937.748117027394, + "y": 1064.501079596579, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 243.73974609375, + "height": 25, + "seed": 322492680, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888248338, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "departureTimes per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "1EM7F76lddpreFqAiRriH", + "originalText": "departureTimes per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1244, + "versionNonce": 1607289780, + "index": "b09", + "isDeleted": false, + "id": "dKqPKHU31_0TbCfsfGz-g", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 764.184396324269, + "y": 873.7510795965791, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 201.5, + "height": 83.75000000000006, + "seed": 533231624, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "QvwVCas-jQdX5p-cbYcsX", + "type": "text" + }, + { + "id": "FsVTEGtkP8-9PGe0Zy07s", + "type": "arrow" + }, + { + "id": "AKspao2VjkV_4HoqeUDxG", + "type": "arrow" + } + ], + "updated": 1721888248338, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1126, + "versionNonce": 574903348, + "index": "b0A", + "isDeleted": false, + "id": "QvwVCas-jQdX5p-cbYcsX", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 817.4044433213393, + "y": 878.7510795965791, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 95.05990600585938, + "height": 25, + "seed": 1460418312, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888248338, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "dKqPKHU31_0TbCfsfGz-g", + "originalText": "Timetable", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 6788, + "versionNonce": 794468788, + "index": "b0B", + "isDeleted": false, + "id": "5h2Tk0yITMlM3pP0nEHD1", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 797.1836528210454, + "y": 949.333251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 41.7886733093801, + "height": 39.72883533414756, + "seed": 226498056, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "QPerhGZfPnT8rSt9KJOWf" + } + ], + "updated": 1721888248354, + "link": null, + "locked": false, + "startBinding": { + "elementId": "mprcOW-xsVDltUw2DeUS5", + "focus": 0.7878884036700777, + "gap": 1 + }, + "endBinding": { + "elementId": "1xQ5UI5NY7snt2ZuXK7S-", + "focus": -0.5433167343314617, + "gap": 4.462070193843431 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 7.297403081814082, + 32.75274556854151 + ], + [ + 41.7886733093801, + 39.72883533414756 + ] + ] + }, + { + "type": "text", + "version": 47, + "versionNonce": 969716236, + "index": "b0C", + "isDeleted": false, + "id": "QPerhGZfPnT8rSt9KJOWf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 795.2250668281524, + "y": 972.0859975216665, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 18.511978149414062, + "height": 20, + "seed": 673191176, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888248336, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:N", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "5h2Tk0yITMlM3pP0nEHD1", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 5418, + "versionNonce": 1141906356, + "index": "b0D", + "isDeleted": false, + "id": "j91c5Cmvg3yCzuTEpoL-l", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 856.9157431964584, + "y": 1008.345907182786, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 62.518653127810694, + "height": 29.20654322532164, + "seed": 208155656, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721888248353, + "link": null, + "locked": false, + "startBinding": { + "elementId": "1xQ5UI5NY7snt2ZuXK7S-", + "focus": 0.8764519102761054, + "gap": 3.0000000000001137 + }, + "endBinding": { + "elementId": "4gVSIlkoc59scEVQeVCOX", + "focus": -0.48926578214451055, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 17.39865312781069, + 24.965517241379303 + ], + [ + 62.518653127810694, + 29.20654322532164 + ] + ] + }, + { + "type": "arrow", + "version": 5464, + "versionNonce": 1215989044, + "index": "b0E", + "isDeleted": false, + "id": "UVr6dV_o4fzc4ZkDKXj-F", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 855.1405235192574, + "y": 1007.9838382172688, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 63.64543530501146, + "height": 70.5544507985046, + "seed": 273969928, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721888248354, + "link": null, + "locked": false, + "startBinding": { + "elementId": "1xQ5UI5NY7snt2ZuXK7S-", + "focus": 0.8643694949208636, + "gap": 2.6379310344829037 + }, + "endBinding": { + "elementId": "1EM7F76lddpreFqAiRriH", + "focus": -0.7418501122386236, + "gap": 2.999999999999943 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 9.293872805011574, + 54.431034482758605 + ], + [ + 63.64543530501146, + 70.5544507985046 + ] + ] + }, + { + "type": "arrow", + "version": 4909, + "versionNonce": 1994112692, + "index": "b0F", + "isDeleted": false, + "id": "FsVTEGtkP8-9PGe0Zy07s", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 716.8856828452381, + "y": 848.083251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 42.298713479030766, + "height": 61.36002245068323, + "seed": 1784730120, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "pLlW6zRhp__rPNy3EHBxQ" + } + ], + "updated": 1721888248354, + "link": null, + "locked": false, + "startBinding": { + "elementId": "M-19ig2T14SjZyCKR0PpI", + "focus": 0.8477281591074967, + "gap": 1 + }, + "endBinding": { + "elementId": "dKqPKHU31_0TbCfsfGz-g", + "focus": -0.5467242788492811, + "gap": 5.000000000000114 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 7.548713479030766, + 41.417827643454075 + ], + [ + 42.298713479030766, + 61.36002245068323 + ] + ] + }, + { + "type": "text", + "version": 27, + "versionNonce": 2029559564, + "index": "b0G", + "isDeleted": false, + "id": "pLlW6zRhp__rPNy3EHBxQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 715.1784072495618, + "y": 879.5010795965791, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 18.511978149414062, + "height": 20, + "seed": 562647304, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888248336, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:N", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "FsVTEGtkP8-9PGe0Zy07s", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 459, + "versionNonce": 665692084, + "index": "b0L", + "isDeleted": false, + "id": "1zm0B7DfMRc8ag7vaaKVq", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 663.3031889728064, + "y": 712.0669975653291, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 391.2662543877959, + "seed": 1097706504, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721888248336, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 391.2662543877959 + ] + ] + }, + { + "type": "text", + "version": 199, + "versionNonce": 824761740, + "index": "b0M", + "isDeleted": false, + "id": "k833tABvmsfzkwffWjqlT", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 752.3031889728064, + "y": 722.0669975653291, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 129.48800659179688, + "height": 20, + "seed": 1978329864, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888248336, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "LIVE SNAPSHOT", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "LIVE SNAPSHOT", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 215, + "versionNonce": 2022084916, + "index": "b0N", + "isDeleted": false, + "id": "f_m2IV-CNJ3isR1GON1HF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 459.3031889728064, + "y": 720.0669975653291, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 63.05599594116211, + "height": 20, + "seed": 1181239816, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888248336, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "BUFFER", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "BUFFER", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 226, + "versionNonce": 634211340, + "index": "b0O", + "isDeleted": false, + "id": "jeznJ_cHzCGtd1TNekK-e", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 558.3031889728064, + "y": 676.0669975653291, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 211.0240020751953, + "height": 20, + "seed": 2111949064, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888248336, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "TimetableSnapshotManager", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "TimetableSnapshotManager", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 913, + "versionNonce": 25156788, + "index": "b0P", + "isDeleted": false, + "id": "mprcOW-xsVDltUw2DeUS5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 777.2437926485374, + "y": 908.333251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 29880440, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "5h2Tk0yITMlM3pP0nEHD1", + "type": "arrow" + }, + { + "id": "RuZ8kER3jTPDPUDsQzhFM", + "type": "text" + } + ], + "updated": 1721888248338, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 887, + "versionNonce": 1825202996, + "index": "b0Q", + "isDeleted": false, + "id": "RuZ8kER3jTPDPUDsQzhFM", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 804.0798369600609, + "y": 918.333251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.32791137695312, + "height": 20, + "seed": 2103648632, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888248338, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "mprcOW-xsVDltUw2DeUS5", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 358, + "versionNonce": 1784799924, + "index": "b0R", + "isDeleted": false, + "id": "kgIEU_mFlYK37pHCz-tvP", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1038.2437926485377, + "y": 969.583251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 147.5, + "height": 35, + "seed": 1139081992, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "gbRmM9dWYQK1i2oPj7e2h", + "type": "text" + }, + { + "id": "rLKdiPHecss0pR5EGhTM7", + "type": "arrow" + } + ], + "updated": 1721888248338, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 297, + "versionNonce": 608839220, + "index": "b0S", + "isDeleted": false, + "id": "gbRmM9dWYQK1i2oPj7e2h", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1092.5338164522486, + "y": 974.583251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 38.919952392578125, + "height": 25, + "seed": 1098661752, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888248338, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Trip", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "kgIEU_mFlYK37pHCz-tvP", + "originalText": "Trip", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 881, + "versionNonce": 881085236, + "index": "b0T", + "isDeleted": false, + "id": "rLKdiPHecss0pR5EGhTM7", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1004.4937926485376, + "y": 989.583251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 32.750000000000114, + "height": 0, + "seed": 62892040, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721888248354, + "link": null, + "locked": false, + "startBinding": { + "elementId": "1xQ5UI5NY7snt2ZuXK7S-", + "focus": 0.09927684401937507, + "gap": 1.0593963242687892 + }, + "endBinding": { + "elementId": "kgIEU_mFlYK37pHCz-tvP", + "focus": -0.14285714285714285, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 32.750000000000114, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 1372, + "versionNonce": 178767284, + "index": "b0V", + "isDeleted": false, + "id": "xvj5PGd6t4I437Ep9m9SK", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 375.7437926485377, + "y": 760.208251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 238.75, + "height": 101.25000000000001, + "seed": 389454856, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "IQpc9Yzs3yNzlOUvLHQzw", + "type": "text" + } + ], + "updated": 1721888248338, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1337, + "versionNonce": 1861970996, + "index": "b0W", + "isDeleted": false, + "id": "IQpc9Yzs3yNzlOUvLHQzw", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 401.018885727151, + "y": 765.208251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 188.19981384277344, + "height": 25, + "seed": 85318408, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888248338, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshot'", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "xvj5PGd6t4I437Ep9m9SK", + "originalText": "TimetableSnapshot'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 888, + "versionNonce": 1568193292, + "index": "b0X", + "isDeleted": false, + "id": "SJwBUGPeewPxIDe_Y1iPU", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 390.1781889728063, + "y": 799.0404243096709, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 212.0000000000001, + "height": 50, + "seed": 313866760, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "0LSedD3crScR1dXLvfhiZ", + "type": "text" + }, + { + "id": "AKspao2VjkV_4HoqeUDxG", + "type": "arrow" + } + ], + "updated": 1721888248338, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 788, + "versionNonce": 1921838900, + "index": "b0Y", + "isDeleted": false, + "id": "0LSedD3crScR1dXLvfhiZ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 396.20225208315793, + "y": 804.0404243096709, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 199.95187377929688, + "height": 40, + "seed": 319863048, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888248338, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "SJwBUGPeewPxIDe_Y1iPU", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 5830, + "versionNonce": 163366068, + "index": "b0l", + "isDeleted": false, + "id": "AKspao2VjkV_4HoqeUDxG", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 498.41830428105027, + "y": 850.040424309671, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 262.3254883674872, + "height": 75.11002245068335, + "seed": 1819905032, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "hmfLbl4pt1lgartKiE3i0" + } + ], + "updated": 1721888248354, + "link": null, + "locked": false, + "startBinding": { + "elementId": "SJwBUGPeewPxIDe_Y1iPU", + "focus": 0.1778488908101812, + "gap": 1 + }, + "endBinding": { + "elementId": "dKqPKHU31_0TbCfsfGz-g", + "focus": -0.3320182115892062, + "gap": 3.440603675731495 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 61.325488367487196, + 62.66782764345396 + ], + [ + 262.3254883674872, + 75.11002245068335 + ] + ] + }, + { + "type": "text", + "version": 34, + "versionNonce": 389880372, + "index": "b0m", + "isDeleted": false, + "id": "hmfLbl4pt1lgartKiE3i0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 550.4878035738304, + "y": 902.708251953125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 18.511978149414062, + "height": 20, + "seed": 343591688, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888248336, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:N", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "AKspao2VjkV_4HoqeUDxG", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/snapshot-manager-2.svg b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-2.svg new file mode 100644 index 00000000000..3a89ea9a111 --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-2.svg @@ -0,0 +1,21 @@ + + + + + + + + TimetableSnapshotMapTripPattern -> TimetableTripTimesarrivalTimes per stopdepartureTimes per stopTimetable1:N1:NLIVE SNAPSHOTBUFFERTimetableSnapshotManagerList<TripTimes>TripTimetableSnapshot'MapTripPattern -> Timetable1:N \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/snapshot-manager-3.excalidraw b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-3.excalidraw new file mode 100644 index 00000000000..1026936f048 --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-3.excalidraw @@ -0,0 +1,2225 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 1780, + "versionNonce": 395737612, + "index": "Zt", + "isDeleted": false, + "id": "zekmasGhgHti0R9pLx3jA", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 921.7437926485378, + "y": 1432.8994140625, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 2129910136, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "PxZEnjZZUqHL8P5TQ-eyD", + "type": "text" + } + ], + "updated": 1721888286561, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1658, + "versionNonce": 2147470220, + "index": "Zu", + "isDeleted": false, + "id": "PxZEnjZZUqHL8P5TQ-eyD", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 963.5238448335964, + "y": 1437.8994140625, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 117.93989562988281, + "height": 25, + "seed": 522840696, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286561, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable C", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "zekmasGhgHti0R9pLx3jA", + "originalText": "Timetable C", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1806, + "versionNonce": 1912892172, + "index": "Zv", + "isDeleted": false, + "id": "lr9C2XJmEVc59-xhDW9EB", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 900.9937926485377, + "y": 1454.3994140625, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 2050963832, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "kZT_wfvqg4b1nvpWgAKYg", + "type": "text" + }, + { + "id": "5Jrn7U7PAjx0l4RnY_b8n", + "type": "arrow" + } + ], + "updated": 1721888286561, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1666, + "versionNonce": 357783692, + "index": "Zw", + "isDeleted": false, + "id": "kZT_wfvqg4b1nvpWgAKYg", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 941.9438430025416, + "y": 1459.3994140625, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 119.59989929199219, + "height": 25, + "seed": 1510558328, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286561, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable B", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "lr9C2XJmEVc59-xhDW9EB", + "originalText": "Timetable B", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1217, + "versionNonce": 895813900, + "index": "b0t", + "isDeleted": false, + "id": "wiMJAzG4KtMhmFx_r6m0P", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 690.589094486403, + "y": 1307.392263468355, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 238.75, + "height": 101.25000000000001, + "seed": 1369468680, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "vTZrbvR9khR3XWa7ph_tQ", + "type": "arrow" + }, + { + "id": "KrSIu95bhPRB9XOPMnweX", + "type": "text" + }, + { + "id": "dd7xetQVtA4L96sHmiqVQ", + "type": "arrow" + } + ], + "updated": 1721888286564, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1187, + "versionNonce": 719824268, + "index": "b0u", + "isDeleted": false, + "id": "KrSIu95bhPRB9XOPMnweX", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 718.5241835977312, + "y": 1312.392263468355, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 182.87982177734375, + "height": 25, + "seed": 416108040, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286561, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshot", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "wiMJAzG4KtMhmFx_r6m0P", + "originalText": "TimetableSnapshot", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 822, + "versionNonce": 1610458164, + "index": "b0v", + "isDeleted": false, + "id": "zSkHhyaA5aF_mKIjZKHH6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 705.0234908106717, + "y": 1346.2244358249009, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 212.0000000000001, + "height": 50, + "seed": 1158040840, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "lzwMVFwY7PloNSridSKNv", + "type": "text" + }, + { + "id": "vTZrbvR9khR3XWa7ph_tQ", + "type": "arrow" + } + ], + "updated": 1721888286561, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 707, + "versionNonce": 931967628, + "index": "b0w", + "isDeleted": false, + "id": "lzwMVFwY7PloNSridSKNv", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 711.0475539210233, + "y": 1351.2244358249009, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 199.95187377929688, + "height": 40, + "seed": 533163016, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286561, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "zSkHhyaA5aF_mKIjZKHH6", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2022, + "versionNonce": 826532876, + "index": "b0x", + "isDeleted": false, + "id": "jKSrqDsfdMTp8P14XycHY", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 958.5890944864032, + "y": 1571.987091054562, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 159.99999999999994, + "height": 35, + "seed": 1661774600, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "UtsGqq8cQxWqmsew7lWF1", + "type": "text" + }, + { + "id": "jM3qNJlg03ePJgXCW4r1p", + "type": "arrow" + }, + { + "id": "teompcNiR-rpNaF3fKpQU", + "type": "arrow" + }, + { + "id": "NDE341Z9r4ykYtVdukw7L", + "type": "arrow" + }, + { + "id": "iL8WAoqHJYuouYNfXfgds", + "type": "arrow" + }, + { + "id": "6zJk2gV3txR3FXkAwcP-y", + "type": "arrow" + } + ], + "updated": 1721888286561, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1963, + "versionNonce": 1041399692, + "index": "b0y", + "isDeleted": false, + "id": "UtsGqq8cQxWqmsew7lWF1", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 991.6691497232196, + "y": 1576.987091054562, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 93.83988952636719, + "height": 25, + "seed": 693242376, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286561, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "jKSrqDsfdMTp8P14XycHY", + "originalText": "TripTimes", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1795, + "versionNonce": 700108556, + "index": "b0z", + "isDeleted": false, + "id": "TPLHRNmYiciGrRVy6PVGG", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1035.5890944864032, + "y": 1619.8863295475148, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 277.5, + "height": 35, + "seed": 1569949960, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "d2Zo3f0Y77nsmeNZJ765b", + "type": "text" + }, + { + "id": "teompcNiR-rpNaF3fKpQU", + "type": "arrow" + } + ], + "updated": 1721888286561, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1508, + "versionNonce": 1296632972, + "index": "b10", + "isDeleted": false, + "id": "d2Zo3f0Y77nsmeNZJ765b", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1068.6391967202899, + "y": 1624.8863295475148, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 211.39979553222656, + "height": 25, + "seed": 726476808, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286561, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "arrivalTimes per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "TPLHRNmYiciGrRVy6PVGG", + "originalText": "arrivalTimes per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1854, + "versionNonce": 1338211340, + "index": "b11", + "isDeleted": false, + "id": "EGlDFPnP-CzfLx4uEmhoW", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1036.9406569864032, + "y": 1661.142263468355, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 275.66406249999994, + "height": 35, + "seed": 1853548296, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "teompcNiR-rpNaF3fKpQU", + "type": "arrow" + }, + { + "id": "VYnTwO4q63BTlCSC4ZYb4", + "type": "text" + }, + { + "id": "NDE341Z9r4ykYtVdukw7L", + "type": "arrow" + } + ], + "updated": 1721888286561, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1461, + "versionNonce": 22924, + "index": "b12", + "isDeleted": false, + "id": "VYnTwO4q63BTlCSC4ZYb4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1052.9028151895282, + "y": 1666.142263468355, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 243.73974609375, + "height": 25, + "seed": 597214728, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286561, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "departureTimes per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "EGlDFPnP-CzfLx4uEmhoW", + "originalText": "departureTimes per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1527, + "versionNonce": 1665460492, + "index": "b13", + "isDeleted": false, + "id": "mFeSYM850yq688DVwI6Ud", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 879.3390944864032, + "y": 1475.392263468355, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 1470285064, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "ZBp0grrycUfLRuys3ZAyQ", + "type": "text" + }, + { + "id": "vTZrbvR9khR3XWa7ph_tQ", + "type": "arrow" + } + ], + "updated": 1721888286561, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1405, + "versionNonce": 2097702540, + "index": "b14", + "isDeleted": false, + "id": "ZBp0grrycUfLRuys3ZAyQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 920.9991439248797, + "y": 1480.392263468355, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 118.17990112304688, + "height": 25, + "seed": 404022280, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286561, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable A", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "mFeSYM850yq688DVwI6Ud", + "originalText": "Timetable A", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 7817, + "versionNonce": 111785396, + "index": "b15", + "isDeleted": false, + "id": "jM3qNJlg03ePJgXCW4r1p", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 912.3383509831797, + "y": 1550.9744358249009, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 41.7886733093801, + "height": 39.72883533414779, + "seed": 1659577096, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "XoszHSAJOO7VdMp51YxnJ" + } + ], + "updated": 1721888286578, + "link": null, + "locked": false, + "startBinding": { + "elementId": "BffK2tmk1E94Y_nj_LdaL", + "focus": 0.7878884036700791, + "gap": 1 + }, + "endBinding": { + "elementId": "jKSrqDsfdMTp8P14XycHY", + "focus": -0.5433167343314657, + "gap": 4.462070193843374 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 7.297403081814082, + 32.75274556854151 + ], + [ + 41.7886733093801, + 39.72883533414779 + ] + ] + }, + { + "type": "text", + "version": 62, + "versionNonce": 365467148, + "index": "b16", + "isDeleted": false, + "id": "XoszHSAJOO7VdMp51YxnJ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 910.3797649902867, + "y": 1573.7271813934424, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 18.511978149414062, + "height": 20, + "seed": 379683336, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286560, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:N", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "jM3qNJlg03ePJgXCW4r1p", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 6447, + "versionNonce": 380705076, + "index": "b17", + "isDeleted": false, + "id": "teompcNiR-rpNaF3fKpQU", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 972.0704413585929, + "y": 1609.987091054562, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 62.51865312781024, + "height": 29.20654322532164, + "seed": 1647575304, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721888286578, + "link": null, + "locked": false, + "startBinding": { + "elementId": "jKSrqDsfdMTp8P14XycHY", + "focus": 0.8764519102761019, + "gap": 3 + }, + "endBinding": { + "elementId": "TPLHRNmYiciGrRVy6PVGG", + "focus": -0.48926578214451166, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 17.398653127810462, + 24.965517241379303 + ], + [ + 62.51865312781024, + 29.20654322532164 + ] + ] + }, + { + "type": "arrow", + "version": 6493, + "versionNonce": 1763693236, + "index": "b18", + "isDeleted": false, + "id": "NDE341Z9r4ykYtVdukw7L", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 970.2952216813916, + "y": 1609.6250220890447, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 63.645435305011574, + "height": 70.5544507985046, + "seed": 891199496, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721888286578, + "link": null, + "locked": false, + "startBinding": { + "elementId": "jKSrqDsfdMTp8P14XycHY", + "focus": 0.8643694949208651, + "gap": 2.63793103448279 + }, + "endBinding": { + "elementId": "EGlDFPnP-CzfLx4uEmhoW", + "focus": -0.7418501122386243, + "gap": 3 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 9.293872805011688, + 54.431034482758605 + ], + [ + 63.645435305011574, + 70.5544507985046 + ] + ] + }, + { + "type": "arrow", + "version": 5959, + "versionNonce": 1081826356, + "index": "b19", + "isDeleted": false, + "id": "vTZrbvR9khR3XWa7ph_tQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 772.1853988716041, + "y": 1397.2244358249009, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 102.15369561479929, + "height": 117.21041353830856, + "seed": 794648328, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "6SnawDtDtYNny8VpbVhlf" + } + ], + "updated": 1721888286578, + "link": null, + "locked": false, + "startBinding": { + "elementId": "zSkHhyaA5aF_mKIjZKHH6", + "focus": 0.3890582261519381, + "gap": 1 + }, + "endBinding": { + "elementId": "mFeSYM850yq688DVwI6Ud", + "focus": -0.28923171675536635, + "gap": 4.999999999999886 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 14.785416969799371, + 100.16782764345407 + ], + [ + 102.15369561479929, + 117.21041353830856 + ] + ] + }, + { + "type": "text", + "version": 17, + "versionNonce": 1783333644, + "index": "b19V", + "isDeleted": false, + "id": "6SnawDtDtYNny8VpbVhlf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 777.7148267666964, + "y": 1487.392263468355, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 18.511978149414062, + "height": 20, + "seed": 620016648, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286560, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:N", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "vTZrbvR9khR3XWa7ph_tQ", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 554, + "versionNonce": 1416299444, + "index": "b1B", + "isDeleted": false, + "id": "dKxiAREyn4qzIfEZOz7tN", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 668.4578871349407, + "y": 1261.208181437105, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 391.2662543877959, + "seed": 742866184, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721888286560, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 391.2662543877959 + ] + ] + }, + { + "type": "text", + "version": 294, + "versionNonce": 1048527244, + "index": "b1C", + "isDeleted": false, + "id": "bEt14uqWsbunz4gMAnI2H", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 757.4578871349407, + "y": 1271.208181437105, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 129.48800659179688, + "height": 20, + "seed": 1365924872, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286560, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "LIVE SNAPSHOT", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "LIVE SNAPSHOT", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 310, + "versionNonce": 1563979060, + "index": "b1D", + "isDeleted": false, + "id": "g4i5F3MBQQORnCh_CgjnZ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 464.45788713494073, + "y": 1269.208181437105, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 63.05599594116211, + "height": 20, + "seed": 1121032968, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286560, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "BUFFER", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "BUFFER", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 321, + "versionNonce": 45559820, + "index": "b1E", + "isDeleted": false, + "id": "47UB09_AFzSgFQ9GYQ6CJ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 563.4578871349407, + "y": 1225.208181437105, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 211.0240020751953, + "height": 20, + "seed": 586742280, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286560, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "TimetableSnapshotManager", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "TimetableSnapshotManager", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1191, + "versionNonce": 1829705228, + "index": "b1F", + "isDeleted": false, + "id": "BffK2tmk1E94Y_nj_LdaL", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 892.3984908106718, + "y": 1509.9744358249009, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 1692170504, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "jM3qNJlg03ePJgXCW4r1p", + "type": "arrow" + }, + { + "id": "ov_KHrAaNJVaOP7TqjDJB", + "type": "text" + } + ], + "updated": 1721888286561, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1163, + "versionNonce": 1599858572, + "index": "b1G", + "isDeleted": false, + "id": "ov_KHrAaNJVaOP7TqjDJB", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 919.2345351221953, + "y": 1519.9744358249009, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.32791137695312, + "height": 20, + "seed": 1006114824, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286561, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "BffK2tmk1E94Y_nj_LdaL", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 637, + "versionNonce": 1119727628, + "index": "b1H", + "isDeleted": false, + "id": "uuXDTB3aIY3WbOoH17mx2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1153.3984908106718, + "y": 1571.2244358249009, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 147.5, + "height": 35, + "seed": 206034696, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "J17XHvkYNZ8sT5-X1wdlI", + "type": "text" + }, + { + "id": "iL8WAoqHJYuouYNfXfgds", + "type": "arrow" + } + ], + "updated": 1721888286561, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 572, + "versionNonce": 1767404684, + "index": "b1I", + "isDeleted": false, + "id": "J17XHvkYNZ8sT5-X1wdlI", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1207.6885146143827, + "y": 1576.2244358249009, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 38.919952392578125, + "height": 25, + "seed": 1514649096, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286561, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Trip", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "uuXDTB3aIY3WbOoH17mx2", + "originalText": "Trip", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 1910, + "versionNonce": 2026954548, + "index": "b1J", + "isDeleted": false, + "id": "iL8WAoqHJYuouYNfXfgds", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1119.6484908106718, + "y": 1591.2244358249009, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 32.75, + "height": 0, + "seed": 989050120, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721888286579, + "link": null, + "locked": false, + "startBinding": { + "elementId": "jKSrqDsfdMTp8P14XycHY", + "focus": 0.09927684401936858, + "gap": 1.0593963242686186 + }, + "endBinding": { + "elementId": "uuXDTB3aIY3WbOoH17mx2", + "focus": -0.14285714285714285, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 32.75, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 1559, + "versionNonce": 1547836684, + "index": "b1X", + "isDeleted": false, + "id": "h14QaZ6zGvH8BAR514q3P", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 260.9937926485376, + "y": 1314.1494140625, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 238.75, + "height": 101.25000000000001, + "seed": 435492728, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "hcKGpHSoayZ0PDIToVJf6", + "type": "arrow" + }, + { + "id": "vqg8ANjfl-sDl3lOUJGbn", + "type": "text" + } + ], + "updated": 1721888286561, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1536, + "versionNonce": 35901068, + "index": "b1Y", + "isDeleted": false, + "id": "vqg8ANjfl-sDl3lOUJGbn", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 286.26888572715086, + "y": 1319.1494140625, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 188.19981384277344, + "height": 25, + "seed": 55797880, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286561, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshot'", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "h14QaZ6zGvH8BAR514q3P", + "originalText": "TimetableSnapshot'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1170, + "versionNonce": 1707803188, + "index": "b1Z", + "isDeleted": false, + "id": "ZCrLLiTG39xsQ1RLIg_A0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 275.4281889728062, + "y": 1352.981586419046, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 212.0000000000001, + "height": 50, + "seed": 784308600, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "zTvBObNQlbQ5c6r63S-XK", + "type": "text" + }, + { + "id": "hcKGpHSoayZ0PDIToVJf6", + "type": "arrow" + }, + { + "id": "5Jrn7U7PAjx0l4RnY_b8n", + "type": "arrow" + } + ], + "updated": 1721888286561, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1043, + "versionNonce": 714251148, + "index": "b1a", + "isDeleted": false, + "id": "zTvBObNQlbQ5c6r63S-XK", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 281.4522520831578, + "y": 1357.981586419046, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 199.95187377929688, + "height": 40, + "seed": 1602071160, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286561, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ZCrLLiTG39xsQ1RLIg_A0", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1695, + "versionNonce": 1573605644, + "index": "b1h", + "isDeleted": false, + "id": "EnHgcF1fJ24fms56PPyzz", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 430.7437926485376, + "y": 1484.1494140625, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 586457464, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "rM-wga-gBsD3p_dhWD4Je", + "type": "text" + }, + { + "id": "hcKGpHSoayZ0PDIToVJf6", + "type": "arrow" + } + ], + "updated": 1721888286561, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1575, + "versionNonce": 896673932, + "index": "b1i", + "isDeleted": false, + "id": "rM-wga-gBsD3p_dhWD4Je", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 469.7438460542993, + "y": 1489.1494140625, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.49989318847656, + "height": 25, + "seed": 399618680, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286561, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable A'", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "EnHgcF1fJ24fms56PPyzz", + "originalText": "Timetable A'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 8581, + "versionNonce": 1641915700, + "index": "b1j", + "isDeleted": false, + "id": "6zJk2gV3txR3FXkAwcP-y", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 533.7430491453146, + "y": 1559.731586419046, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 420.53867330937953, + "height": 47.22883533414824, + "seed": 1894032248, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "zhZY5a5E83Xir1ZP5VSY9" + } + ], + "updated": 1721888286579, + "link": null, + "locked": false, + "startBinding": { + "elementId": "zehtgWNT-YoG49WS7nY-H", + "focus": 0.23908382311363352, + "gap": 1 + }, + "endBinding": { + "elementId": "jKSrqDsfdMTp8P14XycHY", + "focus": -0.9989878615000334, + "gap": 4.307372031709065 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 64.79740308181363, + 46.50274556854151 + ], + [ + 420.53867330937953, + 47.22883533414824 + ] + ] + }, + { + "type": "text", + "version": 67, + "versionNonce": 790958004, + "index": "b1k", + "isDeleted": false, + "id": "zhZY5a5E83Xir1ZP5VSY9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 589.2844631524212, + "y": 1596.2343319875874, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 18.511978149414062, + "height": 20, + "seed": 1819374712, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286560, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:N", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "6zJk2gV3txR3FXkAwcP-y", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 7155, + "versionNonce": 324484020, + "index": "b1n", + "isDeleted": false, + "id": "hcKGpHSoayZ0PDIToVJf6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 348.11395156352387, + "y": 1403.981586419046, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 77.6298410850136, + "height": 118.61521036093359, + "seed": 1562060664, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721888286579, + "link": null, + "locked": false, + "startBinding": { + "elementId": "ZCrLLiTG39xsQ1RLIg_A0", + "focus": 0.3553760606679721, + "gap": 1 + }, + "endBinding": { + "elementId": "EnHgcF1fJ24fms56PPyzz", + "focus": -0.5046436094648972, + "gap": 5.000000000000114 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 24.091331613050748, + 94.66782764345407 + ], + [ + 77.6298410850136, + 118.61521036093359 + ] + ] + }, + { + "type": "rectangle", + "version": 1363, + "versionNonce": 1998290444, + "index": "b1o", + "isDeleted": false, + "id": "zehtgWNT-YoG49WS7nY-H", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 443.8031889728062, + "y": 1518.731586419046, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 389511288, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "6zJk2gV3txR3FXkAwcP-y", + "type": "arrow" + }, + { + "id": "TZPWlXHQbYuVQqoQ561Go", + "type": "text" + } + ], + "updated": 1721888286561, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1333, + "versionNonce": 215306124, + "index": "b1p", + "isDeleted": false, + "id": "TZPWlXHQbYuVQqoQ561Go", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 470.63923328432975, + "y": 1528.731586419046, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.32791137695312, + "height": 20, + "seed": 1268885880, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286561, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "zehtgWNT-YoG49WS7nY-H", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 8558, + "versionNonce": 98673204, + "index": "b1t", + "isDeleted": false, + "id": "5Jrn7U7PAjx0l4RnY_b8n", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 346.9340189599316, + "y": 1403.981586419046, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 556.871530704148, + "height": 48.95136663695985, + "seed": 877125496, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "K1sSrOQALh-emgix3QKk7" + } + ], + "updated": 1721888286579, + "link": null, + "locked": false, + "startBinding": { + "elementId": "ZCrLLiTG39xsQ1RLIg_A0", + "focus": 0.5972902612040693, + "gap": 1 + }, + "endBinding": { + "elementId": "lr9C2XJmEVc59-xhDW9EB", + "focus": 0.9752564351058418, + "gap": 1.466461006494228 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 112.70913042812123, + 43.285789141170426 + ], + [ + 556.871530704148, + 48.95136663695985 + ] + ] + }, + { + "type": "text", + "version": 28, + "versionNonce": 755827340, + "index": "b1u", + "isDeleted": false, + "id": "K1sSrOQALh-emgix3QKk7", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 438.9071722152013, + "y": 1437.2673755602164, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 41.471954345703125, + "height": 20, + "seed": 584667912, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286560, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:(N-1)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "5Jrn7U7PAjx0l4RnY_b8n", + "originalText": "1:(N-1)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1457, + "versionNonce": 1796461836, + "index": "b9o", + "isDeleted": false, + "id": "AGYl0olO1B8sZ96X8LK9p", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1476.1467906191187, + "y": 1223.124959309896, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 313.00000000000006, + "height": 94.9999999999999, + "seed": 1199296376, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "NidGvjZk1s-ZZLkijRyLz", + "type": "text" + } + ], + "updated": 1721888286563, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1390, + "versionNonce": 919373452, + "index": "b9p", + "isDeleted": false, + "id": "NidGvjZk1s-ZZLkijRyLz", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1596.3508158876734, + "y": 1228.124959309896, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 72.59194946289062, + "height": 20, + "seed": 57278728, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286563, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Requests", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "AGYl0olO1B8sZ96X8LK9p", + "originalText": "Requests", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1489, + "versionNonce": 1479246900, + "index": "b9q", + "isDeleted": false, + "id": "lelKIpf1rrQBxHTYg2lnd", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1685.1467906191187, + "y": 1257.124959309896, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.05769230769234, + "height": 50, + "seed": 790703624, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "t31n4VPNGGbaLVLqRzRMZ", + "type": "text" + }, + { + "id": "dd7xetQVtA4L96sHmiqVQ", + "type": "arrow" + } + ], + "updated": 1721888286564, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1436, + "versionNonce": 1718440844, + "index": "b9r", + "isDeleted": false, + "id": "t31n4VPNGGbaLVLqRzRMZ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1696.9196553276524, + "y": 1262.124959309896, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 68.511962890625, + "height": 40, + "seed": 1478197368, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286563, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Thread 1\nRouting", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "lelKIpf1rrQBxHTYg2lnd", + "originalText": "Thread 1\nRouting", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1481, + "versionNonce": 619376396, + "index": "b9s", + "isDeleted": false, + "id": "MZUkG198b4DAk6bj-p2CZ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1587.1467906191187, + "y": 1257.124959309896, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.00000000000006, + "height": 50, + "seed": 1723566856, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "-Rrypb1RhXLta9Lm3wX9o", + "type": "text" + } + ], + "updated": 1721888286563, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1451, + "versionNonce": 1409212556, + "index": "b9t", + "isDeleted": false, + "id": "-Rrypb1RhXLta9Lm3wX9o", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1595.3628092958766, + "y": 1262.124959309896, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 75.56796264648438, + "height": 40, + "seed": 337963528, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286563, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Thread 2\nIdle", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "MZUkG198b4DAk6bj-p2CZ", + "originalText": "Thread 2\nIdle", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1676, + "versionNonce": 359373836, + "index": "bA1", + "isDeleted": false, + "id": "npPKpy-OvN3MRBf8c2as6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1489.1467906191187, + "y": 1257.124959309896, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.08333333333327, + "height": 50, + "seed": 2088231432, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "HyzW-9-aC-fuq8FUyJTSC", + "type": "text" + } + ], + "updated": 1721888286564, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1648, + "versionNonce": 1246834060, + "index": "bA2", + "isDeleted": false, + "id": "HyzW-9-aC-fuq8FUyJTSC", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1497.6524770611761, + "y": 1262.124959309896, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 75.07196044921875, + "height": 40, + "seed": 2118017288, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721888286564, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Thread 3\nIdle", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "npPKpy-OvN3MRBf8c2as6", + "originalText": "Thread 3\nIdle", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 615, + "versionNonce": 847117108, + "index": "bA4", + "isDeleted": false, + "id": "dd7xetQVtA4L96sHmiqVQ", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1731.9528424343757, + "y": 1308.124959309896, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 795.806051815257, + "height": 52.946533203125, + "seed": 936949624, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721888286586, + "link": null, + "locked": false, + "startBinding": { + "elementId": "lelKIpf1rrQBxHTYg2lnd", + "focus": -0.6955407031059301, + "gap": 1 + }, + "endBinding": { + "elementId": "wiMJAzG4KtMhmFx_r6m0P", + "focus": 0.06796460894051319, + "gap": 6.807696132715591 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -184.80605181525698, + 50.946533203125 + ], + [ + -795.806051815257, + 52.946533203125 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/snapshot-manager-3.svg b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-3.svg new file mode 100644 index 00000000000..9e166cc9d34 --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-3.svg @@ -0,0 +1,21 @@ + + + + + + + + Timetable CTimetable BTimetableSnapshotMapTripPattern -> TimetableTripTimesarrivalTimes per stopdepartureTimes per stopTimetable A1:N1:NLIVE SNAPSHOTBUFFERTimetableSnapshotManagerList<TripTimes>TripTimetableSnapshot'MapTripPattern -> TimetableTimetable A'1:NList<TripTimes>1:(N-1)RequestsThread 1RoutingThread 2IdleThread 3Idle \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/snapshot-manager-4.excalidraw b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-4.excalidraw new file mode 100644 index 00000000000..56eea6069db --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-4.excalidraw @@ -0,0 +1,3101 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "arrow", + "version": 9111, + "versionNonce": 258668511, + "index": "ZY", + "isDeleted": false, + "id": "OdtlN980i1fHmWKYQWJLF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 330.6355575157419, + "y": 2246.314117384496, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 705.1082351327954, + "height": 58.45654322532209, + "seed": 791331704, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "startBinding": { + "elementId": "dw4dEcACUQZwCTettMOSN", + "focus": 0.6836388927982713, + "gap": 1 + }, + "endBinding": { + "elementId": "A-UacdCIKFnewm8U5_M4S", + "focus": -0.25981865015084876, + "gap": 2.2976509189334138 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 147.48823513279558, + 39.21551724137953 + ], + [ + 705.1082351327954, + 58.45654322532209 + ] + ] + }, + { + "type": "arrow", + "version": 8206, + "versionNonce": 717765009, + "index": "ZZ", + "isDeleted": false, + "id": "C5k7bA8B6Js0Bi-l_Ockl", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 326.2783953898195, + "y": 2246.314117384496, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 46.038114911224, + "height": 84.78461886476316, + "seed": 716786296, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "startBinding": { + "elementId": "dw4dEcACUQZwCTettMOSN", + "focus": 0.4384955575098758, + "gap": 1 + }, + "endBinding": { + "elementId": "w2bj0dnGVZzTU8w42eIRs", + "focus": -0.5900275270581303, + "gap": 2.845251097494099 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 4.18655241122417, + 73.78947439428521 + ], + [ + 46.038114911224, + 84.78461886476316 + ] + ] + }, + { + "type": "rectangle", + "version": 2910, + "versionNonce": 1881056255, + "index": "Za", + "isDeleted": false, + "id": "dw4dEcACUQZwCTettMOSN", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 280.7437926485376, + "y": 2210.314117384496, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 159.99999999999994, + "height": 35, + "seed": 460998008, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "zlyKnkkYr97YPszNokitg" + }, + { + "id": "OdtlN980i1fHmWKYQWJLF", + "type": "arrow" + }, + { + "id": "b97ko1FCuCFCDKQUcVUF9", + "type": "arrow" + }, + { + "id": "rOHmBHeGUMpy0gYCtfBKw", + "type": "arrow" + }, + { + "id": "C5k7bA8B6Js0Bi-l_Ockl", + "type": "arrow" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2779, + "versionNonce": 562880369, + "index": "Zb", + "isDeleted": false, + "id": "zlyKnkkYr97YPszNokitg", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 296.8937903597192, + "y": 2215.314117384496, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 127.70000457763672, + "height": 25, + "seed": 939710072, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes A1'", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "dw4dEcACUQZwCTettMOSN", + "originalText": "TripTimes A1'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 4956, + "versionNonce": 835177503, + "index": "Ze", + "isDeleted": false, + "id": "b97ko1FCuCFCDKQUcVUF9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 441.8031889728062, + "y": 2231.781696073036, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 713.7115902136746, + "height": 29.978679903526427, + "seed": 1724893560, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "startBinding": { + "elementId": "dw4dEcACUQZwCTettMOSN", + "focus": -0.005352539817178172, + "gap": 1.059396324268647 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 595.1906036757315, + 29.978679903526427 + ], + [ + 713.7115902136746, + 16.845099814068817 + ] + ] + }, + { + "type": "rectangle", + "version": 1874, + "versionNonce": 370414207, + "index": "b2d", + "isDeleted": false, + "id": "3RVXGI7B4IHSFGdyza6wy", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 915.4461417296051, + "y": 2016.85993379727, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 993329672, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "GBPyDgVlq2s1Vmwe2g7vW" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1752, + "versionNonce": 315332849, + "index": "b2e", + "isDeleted": false, + "id": "GBPyDgVlq2s1Vmwe2g7vW", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 957.226140508902, + "y": 2021.85993379727, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 117.94000244140625, + "height": 25, + "seed": 676823304, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable C", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "3RVXGI7B4IHSFGdyza6wy", + "originalText": "Timetable C", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1902, + "versionNonce": 67857055, + "index": "b2f", + "isDeleted": false, + "id": "i0loKWTsoAh_HSKYq1r0w", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 894.6961417296051, + "y": 2038.35993379727, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 1792155656, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "zn1uFo33ehgdjQdFl_b5Z" + }, + { + "id": "urTZ263bvn2WOwPokUWC-", + "type": "arrow" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1760, + "versionNonce": 615338705, + "index": "b2g", + "isDeleted": false, + "id": "zn1uFo33ehgdjQdFl_b5Z", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 935.6461386778473, + "y": 2043.35993379727, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 119.60000610351562, + "height": 25, + "seed": 1654710024, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable B", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "i0loKWTsoAh_HSKYq1r0w", + "originalText": "Timetable B", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1317, + "versionNonce": 1168293567, + "index": "b2h", + "isDeleted": false, + "id": "7eC27NbG97v9egCaffOTt", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 684.2914435674701, + "y": 1891.352783203125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 238.75, + "height": 101.25000000000001, + "seed": 272612872, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "5mWIjOk63J1oZkDI7JTy3" + }, + { + "id": "hOVFUuqrhihj4pkhsZJyW", + "type": "arrow" + }, + { + "id": "1sFSiCO0wwcJO7v_sKatX", + "type": "arrow" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1281, + "versionNonce": 1778176177, + "index": "b2i", + "isDeleted": false, + "id": "5mWIjOk63J1oZkDI7JTy3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 712.2264411260638, + "y": 1896.352783203125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 182.8800048828125, + "height": 25, + "seed": 78298376, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshot", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "7eC27NbG97v9egCaffOTt", + "originalText": "TimetableSnapshot", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 918, + "versionNonce": 1568313055, + "index": "b2j", + "isDeleted": false, + "id": "zbsMPCppTMXC_7Tyqgbco", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 698.7258398917387, + "y": 1930.184955559671, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 212.0000000000001, + "height": 50, + "seed": 1365105672, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "5LHGI53ZFNHsjpC7yPo1J" + }, + { + "id": "hOVFUuqrhihj4pkhsZJyW", + "type": "arrow" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 803, + "versionNonce": 945918609, + "index": "b2k", + "isDeleted": false, + "id": "5LHGI53ZFNHsjpC7yPo1J", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 704.7498343375396, + "y": 1935.184955559671, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 199.95201110839844, + "height": 40, + "seed": 2018843400, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "zbsMPCppTMXC_7Tyqgbco", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2070, + "versionNonce": 639851263, + "index": "b2n", + "isDeleted": false, + "id": "A-UacdCIKFnewm8U5_M4S", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1038.0414435674707, + "y": 2286.346849282285, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 277.5, + "height": 35, + "seed": 1714133000, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "XNa_8ev2GyOsqSv3Ct3gH", + "type": "text" + }, + { + "id": "CaruAcm7oxSPebIKccsrS", + "type": "arrow" + }, + { + "id": "OdtlN980i1fHmWKYQWJLF", + "type": "arrow" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1768, + "versionNonce": 1150490737, + "index": "b2o", + "isDeleted": false, + "id": "XNa_8ev2GyOsqSv3Ct3gH", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1071.0914313604394, + "y": 2291.346849282285, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 211.4000244140625, + "height": 25, + "seed": 604722952, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "arrivalTimes per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "A-UacdCIKFnewm8U5_M4S", + "originalText": "arrivalTimes per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2118, + "versionNonce": 1411228447, + "index": "b2p", + "isDeleted": false, + "id": "CPe519nvZ3W0LdM67W8Yl", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1039.3930060674707, + "y": 2327.602783203125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 275.66406249999994, + "height": 35, + "seed": 765146632, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "b45SYk4gi11ikap3vr0o3", + "type": "text" + }, + { + "id": "CaruAcm7oxSPebIKccsrS", + "type": "arrow" + }, + { + "id": "dyPvTAOWBP6PwBmBBJMHH", + "type": "arrow" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1721, + "versionNonce": 1079566929, + "index": "b2q", + "isDeleted": false, + "id": "b45SYk4gi11ikap3vr0o3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1055.355026941494, + "y": 2332.602783203125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 243.74002075195312, + "height": 25, + "seed": 1473721608, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "departureTimes per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "CPe519nvZ3W0LdM67W8Yl", + "originalText": "departureTimes per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1623, + "versionNonce": 937027391, + "index": "b2r", + "isDeleted": false, + "id": "ahSKRylM1Farp4iq7fxY5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 873.0414435674705, + "y": 2059.352783203125, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 2017154056, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "aZCACzYq16O4UE1Wog8Wv" + }, + { + "id": "hOVFUuqrhihj4pkhsZJyW", + "type": "arrow" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1499, + "versionNonce": 948596785, + "index": "b2s", + "isDeleted": false, + "id": "aZCACzYq16O4UE1Wog8Wv", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 914.7014396001854, + "y": 2064.352783203125, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 118.18000793457031, + "height": 25, + "seed": 1422103304, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable A", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "ahSKRylM1Farp4iq7fxY5", + "originalText": "Timetable A", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 9135, + "versionNonce": 1069066079, + "index": "b2t", + "isDeleted": false, + "id": "G7IlC6BVJzTx6CmOeYtXO", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 906.0407000642472, + "y": 2134.934955559671, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 51.55359222391917, + "height": 95.07400072944347, + "seed": 334230024, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "aCrF7w399nyeAyq3xkphS" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false, + "startBinding": { + "elementId": "RG04rdz1KVHu_pLQ0D1im", + "focus": 0.7772933976281392, + "gap": 1 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 3.547403081813968, + 84.00274556854129 + ], + [ + 51.55359222391917, + 95.07400072944347 + ] + ] + }, + { + "type": "text", + "version": 77, + "versionNonce": 856637969, + "index": "b2u", + "isDeleted": false, + "id": "aCrF7w399nyeAyq3xkphS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 900.3321026272623, + "y": 2208.937701128212, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 18.512001037597656, + "height": 20, + "seed": 749760776, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:N", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "G7IlC6BVJzTx6CmOeYtXO", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 7819, + "versionNonce": 973759359, + "index": "b2v", + "isDeleted": false, + "id": "CaruAcm7oxSPebIKccsrS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 974.5227904396602, + "y": 2251.447610789332, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 62.51865312781001, + "height": 38.84885675576243, + "seed": 1452085256, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "A-UacdCIKFnewm8U5_M4S", + "focus": -0.4892657821445258, + "gap": 1.0000000000004547 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 17.39865312781035, + 24.965517241379303 + ], + [ + 62.51865312781001, + 38.84885675576243 + ] + ] + }, + { + "type": "arrow", + "version": 7865, + "versionNonce": 215930865, + "index": "b2w", + "isDeleted": false, + "id": "dyPvTAOWBP6PwBmBBJMHH", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 972.7475707624587, + "y": 2251.0855418238148, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 63.64543530501169, + "height": 85.17563736107877, + "seed": 2038288136, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "CPe519nvZ3W0LdM67W8Yl", + "focus": -0.741850112238638, + "gap": 3.0000000000002274 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 9.293872805011802, + 54.431034482758605 + ], + [ + 63.64543530501169, + 85.17563736107877 + ] + ] + }, + { + "type": "arrow", + "version": 6251, + "versionNonce": 1460640671, + "index": "b2x", + "isDeleted": false, + "id": "hOVFUuqrhihj4pkhsZJyW", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 765.8877479526712, + "y": 1981.184955559671, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 102.15369561479918, + "height": 117.21041353830879, + "seed": 503229960, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "0YJ7S5K5gfsakYFgU0Rdh" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false, + "startBinding": { + "elementId": "zbsMPCppTMXC_7Tyqgbco", + "focus": 0.3890582261519372, + "gap": 1 + }, + "endBinding": { + "elementId": "ahSKRylM1Farp4iq7fxY5", + "focus": -0.2892317167553744, + "gap": 5.0000000000001705 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 14.785416969799371, + 100.16782764345407 + ], + [ + 102.15369561479918, + 117.21041353830879 + ] + ] + }, + { + "type": "text", + "version": 31, + "versionNonce": 402698705, + "index": "b2y", + "isDeleted": false, + "id": "0YJ7S5K5gfsakYFgU0Rdh", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 771.4171644036718, + "y": 2071.352783203125, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 18.512001037597656, + "height": 20, + "seed": 1561046280, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:N", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "hOVFUuqrhihj4pkhsZJyW", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 688, + "versionNonce": 1794691007, + "index": "b2z", + "isDeleted": false, + "id": "J3_YyB_gMfihK0OvOQLbs", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 662.1602362160078, + "y": 1845.168701171875, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 529.0916748046875, + "seed": 1344326664, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 529.0916748046875 + ] + ] + }, + { + "type": "text", + "version": 386, + "versionNonce": 2039238577, + "index": "b30", + "isDeleted": false, + "id": "xS4FUFi5cEfx1QCTeED8c", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 751.1602362160078, + "y": 1855.168701171875, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 129.48800659179688, + "height": 20, + "seed": 20581128, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "LIVE SNAPSHOT", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "LIVE SNAPSHOT", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 402, + "versionNonce": 1877376991, + "index": "b31", + "isDeleted": false, + "id": "g2ouCwPkNehI2bpvZaGAi", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 458.1602362160078, + "y": 1853.168701171875, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 63.05599594116211, + "height": 20, + "seed": 1523804680, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "BUFFER", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "BUFFER", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 413, + "versionNonce": 1202270609, + "index": "b32", + "isDeleted": false, + "id": "tSMy0QVFxczfZRu4wlgWC", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 557.1602362160078, + "y": 1809.168701171875, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 211.0240020751953, + "height": 20, + "seed": 401139976, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "TimetableSnapshotManager", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "TimetableSnapshotManager", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1289, + "versionNonce": 1206463487, + "index": "b33", + "isDeleted": false, + "id": "RG04rdz1KVHu_pLQ0D1im", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 886.1008398917392, + "y": 2093.934955559671, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 871794696, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "wa-YT7NU77_abmqwj0Zik" + }, + { + "id": "G7IlC6BVJzTx6CmOeYtXO", + "type": "arrow" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1258, + "versionNonce": 1737414513, + "index": "b34", + "isDeleted": false, + "id": "wa-YT7NU77_abmqwj0Zik", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 912.9368422415928, + "y": 2103.934955559671, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.32799530029297, + "height": 20, + "seed": 1526220552, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "RG04rdz1KVHu_pLQ0D1im", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1733, + "versionNonce": 1057051679, + "index": "b38", + "isDeleted": false, + "id": "5HTbexaDifLg6QQVE4MDo", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 138.19614172960473, + "y": 1894.60993379727, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 238.75, + "height": 101.25000000000001, + "seed": 1975026440, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "HOknnR31Epb8Uv1iStGD2" + }, + { + "id": "oZ4RAIdeQy0evsoHtQUiw", + "type": "arrow" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1710, + "versionNonce": 730120529, + "index": "b39", + "isDeleted": false, + "id": "HOknnR31Epb8Uv1iStGD2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 163.4711356260891, + "y": 1899.60993379727, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 188.20001220703125, + "height": 25, + "seed": 683860488, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshot'", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "5HTbexaDifLg6QQVE4MDo", + "originalText": "TimetableSnapshot'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1352, + "versionNonce": 1293908031, + "index": "b3A", + "isDeleted": false, + "id": "E4c8qTtREHhjvMa2dAZ6r", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 152.63053805387335, + "y": 1933.442106153816, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 212.0000000000001, + "height": 50, + "seed": 1764319496, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "zZ_ZCosGcV_Qv1S5gDrQh" + }, + { + "id": "oZ4RAIdeQy0evsoHtQUiw", + "type": "arrow" + }, + { + "id": "urTZ263bvn2WOwPokUWC-", + "type": "arrow" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1221, + "versionNonce": 820145969, + "index": "b3B", + "isDeleted": false, + "id": "zZ_ZCosGcV_Qv1S5gDrQh", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 158.6545324996742, + "y": 1938.442106153816, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 199.95201110839844, + "height": 40, + "seed": 1986527240, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "E4c8qTtREHhjvMa2dAZ6r", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2021, + "versionNonce": 405018719, + "index": "b3C", + "isDeleted": false, + "id": "SyT2zECeAEbEsA-2FUzI_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 204.19614172960462, + "y": 2059.60993379727, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 1081786120, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "7qwgbu4CHO-tiRsdPUY7R" + }, + { + "id": "oZ4RAIdeQy0evsoHtQUiw", + "type": "arrow" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1897, + "versionNonce": 2114937105, + "index": "b3D", + "isDeleted": false, + "id": "7qwgbu4CHO-tiRsdPUY7R", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 243.19613791490735, + "y": 2064.60993379727, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.50000762939453, + "height": 25, + "seed": 1474141704, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable A'", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "SyT2zECeAEbEsA-2FUzI_", + "originalText": "Timetable A'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 10863, + "versionNonce": 903853183, + "index": "b3E", + "isDeleted": false, + "id": "Mgrr7LfiwjEnehP-pnY3L", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 239.78136048608735, + "y": 2135.192106153816, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 738.2027110496742, + "height": 61.20950151153875, + "seed": 1569496328, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "G_DkEOzzoFvELSw_MweZS" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false, + "startBinding": { + "elementId": "Tr71bKl_fTHPaUBVCYKqp", + "focus": 0.8997645215512353, + "gap": 1 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 249.96144082210822, + 55.002745568541286 + ], + [ + 738.2027110496742, + 61.20950151153875 + ] + ] + }, + { + "type": "text", + "version": 97, + "versionNonce": 1619203825, + "index": "b3F", + "isDeleted": false, + "id": "G_DkEOzzoFvELSw_MweZS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 469.0068012471604, + "y": 2180.1948517223573, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 41.47200012207031, + "height": 20, + "seed": 1929624584, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:(N-1)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Mgrr7LfiwjEnehP-pnY3L", + "originalText": "1:(N-1)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 8507, + "versionNonce": 772983967, + "index": "b3G", + "isDeleted": false, + "id": "oZ4RAIdeQy0evsoHtQUiw", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 173.17865329267988, + "y": 1984.4421061538167, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 39.788509471962925, + "height": 120.77907708693397, + "seed": 708770568, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "startBinding": { + "elementId": "E4c8qTtREHhjvMa2dAZ6r", + "focus": 0.7510091457271528, + "gap": 1.0000000000006821 + }, + "endBinding": { + "elementId": "SyT2zECeAEbEsA-2FUzI_", + "focus": -0.6585270978959544, + "gap": 3.7499999999998863 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -12.521021035038075, + 95.91782764345339 + ], + [ + 27.26748843692485, + 120.77907708693397 + ] + ] + }, + { + "type": "rectangle", + "version": 1700, + "versionNonce": 180687057, + "index": "b3H", + "isDeleted": false, + "id": "Tr71bKl_fTHPaUBVCYKqp", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 217.25553805387324, + "y": 2094.192106153816, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 1975147016, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "qhs6bnc4IDgYlQWtmo069" + }, + { + "id": "Mgrr7LfiwjEnehP-pnY3L", + "type": "arrow" + }, + { + "id": "rOHmBHeGUMpy0gYCtfBKw", + "type": "arrow" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1657, + "versionNonce": 1013833919, + "index": "b3I", + "isDeleted": false, + "id": "qhs6bnc4IDgYlQWtmo069", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 244.09154040372687, + "y": 2104.192106153816, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.32799530029297, + "height": 20, + "seed": 410114312, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Tr71bKl_fTHPaUBVCYKqp", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 9165, + "versionNonce": 1919733425, + "index": "b3J", + "isDeleted": false, + "id": "urTZ263bvn2WOwPokUWC-", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 176.06772463490375, + "y": 1984.4421061538162, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 721.4401741102342, + "height": 52.45136663695962, + "seed": 367097864, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "J_pRGhPrlmLg6Xpk77y3E" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false, + "startBinding": { + "elementId": "E4c8qTtREHhjvMa2dAZ6r", + "focus": 0.8935201352622096, + "gap": 1.0000000000002274 + }, + "endBinding": { + "elementId": "i0loKWTsoAh_HSKYq1r0w", + "focus": 1.0079890902168411, + "gap": 1.466461006494228 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 163.52777383421636, + 49.2857891411702 + ], + [ + 721.4401741102342, + 52.45136663695962 + ] + ] + }, + { + "type": "text", + "version": 42, + "versionNonce": 1385263327, + "index": "b3K", + "isDeleted": false, + "id": "J_pRGhPrlmLg6Xpk77y3E", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 318.85949840808496, + "y": 2023.7278952949864, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 41.47200012207031, + "height": 20, + "seed": 570767112, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:(N-1)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "urTZ263bvn2WOwPokUWC-", + "originalText": "1:(N-1)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 675, + "versionNonce": 1847709841, + "index": "b3c", + "isDeleted": false, + "id": "rOHmBHeGUMpy0gYCtfBKw", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 236.5550058302212, + "y": 2135.192106153816, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 40.68878681831637, + "height": 91.70301073926476, + "seed": 253100152, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "startBinding": { + "elementId": "Tr71bKl_fTHPaUBVCYKqp", + "focus": 0.7890206670033456, + "gap": 1 + }, + "endBinding": { + "elementId": "dw4dEcACUQZwCTettMOSN", + "focus": -0.730807890438654, + "gap": 3.5000000000000284 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 8.938786818316373, + 74.31826982274652 + ], + [ + 40.68878681831637, + 91.70301073926476 + ] + ] + }, + { + "type": "rectangle", + "version": 2278, + "versionNonce": 1510866175, + "index": "b3d", + "isDeleted": false, + "id": "w2bj0dnGVZzTU8w42eIRs", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 375.1617613985376, + "y": 2318.2603759765625, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 264.41406249999994, + "height": 35, + "seed": 271234824, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "xpoy9uPV84uPJXXzR8-CE", + "type": "text" + }, + { + "id": "C5k7bA8B6Js0Bi-l_Ockl", + "type": "arrow" + } + ], + "updated": 1721898004941, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1866, + "versionNonce": 328963697, + "index": "b3e", + "isDeleted": false, + "id": "xpoy9uPV84uPJXXzR8-CE", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 382.83877861045164, + "y": 2323.2603759765625, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 249.06002807617188, + "height": 25, + "seed": 516855304, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004941, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "departureTimes' per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "w2bj0dnGVZzTU8w42eIRs", + "originalText": "departureTimes' per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 3199, + "versionNonce": 1723529023, + "index": "b9M", + "isDeleted": false, + "id": "hT_P4dfTkAdaoSNVrjuou", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1003.0523821613258, + "y": 2166.890441075282, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 159.99999999999994, + "height": 35, + "seed": 843640328, + "groupIds": [ + "NjqT3Z62moKDtKGIbV2O1", + "z5vlQ1PsB1juyYlkrtKK_" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "Gf-GbO02lGvYf5yzoTuQi", + "type": "text" + }, + { + "id": "hdhMDbk7048h8AThRV-R1", + "type": "arrow" + } + ], + "updated": 1721898004942, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 3109, + "versionNonce": 805462065, + "index": "b9N", + "isDeleted": false, + "id": "Gf-GbO02lGvYf5yzoTuQi", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1017.7623812457985, + "y": 2171.890441075282, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 130.5800018310547, + "height": 25, + "seed": 1846406408, + "groupIds": [ + "NjqT3Z62moKDtKGIbV2O1", + "z5vlQ1PsB1juyYlkrtKK_" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004942, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes A3", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "hT_P4dfTkAdaoSNVrjuou", + "originalText": "TripTimes A3", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1854, + "versionNonce": 267892575, + "index": "b9O", + "isDeleted": false, + "id": "PEepmGiQbZx-PTElTbvlt", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1197.861778485594, + "y": 2166.1277858456215, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 147.5, + "height": 35, + "seed": 115018760, + "groupIds": [ + "NjqT3Z62moKDtKGIbV2O1", + "z5vlQ1PsB1juyYlkrtKK_" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "0t1alcjtsXIXDN65i42AT", + "type": "text" + }, + { + "id": "hdhMDbk7048h8AThRV-R1", + "type": "arrow" + } + ], + "updated": 1721898004942, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1700, + "versionNonce": 1443443217, + "index": "b9P", + "isDeleted": false, + "id": "0t1alcjtsXIXDN65i42AT", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1233.7817766545393, + "y": 2171.1277858456215, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 75.66000366210938, + "height": 25, + "seed": 1722567432, + "groupIds": [ + "NjqT3Z62moKDtKGIbV2O1", + "z5vlQ1PsB1juyYlkrtKK_" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004942, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Trip A3", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "PEepmGiQbZx-PTElTbvlt", + "originalText": "Trip A3", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 5629, + "versionNonce": 1340568447, + "index": "b9Q", + "isDeleted": false, + "id": "hdhMDbk7048h8AThRV-R1", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1164.1117784855949, + "y": 2186.1277858456215, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 32.74999999999909, + "height": 0, + "seed": 1476241928, + "groupIds": [ + "NjqT3Z62moKDtKGIbV2O1", + "z5vlQ1PsB1juyYlkrtKK_" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898004942, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "VwpL2NcmHR7dcVs41R1ar", + "focus": 1.1857142857142857, + "gap": 3.25 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 32.74999999999909, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 3070, + "versionNonce": 454739953, + "index": "b9R", + "isDeleted": false, + "id": "_5nQZlP8PMVoXKVHKM1il", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 982.5523821613258, + "y": 2190.140441075282, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 159.99999999999994, + "height": 35, + "seed": 1976248584, + "groupIds": [ + "S6oYff1gtvbhELORnQb7a", + "z5vlQ1PsB1juyYlkrtKK_" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "LOKuqqBxniB7GLdrMl-B2", + "type": "text" + }, + { + "id": "FeqGie6RwxghciJV4TfTq", + "type": "arrow" + } + ], + "updated": 1721898004942, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2976, + "versionNonce": 1321905055, + "index": "b9S", + "isDeleted": false, + "id": "LOKuqqBxniB7GLdrMl-B2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 996.9523760578102, + "y": 2195.140441075282, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 131.20001220703125, + "height": 25, + "seed": 462473224, + "groupIds": [ + "S6oYff1gtvbhELORnQb7a", + "z5vlQ1PsB1juyYlkrtKK_" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004942, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes A2", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "_5nQZlP8PMVoXKVHKM1il", + "originalText": "TripTimes A2", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1732, + "versionNonce": 1497276881, + "index": "b9T", + "isDeleted": false, + "id": "VwpL2NcmHR7dcVs41R1ar", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1179.361778485594, + "y": 2189.3777858456215, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 147.5, + "height": 35, + "seed": 1430947592, + "groupIds": [ + "S6oYff1gtvbhELORnQb7a", + "z5vlQ1PsB1juyYlkrtKK_" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "4P8LKs1Wiyt40A_O7r3j0", + "type": "text" + }, + { + "id": "FeqGie6RwxghciJV4TfTq", + "type": "arrow" + }, + { + "id": "hdhMDbk7048h8AThRV-R1", + "type": "arrow" + } + ], + "updated": 1721898004942, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1566, + "versionNonce": 1490149311, + "index": "b9U", + "isDeleted": false, + "id": "4P8LKs1Wiyt40A_O7r3j0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1214.9717790959455, + "y": 2194.3777858456215, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 76.27999877929688, + "height": 25, + "seed": 1333028360, + "groupIds": [ + "S6oYff1gtvbhELORnQb7a", + "z5vlQ1PsB1juyYlkrtKK_" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004942, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Trip A2", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "VwpL2NcmHR7dcVs41R1ar", + "originalText": "Trip A2", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 4995, + "versionNonce": 1032601521, + "index": "b9V", + "isDeleted": false, + "id": "FeqGie6RwxghciJV4TfTq", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1143.6117784855949, + "y": 2209.3777858456215, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 34.74999999999909, + "height": 0, + "seed": 1191863560, + "groupIds": [ + "S6oYff1gtvbhELORnQb7a", + "z5vlQ1PsB1juyYlkrtKK_" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898004942, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "S7lw1FiPgRRDaA6hLZj6l", + "focus": 1.2473184567022797, + "gap": 4.32807299228989 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 34.74999999999909, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 2972, + "versionNonce": 1612550111, + "index": "b9W", + "isDeleted": false, + "id": "dQVV89V_Wif0k8gdGt0hw", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 960.5702512559258, + "y": 2214.468514067572, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 159.99999999999994, + "height": 35, + "seed": 2038989832, + "groupIds": [ + "eSZb6qPPhidWQ6yquqGQU", + "z5vlQ1PsB1juyYlkrtKK_" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "k4zVGKaAYFf-N_m3kPXbq", + "type": "text" + }, + { + "id": "zqQUgc89Zs_7_olKW3pOv", + "type": "arrow" + } + ], + "updated": 1721898004942, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2870, + "versionNonce": 1714102673, + "index": "b9X", + "isDeleted": false, + "id": "k4zVGKaAYFf-N_m3kPXbq", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 979.3802488145195, + "y": 2219.468514067572, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 122.3800048828125, + "height": 25, + "seed": 350929672, + "groupIds": [ + "eSZb6qPPhidWQ6yquqGQU", + "z5vlQ1PsB1juyYlkrtKK_" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004942, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes A1", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "dQVV89V_Wif0k8gdGt0hw", + "originalText": "TripTimes A1", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1634, + "versionNonce": 1052654591, + "index": "b9Y", + "isDeleted": false, + "id": "S7lw1FiPgRRDaA6hLZj6l", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1155.379647580194, + "y": 2213.7058588379114, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 147.5, + "height": 35, + "seed": 665657864, + "groupIds": [ + "eSZb6qPPhidWQ6yquqGQU", + "z5vlQ1PsB1juyYlkrtKK_" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "1E3iX_QCOFVXuj7XG_jql", + "type": "text" + }, + { + "id": "zqQUgc89Zs_7_olKW3pOv", + "type": "arrow" + }, + { + "id": "FeqGie6RwxghciJV4TfTq", + "type": "arrow" + } + ], + "updated": 1721898004942, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1463, + "versionNonce": 1079166833, + "index": "b9Z", + "isDeleted": false, + "id": "1E3iX_QCOFVXuj7XG_jql", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1195.3996480379576, + "y": 2218.7058588379114, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 67.45999908447266, + "height": 25, + "seed": 281780488, + "groupIds": [ + "eSZb6qPPhidWQ6yquqGQU", + "z5vlQ1PsB1juyYlkrtKK_" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004942, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Trip A1", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "S7lw1FiPgRRDaA6hLZj6l", + "originalText": "Trip A1", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 4860, + "versionNonce": 951346207, + "index": "b9a", + "isDeleted": false, + "id": "zqQUgc89Zs_7_olKW3pOv", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1121.6296475801944, + "y": 2233.7058588379114, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 32.749999999999545, + "height": 0, + "seed": 1875001352, + "groupIds": [ + "eSZb6qPPhidWQ6yquqGQU", + "z5vlQ1PsB1juyYlkrtKK_" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898004942, + "link": null, + "locked": false, + "startBinding": { + "elementId": "dQVV89V_Wif0k8gdGt0hw", + "focus": 0.09927684401939457, + "gap": 1.0593963242686186 + }, + "endBinding": { + "elementId": "S7lw1FiPgRRDaA6hLZj6l", + "focus": -0.14285714285714285, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 32.749999999999545, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 1571, + "versionNonce": 292395359, + "index": "bA5", + "isDeleted": false, + "id": "7RkCwVinxK7vnONT6qlyS", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1465.6467906191187, + "y": 1836.4326705769781, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 313.00000000000006, + "height": 94.9999999999999, + "seed": 588149768, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "f_Ultvpbd94_wKlegDZGa" + } + ], + "updated": 1721898004942, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1504, + "versionNonce": 953839633, + "index": "bA6", + "isDeleted": false, + "id": "f_Ultvpbd94_wKlegDZGa", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1585.8507929994898, + "y": 1841.4326705769781, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 72.59199523925781, + "height": 20, + "seed": 2016588552, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004942, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Requests", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "7RkCwVinxK7vnONT6qlyS", + "originalText": "Requests", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1605, + "versionNonce": 743511423, + "index": "bA7", + "isDeleted": false, + "id": "88dYStw2uKLmq2W3Irmk0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1674.6467906191187, + "y": 1870.4326705769781, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.05769230769234, + "height": 50, + "seed": 1014436360, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "4MMEjE4vWspGuBloX1dge" + }, + { + "id": "W1dUfmh1_Hj4q4dF-ZKoZ", + "type": "arrow" + } + ], + "updated": 1721898004942, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1550, + "versionNonce": 1293174257, + "index": "bA8", + "isDeleted": false, + "id": "4MMEjE4vWspGuBloX1dge", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1686.4196400688634, + "y": 1875.4326705769781, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 68.51199340820312, + "height": 40, + "seed": 1456967944, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004942, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Thread 1\nRouting", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "88dYStw2uKLmq2W3Irmk0", + "originalText": "Thread 1\nRouting", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1604, + "versionNonce": 1907363231, + "index": "bA9", + "isDeleted": false, + "id": "Al-CQM1-MRq4Ov1JcP5Zs", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1576.6467906191187, + "y": 1870.4326705769781, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.00000000000006, + "height": 50, + "seed": 1859137544, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "ateHGhzN9G9xoJfxmu516" + }, + { + "id": "1sFSiCO0wwcJO7v_sKatX", + "type": "arrow" + } + ], + "updated": 1721898004942, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1574, + "versionNonce": 1264082897, + "index": "bAA", + "isDeleted": false, + "id": "ateHGhzN9G9xoJfxmu516", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1584.8627940370875, + "y": 1875.4326705769781, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 75.5679931640625, + "height": 40, + "seed": 1549594376, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004942, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Thread 2\nGraphQL", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Al-CQM1-MRq4Ov1JcP5Zs", + "originalText": "Thread 2\nGraphQL", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1790, + "versionNonce": 2092630463, + "index": "bAB", + "isDeleted": false, + "id": "wz8FNT6NtKXiZQaiJQWxW", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1478.6467906191187, + "y": 1870.4326705769781, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.08333333333327, + "height": 50, + "seed": 402247176, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "GL5d9Yv7EbXeCA0t4lvjQ" + } + ], + "updated": 1721898004942, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1763, + "versionNonce": 29244849, + "index": "bAC", + "isDeleted": false, + "id": "GL5d9Yv7EbXeCA0t4lvjQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1487.152461802387, + "y": 1875.4326705769781, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 75.07199096679688, + "height": 40, + "seed": 715312392, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721898004942, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Thread 3\nIdle", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "wz8FNT6NtKXiZQaiJQWxW", + "originalText": "Thread 3\nIdle", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 836, + "versionNonce": 781349343, + "index": "bAD", + "isDeleted": false, + "id": "W1dUfmh1_Hj4q4dF-ZKoZ", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1721.6467906191187, + "y": 1920.3792037801031, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 796.0000000000001, + "height": 53, + "seed": 500427784, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898004942, + "link": null, + "locked": false, + "startBinding": { + "elementId": "88dYStw2uKLmq2W3Irmk0", + "focus": -0.669007785877875, + "gap": 1 + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -185, + 51 + ], + [ + -796.0000000000001, + 53 + ] + ] + }, + { + "type": "arrow", + "version": 1066, + "versionNonce": 258826129, + "index": "bAE", + "isDeleted": false, + "id": "1sFSiCO0wwcJO7v_sKatX", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1627.1467906191187, + "y": 1920.0490035847906, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 699.0000000000001, + "height": 27, + "seed": 1137341448, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721898004942, + "link": null, + "locked": false, + "startBinding": { + "elementId": "Al-CQM1-MRq4Ov1JcP5Zs", + "focus": -0.7551260813828851, + "gap": 1 + }, + "endBinding": { + "elementId": "7eC27NbG97v9egCaffOTt", + "focus": 0.10411064516606583, + "gap": 5.10534705164855 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -137, + 26 + ], + [ + -699.0000000000001, + 27 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/snapshot-manager-4.svg b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-4.svg new file mode 100644 index 00000000000..1f02b83457c --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-4.svg @@ -0,0 +1,21 @@ + + + + + + + + TripTimes A1'Timetable CTimetable BTimetableSnapshotMapTripPattern -> TimetablearrivalTimes per stopdepartureTimes per stopTimetable A1:N1:NLIVE SNAPSHOTBUFFERTimetableSnapshotManagerList<TripTimes>TimetableSnapshot'MapTripPattern -> TimetableTimetable A'1:(N-1)List<TripTimes>1:(N-1)departureTimes' per stopTripTimes A3Trip A3TripTimes A2Trip A2TripTimes A1Trip A1RequestsThread 1RoutingThread 2GraphQLThread 3Idle \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/snapshot-manager-5.excalidraw b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-5.excalidraw new file mode 100644 index 00000000000..33fff15621c --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-5.excalidraw @@ -0,0 +1,3521 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "rectangle", + "version": 3192, + "versionNonce": 451457791, + "index": "b3a", + "isDeleted": false, + "id": "ar1mB7aXA8lncKf5eY_XF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1557.3301599391036, + "y": 2871.7175352593104, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 159.99999999999994, + "height": 37, + "seed": 2081542920, + "groupIds": [ + "8pJyb4fhryOwihpBl4x8R" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "d9dOI1NxIM0Fouh1n33Vc", + "type": "text" + }, + { + "id": "eZ0c5Ysf9QTWChVOTsoL0", + "type": "arrow" + } + ], + "updated": 1722613576246, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 3154, + "versionNonce": 1802453105, + "index": "b3b", + "isDeleted": false, + "id": "d9dOI1NxIM0Fouh1n33Vc", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1576.1601541407638, + "y": 2876.7175352593104, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 122.34001159667969, + "height": 27, + "seed": 930230792, + "groupIds": [ + "8pJyb4fhryOwihpBl4x8R" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613576246, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "TripTimes A3", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ar1mB7aXA8lncKf5eY_XF", + "originalText": "TripTimes A3", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 1851, + "versionNonce": 913832735, + "index": "b3c", + "isDeleted": false, + "id": "AcChynCyyDXhUCU3iSHYi", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1752.1395562633718, + "y": 2870.9548800296498, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 147.5, + "height": 37, + "seed": 91140360, + "groupIds": [ + "8pJyb4fhryOwihpBl4x8R" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "g7iBN4NL2VTfJTkoClXUF", + "type": "text" + }, + { + "id": "eZ0c5Ysf9QTWChVOTsoL0", + "type": "arrow" + } + ], + "updated": 1722613576246, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1745, + "versionNonce": 158337617, + "index": "b3d", + "isDeleted": false, + "id": "g7iBN4NL2VTfJTkoClXUF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1792.2595551952566, + "y": 2875.9548800296498, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 67.26000213623047, + "height": 27, + "seed": 2105757704, + "groupIds": [ + "8pJyb4fhryOwihpBl4x8R" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613576246, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "Trip A3", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "AcChynCyyDXhUCU3iSHYi", + "originalText": "Trip A3", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "arrow", + "version": 5754, + "versionNonce": 220806975, + "index": "b3e", + "isDeleted": false, + "id": "eZ0c5Ysf9QTWChVOTsoL0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1718.3301599391036, + "y": 2889.837226253093, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 32.809396324268164, + "height": 0.5266348687168829, + "seed": 1739329288, + "groupIds": [ + "8pJyb4fhryOwihpBl4x8R" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722613576246, + "link": null, + "locked": false, + "startBinding": { + "elementId": "ar1mB7aXA8lncKf5eY_XF", + "focus": -0.08494028766833706, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "AcChynCyyDXhUCU3iSHYi", + "focus": -0.10713482213609353, + "gap": 1, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 32.809396324268164, + 0.5266348687168829 + ] + ] + }, + { + "type": "arrow", + "version": 9308, + "versionNonce": 1647397489, + "index": "b3f", + "isDeleted": false, + "id": "MKuD96Aw8pVxGQXFuRwma", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 887.75651115656, + "y": 2954.126911916378, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 702.9337578703769, + "height": 57.516732163336656, + "seed": 892295032, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722613347790, + "link": null, + "locked": false, + "startBinding": { + "elementId": "VvQxWw8TCkcz7ckO0RoWR", + "focus": 0.6836388927982726, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "5pL3tmkwVeXwo2Q0arxNd", + "focus": -0.25981865015084876, + "gap": 2.2976509189334138, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 145.31375787037678, + 37.21551724137953 + ], + [ + 702.9337578703769, + 57.516732163336656 + ] + ] + }, + { + "type": "arrow", + "version": 8408, + "versionNonce": 344713727, + "index": "b3g", + "isDeleted": false, + "id": "iam3gKL9dO6YvgqjfXe03", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 881.2679034869524, + "y": 2954.126911916378, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 45.9950831924906, + "height": 83.45748482745466, + "seed": 1027975288, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722613354263, + "link": null, + "locked": false, + "startBinding": { + "elementId": "VvQxWw8TCkcz7ckO0RoWR", + "focus": 0.4384955575098773, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "76I7jd1-ZH7_h7fx4pxEO", + "focus": -0.5900275270581304, + "gap": 2.845251097494156, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 4.143520692490711, + 71.78947439428521 + ], + [ + 45.9950831924906, + 83.45748482745466 + ] + ] + }, + { + "type": "rectangle", + "version": 2982, + "versionNonce": 794874961, + "index": "b3h", + "isDeleted": false, + "id": "VvQxWw8TCkcz7ckO0RoWR", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 835.6902690269371, + "y": 2916.126911916378, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 159.99999999999994, + "height": 37, + "seed": 1885985144, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "nQzM-C83PYiIG3Ls0YOVB" + }, + { + "id": "MKuD96Aw8pVxGQXFuRwma", + "type": "arrow" + }, + { + "id": "EoplLA1LGwC-cqfRjvwUo", + "type": "arrow" + }, + { + "id": "EDhzB1tAsUuNLibxHypy9", + "type": "arrow" + }, + { + "id": "iam3gKL9dO6YvgqjfXe03", + "type": "arrow" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2905, + "versionNonce": 896927313, + "index": "b3i", + "isDeleted": false, + "id": "nQzM-C83PYiIG3Ls0YOVB", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 852.2602649070641, + "y": 2921.126911916378, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 126.8600082397461, + "height": 27, + "seed": 1910499960, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "TripTimes A1'", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "VvQxWw8TCkcz7ckO0RoWR", + "originalText": "TripTimes A1'", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "arrow", + "version": 5704, + "versionNonce": 633610801, + "index": "b3j", + "isDeleted": false, + "id": "EDhzB1tAsUuNLibxHypy9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 996.7496653512057, + "y": 2938.470468303567, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 713.7115902136746, + "height": 24.10270220487746, + "seed": 1550572408, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722613347790, + "link": null, + "locked": false, + "startBinding": { + "elementId": "VvQxWw8TCkcz7ckO0RoWR", + "focus": -0.23082937402785977, + "gap": 1.0593963242686186, + "fixedPoint": null + }, + "endBinding": { + "elementId": "QBSbh4V3DQqgxLapgUPD3", + "focus": -0.5031160384805753, + "gap": 1, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 137.75812628262247, + 17.857504922145836 + ], + [ + 603.1906036757314, + 24.10270220487746 + ], + [ + 713.7115902136746, + 15.96912211541985 + ] + ] + }, + { + "type": "rectangle", + "version": 1949, + "versionNonce": 564860255, + "index": "b3u", + "isDeleted": false, + "id": "AYxZPqTMisee3fmvQLshy", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1470.3926181080046, + "y": 2722.672728329152, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 1653274232, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "AbXsRt3iX09b_DAccouZb" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1878, + "versionNonce": 160498495, + "index": "b3v", + "isDeleted": false, + "id": "AbXsRt3iX09b_DAccouZb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1515.5026187183562, + "y": 2727.672728329152, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 111.27999877929688, + "height": 27, + "seed": 466970488, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "Timetable C", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "AYxZPqTMisee3fmvQLshy", + "originalText": "Timetable C", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 1986, + "versionNonce": 1319850847, + "index": "b3w", + "isDeleted": false, + "id": "BNajgkM5FtoEJIszC5orw", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1449.6426181080046, + "y": 2744.172728329152, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 78231672, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "9eTaW_IVCMifiSl0FO4p9" + } + ], + "updated": 1722613392168, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1886, + "versionNonce": 1449333809, + "index": "b3x", + "isDeleted": false, + "id": "9eTaW_IVCMifiSl0FO4p9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1494.7126178028288, + "y": 2749.172728329152, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 111.36000061035156, + "height": 27, + "seed": 1355540856, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "Timetable B", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "BNajgkM5FtoEJIszC5orw", + "originalText": "Timetable B", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 1397, + "versionNonce": 1975421343, + "index": "b3y", + "isDeleted": false, + "id": "VNqO4dksDtEYKlYJhnKS4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1239.2379199458696, + "y": 2597.165577735007, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 238.75, + "height": 101.25000000000001, + "seed": 461372024, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "4LJIlXZr6SW8dVmbAP0zz" + }, + { + "id": "dMl4v__s-xZnmPn9Ohzg6", + "type": "arrow" + }, + { + "id": "55GVnbJXZxb8yJ4kNzHNa", + "type": "arrow" + }, + { + "id": "V1QUCRejM54ut1OvcXxl3", + "type": "arrow" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1407, + "versionNonce": 70009695, + "index": "b3z", + "isDeleted": false, + "id": "4LJIlXZr6SW8dVmbAP0zz", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1269.0529071284868, + "y": 2602.165577735007, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 179.12002563476562, + "height": 27, + "seed": 2005527416, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "TimetableSnapshot", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "VNqO4dksDtEYKlYJhnKS4", + "originalText": "TimetableSnapshot", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 1000, + "versionNonce": 1446209041, + "index": "b40", + "isDeleted": false, + "id": "Rn9FK0QG93v9wCL5DHk9_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1253.6723162701383, + "y": 2635.997750091553, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 212.0000000000001, + "height": 54, + "seed": 2133703800, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "kaKRUh5BzpQ4rGeRgGiq3" + }, + { + "id": "dMl4v__s-xZnmPn9Ohzg6", + "type": "arrow" + } + ], + "updated": 1722613354260, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 928, + "versionNonce": 1705672575, + "index": "b41", + "isDeleted": false, + "id": "kaKRUh5BzpQ4rGeRgGiq3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1269.9163195660367, + "y": 2641.397750091553, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 179.51199340820312, + "height": 43.2, + "seed": 1680829816, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Rn9FK0QG93v9wCL5DHk9_", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 2144, + "versionNonce": 640806367, + "index": "b42", + "isDeleted": false, + "id": "5pL3tmkwVeXwo2Q0arxNd", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1592.98791994587, + "y": 2992.159643814167, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 277.5, + "height": 37, + "seed": 2144534136, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "8VAIySPERVA_b3dEWp6xB", + "type": "text" + }, + { + "id": "ikFz2V85vM_31vhLCR5kb", + "type": "arrow" + }, + { + "id": "MKuD96Aw8pVxGQXFuRwma", + "type": "arrow" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1893, + "versionNonce": 2145287153, + "index": "b43", + "isDeleted": false, + "id": "8VAIySPERVA_b3dEWp6xB", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1635.9179202510459, + "y": 2997.159643814167, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 191.63999938964844, + "height": 27, + "seed": 1658613624, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "arrivalTimes per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "5pL3tmkwVeXwo2Q0arxNd", + "originalText": "arrivalTimes per stop", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 2195, + "versionNonce": 1050073599, + "index": "b44", + "isDeleted": false, + "id": "FLrcZwTZKz--7E3aowMP8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1594.33948244587, + "y": 3033.415577735007, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 275.66406249999994, + "height": 37, + "seed": 1703919736, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "aOo5JhWzJm_PY3Z_smfzg", + "type": "text" + }, + { + "id": "ikFz2V85vM_31vhLCR5kb", + "type": "arrow" + }, + { + "id": "9a8w7-EJ4i8sFTYahGokw", + "type": "arrow" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1846, + "versionNonce": 1347172255, + "index": "b45", + "isDeleted": false, + "id": "aOo5JhWzJm_PY3Z_smfzg", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1620.291501183663, + "y": 3038.415577735007, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 223.76002502441406, + "height": 27, + "seed": 934343032, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "departureTimes per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "FLrcZwTZKz--7E3aowMP8", + "originalText": "departureTimes per stop", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 1699, + "versionNonce": 801718815, + "index": "b46", + "isDeleted": false, + "id": "Cpr9-mDno9tt6bEFfYZr-", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1427.98791994587, + "y": 2765.165577735007, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 1887699576, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "75P9g-0h_17QH8SOJrcDO" + }, + { + "id": "dMl4v__s-xZnmPn9Ohzg6", + "type": "arrow" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1627, + "versionNonce": 1399576017, + "index": "b47", + "isDeleted": false, + "id": "75P9g-0h_17QH8SOJrcDO", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1472.517918725167, + "y": 2770.165577735007, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 112.44000244140625, + "height": 27, + "seed": 516748152, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "Timetable A", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "Cpr9-mDno9tt6bEFfYZr-", + "originalText": "Timetable A", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "arrow", + "version": 9414, + "versionNonce": 2058987071, + "index": "b48", + "isDeleted": false, + "id": "NtfvF1rCJP3lm6JR4sJwr", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1460.9871764426464, + "y": 2840.747750091553, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 51.553592223919395, + "height": 95.07400072944347, + "seed": 1490011256, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "TQX4OkSHE38wvxlbYRLMA" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false, + "startBinding": { + "elementId": "auDsw3sexJL_zUJDqc5a2", + "focus": 0.7772933976281416, + "gap": 1, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 3.5474030818141955, + 84.00274556854129 + ], + [ + 51.553592223919395, + 95.07400072944347 + ] + ] + }, + { + "type": "text", + "version": 136, + "versionNonce": 1245834175, + "index": "b49", + "isDeleted": false, + "id": "TQX4OkSHE38wvxlbYRLMA", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1451.9425795168313, + "y": 2913.950495660094, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 25.18400001525879, + "height": 21.6, + "seed": 1280341368, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "1:N", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "NtfvF1rCJP3lm6JR4sJwr", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "arrow", + "version": 8098, + "versionNonce": 776504927, + "index": "b4A", + "isDeleted": false, + "id": "ikFz2V85vM_31vhLCR5kb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1529.4692668180596, + "y": 2957.2604053212144, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 62.51865312781001, + "height": 39.42325545982612, + "seed": 1318258296, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722613347790, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "5pL3tmkwVeXwo2Q0arxNd", + "focus": -0.4892657821445213, + "gap": 1.0000000000004547, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 17.39865312781035, + 24.965517241379303 + ], + [ + 62.51865312781001, + 39.42325545982612 + ] + ] + }, + { + "type": "arrow", + "version": 8209, + "versionNonce": 1634203743, + "index": "b4B", + "isDeleted": false, + "id": "9a8w7-EJ4i8sFTYahGokw", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1527.6940471408582, + "y": 2956.8983363556963, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 62.64543530501169, + "height": 94.19435397928191, + "seed": 1817933688, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722613616463, + "link": null, + "locked": false, + "startBinding": { + "elementId": "8BjSVNCUwGyc7plxQpboP", + "focus": 0.8460992026521543, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "FLrcZwTZKz--7E3aowMP8", + "focus": -0.6919174980327499, + "gap": 4.000000000000227, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 12.293872805011915, + 79.4310344827586 + ], + [ + 62.64543530501169, + 94.19435397928191 + ] + ] + }, + { + "type": "arrow", + "version": 6455, + "versionNonce": 2023332255, + "index": "b4C", + "isDeleted": false, + "id": "dMl4v__s-xZnmPn9Ohzg6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1321.0777012378562, + "y": 2690.997750091553, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 101.91021870801387, + "height": 113.21041353830879, + "seed": 2045626488, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "FoMSJAlImrSKAGgN8sdg9" + } + ], + "updated": 1722613354263, + "link": null, + "locked": false, + "startBinding": { + "elementId": "Rn9FK0QG93v9wCL5DHk9_", + "focus": 0.3890582261519361, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "Cpr9-mDno9tt6bEFfYZr-", + "focus": -0.28923171675537335, + "gap": 5, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 14.541940063013953, + 96.16782764345407 + ], + [ + 101.91021870801387, + 113.21041353830879 + ] + ] + }, + { + "type": "text", + "version": 90, + "versionNonce": 1676866481, + "index": "b4D", + "isDeleted": false, + "id": "FoMSJAlImrSKAGgN8sdg9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1323.0276412932408, + "y": 2776.365577735007, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 25.18400001525879, + "height": 21.6, + "seed": 588118392, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "1:N", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "dMl4v__s-xZnmPn9Ohzg6", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "line", + "version": 759, + "versionNonce": 477346463, + "index": "b4E", + "isDeleted": false, + "id": "3W45ZCWDuaRpxKgErufj0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 1217.1067125944073, + "y": 2550.981495703757, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 529.0916748046875, + "seed": 2063600248, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722613347790, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 529.0916748046875 + ] + ] + }, + { + "type": "text", + "version": 577, + "versionNonce": 1068243935, + "index": "b4F", + "isDeleted": false, + "id": "G2QGKBTRdlg-XIehFsIB8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 1499.8567125944073, + "y": 2559.1204833984375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 138.8000030517578, + "height": 21.6, + "seed": 210238328, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "LIVE SNAPSHOT S", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "LIVE SNAPSHOT S", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "text", + "version": 630, + "versionNonce": 1973114257, + "index": "b4G", + "isDeleted": false, + "id": "WbaN7-68EdNP2Uqx-GkXd", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 866.8567125944072, + "y": 2559.1204833984375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 139.62400817871094, + "height": 21.6, + "seed": 1776929912, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "LIVE SNAPSHOT T", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "LIVE SNAPSHOT T", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "text", + "version": 479, + "versionNonce": 1151027199, + "index": "b4H", + "isDeleted": false, + "id": "aF8EEPPdZc6koW6L9xD4g", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 555.8567125944072, + "y": 2497.481495703757, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 206.4560089111328, + "height": 21.6, + "seed": 1021934968, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "TimetableSnapshotManager", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "TimetableSnapshotManager", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 1360, + "versionNonce": 336518879, + "index": "b4I", + "isDeleted": false, + "id": "auDsw3sexJL_zUJDqc5a2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1441.0473162701387, + "y": 2799.747750091553, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 1456148088, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "3MnW23_NL0OnG6gFRvP2i" + }, + { + "id": "NtfvF1rCJP3lm6JR4sJwr", + "type": "arrow" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1383, + "versionNonce": 1536981873, + "index": "b4J", + "isDeleted": false, + "id": "3MnW23_NL0OnG6gFRvP2i", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1471.1953188946504, + "y": 2808.9477500915527, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 116.70399475097656, + "height": 21.6, + "seed": 1079148408, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "auDsw3sexJL_zUJDqc5a2", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 1810, + "versionNonce": 632138495, + "index": "b4P", + "isDeleted": false, + "id": "7rs5mmzMe9wzHEgvaK8wF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 693.1426181080042, + "y": 2600.422728329152, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 238.75, + "height": 101.25000000000001, + "seed": 1039828344, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "O0h2VKRnGinUPvlvQcnz6" + }, + { + "id": "xSmTrC-EYyM7t0zg3bvM5", + "type": "arrow" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1836, + "versionNonce": 386920479, + "index": "b4Q", + "isDeleted": false, + "id": "O0h2VKRnGinUPvlvQcnz6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 721.1976031543909, + "y": 2605.422728329152, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 182.64002990722656, + "height": 27, + "seed": 544937592, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "TimetableSnapshot'", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "7rs5mmzMe9wzHEgvaK8wF", + "originalText": "TimetableSnapshot'", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 1432, + "versionNonce": 373155153, + "index": "b4R", + "isDeleted": false, + "id": "SO_iZkMpiwOByhX5sEKC9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 707.5770144322728, + "y": 2639.254900685698, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 212.0000000000001, + "height": 54, + "seed": 1723959160, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "FsBJ1XkWm-JmDfvHt3eTZ" + }, + { + "id": "MnuHCXJPh-qTZN486xFqn", + "type": "arrow" + }, + { + "id": "xSmTrC-EYyM7t0zg3bvM5", + "type": "arrow" + } + ], + "updated": 1722613354260, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1347, + "versionNonce": 1627034687, + "index": "b4S", + "isDeleted": false, + "id": "FsBJ1XkWm-JmDfvHt3eTZ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 723.8210177281712, + "y": 2644.654900685698, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 179.51199340820312, + "height": 43.2, + "seed": 1561450616, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "SO_iZkMpiwOByhX5sEKC9", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 2086, + "versionNonce": 1963996991, + "index": "b4T", + "isDeleted": false, + "id": "HgrSUZj7M8bu0auDkaYvi", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 759.1426181080041, + "y": 2765.422728329152, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 657459576, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "4fDW_R8yKdTrqABxBbK1y" + }, + { + "id": "xSmTrC-EYyM7t0zg3bvM5", + "type": "arrow" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2024, + "versionNonce": 819401521, + "index": "b4U", + "isDeleted": false, + "id": "4fDW_R8yKdTrqABxBbK1y", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 801.4126185657677, + "y": 2770.422728329152, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 116.95999908447266, + "height": 27, + "seed": 1348475512, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "Timetable A'", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "HgrSUZj7M8bu0auDkaYvi", + "originalText": "Timetable A'", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "arrow", + "version": 11142, + "versionNonce": 564459359, + "index": "b4V", + "isDeleted": false, + "id": "0BMFdl0UU16nFIpRdZTlV", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 794.7278368644882, + "y": 2841.004900685698, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 738.2027110496729, + "height": 61.20950151153875, + "seed": 1533414264, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "gDYtkMTmzJOrUoAlvL8Ts" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false, + "startBinding": { + "elementId": "7P4qi0vg0a0NqxQRtcEOx", + "focus": 0.899764521551228, + "gap": 1, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 249.96144082210685, + 55.002745568541286 + ], + [ + 738.2027110496729, + 61.20950151153875 + ] + ] + }, + { + "type": "text", + "version": 156, + "versionNonce": 976991327, + "index": "b4W", + "isDeleted": false, + "id": "gDYtkMTmzJOrUoAlvL8Ts", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1018.6652775187483, + "y": 2885.207646254239, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 52.04800033569336, + "height": 21.6, + "seed": 1384504440, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "1:(N-1)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "0BMFdl0UU16nFIpRdZTlV", + "originalText": "1:(N-1)", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "arrow", + "version": 8754, + "versionNonce": 1502150111, + "index": "b4X", + "isDeleted": false, + "id": "xSmTrC-EYyM7t0zg3bvM5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 727.4133403703374, + "y": 2694.254900685699, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 48.538509471962925, + "height": 109.27907708693374, + "seed": 1468239224, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722613354263, + "link": null, + "locked": false, + "startBinding": { + "elementId": "SO_iZkMpiwOByhX5sEKC9", + "focus": 0.7246994139403939, + "gap": 1.0000000000009095, + "fixedPoint": null + }, + "endBinding": { + "elementId": "7P4qi0vg0a0NqxQRtcEOx", + "focus": -0.3930318701633938, + "gap": 14.309396324268619, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -18.05923173429619, + 91.91782764345362 + ], + [ + 30.479277737666735, + 109.27907708693374 + ] + ] + }, + { + "type": "rectangle", + "version": 1787, + "versionNonce": 789153777, + "index": "b4Y", + "isDeleted": false, + "id": "7P4qi0vg0a0NqxQRtcEOx", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 772.2020144322728, + "y": 2800.004900685698, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 866328184, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "xFGSY7qPRWK_prMmthos0" + }, + { + "id": "0BMFdl0UU16nFIpRdZTlV", + "type": "arrow" + }, + { + "id": "EoplLA1LGwC-cqfRjvwUo", + "type": "arrow" + }, + { + "id": "2DJrTm3-BfYjMrcJyaWAF", + "type": "arrow" + }, + { + "id": "xSmTrC-EYyM7t0zg3bvM5", + "type": "arrow" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1784, + "versionNonce": 1607435537, + "index": "b4Z", + "isDeleted": false, + "id": "xFGSY7qPRWK_prMmthos0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 802.3500170567846, + "y": 2809.204900685698, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 116.70399475097656, + "height": 21.6, + "seed": 1739100024, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "7P4qi0vg0a0NqxQRtcEOx", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "arrow", + "version": 9497, + "versionNonce": 1932187569, + "index": "b4a", + "isDeleted": false, + "id": "MnuHCXJPh-qTZN486xFqn", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 732.7112077068101, + "y": 2694.254900685698, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 677.7431674167273, + "height": 57.45136663695939, + "seed": 1004003448, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "YRxxjP2GDpzboV9TeZR6R" + } + ], + "updated": 1722613397863, + "link": null, + "locked": false, + "startBinding": { + "elementId": "SO_iZkMpiwOByhX5sEKC9", + "focus": 0.8725832315454902, + "gap": 1, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 123.83076714070933, + 47.285789141170426 + ], + [ + 677.7431674167273, + 57.45136663695939 + ] + ] + }, + { + "type": "text", + "version": 101, + "versionNonce": 1789120639, + "index": "b4b", + "isDeleted": false, + "id": "YRxxjP2GDpzboV9TeZR6R", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 868.5179746796728, + "y": 2728.740689826868, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 52.04800033569336, + "height": 21.6, + "seed": 1341460856, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "1:(N-1)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "MnuHCXJPh-qTZN486xFqn", + "originalText": "1:(N-1)", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "arrow", + "version": 868, + "versionNonce": 885718961, + "index": "b4c", + "isDeleted": false, + "id": "EoplLA1LGwC-cqfRjvwUo", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 791.5014822086207, + "y": 2841.004900685698, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 40.68878681831643, + "height": 92.67074506443805, + "seed": 725885560, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722613347790, + "link": null, + "locked": false, + "startBinding": { + "elementId": "7P4qi0vg0a0NqxQRtcEOx", + "focus": 0.7890206670033463, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "VvQxWw8TCkcz7ckO0RoWR", + "focus": -0.730807890438654, + "gap": 3.5, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 8.938786818316373, + 74.31826982274652 + ], + [ + 40.68878681831643, + 92.67074506443805 + ] + ] + }, + { + "type": "rectangle", + "version": 2361, + "versionNonce": 1587460849, + "index": "b4d", + "isDeleted": false, + "id": "76I7jd1-ZH7_h7fx4pxEO", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 930.1082377769371, + "y": 3024.0731705084445, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 264.41406249999994, + "height": 37, + "seed": 1284051832, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "9eVli8hkn4fsMTdBsYfjE", + "type": "text" + }, + { + "id": "iam3gKL9dO6YvgqjfXe03", + "type": "arrow" + } + ], + "updated": 1722613354260, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1991, + "versionNonce": 1811114143, + "index": "b4e", + "isDeleted": false, + "id": "9eVli8hkn4fsMTdBsYfjE", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 948.1752620078942, + "y": 3029.0731705084445, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 228.28001403808594, + "height": 27, + "seed": 973595768, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354260, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "departureTimes' per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "76I7jd1-ZH7_h7fx4pxEO", + "originalText": "departureTimes' per stop", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "line", + "version": 876, + "versionNonce": 1545829375, + "index": "b4f", + "isDeleted": false, + "id": "74uJ0rdhOOzUXm5trA9KY", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 659.5153392383585, + "y": 2553.4214550450447, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 529.0916748046875, + "seed": 625913608, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722613347790, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 529.0916748046875 + ] + ] + }, + { + "type": "text", + "version": 185, + "versionNonce": 231486673, + "index": "b4k", + "isDeleted": false, + "id": "I_2lT12C0E9FCYKnq5YNd", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 385.2437926485377, + "y": 2559.1204833984375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 60.33599853515625, + "height": 21.6, + "seed": 1991632760, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354261, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "BUFFER", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "BUFFER", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 1963, + "versionNonce": 2003936287, + "index": "b4l", + "isDeleted": false, + "id": "rIFwQ8h5OfKoraW5mPZvI", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 296.3687926485377, + "y": 2601.4954833984375, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 238.75, + "height": 101.25000000000001, + "seed": 1509942280, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "l9WZa5V1ePB6L-Pt59jQ3" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1990, + "versionNonce": 735771839, + "index": "b4m", + "isDeleted": false, + "id": "l9WZa5V1ePB6L-Pt59jQ3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 322.16377555869394, + "y": 2606.4954833984375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 187.1600341796875, + "height": 27, + "seed": 796271368, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354261, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "TimetableSnapshot''", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "rIFwQ8h5OfKoraW5mPZvI", + "originalText": "TimetableSnapshot''", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 1588, + "versionNonce": 1357869745, + "index": "b4n", + "isDeleted": false, + "id": "kHdY2bfsEBwESfRz4YN-M", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 310.8031889728063, + "y": 2640.3276557549834, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 212.0000000000001, + "height": 54, + "seed": 432833032, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "j00JmFfVm15O0QmhQsNAe" + }, + { + "id": "fs6bZqh-c0qMs9sOI1tNe", + "type": "arrow" + }, + { + "id": "2DJrTm3-BfYjMrcJyaWAF", + "type": "arrow" + } + ], + "updated": 1722613354261, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1500, + "versionNonce": 234556639, + "index": "b4o", + "isDeleted": false, + "id": "j00JmFfVm15O0QmhQsNAe", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 327.0471922687048, + "y": 2645.7276557549835, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 179.51199340820312, + "height": 43.2, + "seed": 1207894280, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354261, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "kHdY2bfsEBwESfRz4YN-M", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "arrow", + "version": 9222, + "versionNonce": 44742207, + "index": "b4r", + "isDeleted": false, + "id": "2DJrTm3-BfYjMrcJyaWAF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 427.54162506908756, + "y": 2695.3276557549834, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 329.9924350495843, + "height": 106.9054191654377, + "seed": 113820792, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722613354264, + "link": null, + "locked": false, + "startBinding": { + "elementId": "kHdY2bfsEBwESfRz4YN-M", + "focus": 0.011287467732258886, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "7P4qi0vg0a0NqxQRtcEOx", + "focus": 0.5350900601518436, + "gap": 14.667954313600944, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 40.20392557762136, + 93.29416972195713 + ], + [ + 329.9924350495843, + 106.9054191654377 + ] + ] + }, + { + "type": "arrow", + "version": 10031, + "versionNonce": 1736560447, + "index": "b4s", + "isDeleted": false, + "id": "fs6bZqh-c0qMs9sOI1tNe", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 432.4953099034677, + "y": 2695.453997833486, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 974.9063966905857, + "height": 56.22061343676978, + "seed": 1395225976, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "8aORYwm0GfpHJxJl7xftm" + } + ], + "updated": 1722613386040, + "link": null, + "locked": false, + "startBinding": { + "elementId": "kHdY2bfsEBwESfRz4YN-M", + "focus": 0.3940851560683485, + "gap": 1.1263420785026028, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 181.6881069547195, + 55.285789141170426 + ], + [ + 974.9063966905857, + 56.22061343676978 + ] + ] + }, + { + "type": "text", + "version": 105, + "versionNonce": 749728913, + "index": "b4t", + "isDeleted": false, + "id": "8aORYwm0GfpHJxJl7xftm", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 588.1594166903405, + "y": 2739.9397869746563, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 52.04800033569336, + "height": 21.6, + "seed": 306972280, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354261, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "1:(N-1)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "fs6bZqh-c0qMs9sOI1tNe", + "originalText": "1:(N-1)", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 1697, + "versionNonce": 1138120063, + "index": "bAF", + "isDeleted": false, + "id": "cH9cBPNRMJUvSsnYLBbGw", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1706.6467906191187, + "y": 2533.6471481160406, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 313.00000000000006, + "height": 94.9999999999999, + "seed": 1360361224, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Le8fh7nWKXtCxYu1ppieP" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1680, + "versionNonce": 1158292831, + "index": "bAG", + "isDeleted": false, + "id": "Le8fh7nWKXtCxYu1ppieP", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1829.5467921449977, + "y": 2538.6471481160406, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 67.19999694824219, + "height": 21.6, + "seed": 1952536072, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354261, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "Requests", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "cH9cBPNRMJUvSsnYLBbGw", + "originalText": "Requests", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 1736, + "versionNonce": 1720536479, + "index": "bAH", + "isDeleted": false, + "id": "RR7URnVyH3wS0MYL-pWbS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1915.6467906191187, + "y": 2567.6471481160406, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.05769230769234, + "height": 54, + "seed": 551113992, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "0FLCl23y4xKwk6_YX-gJF" + }, + { + "id": "V1QUCRejM54ut1OvcXxl3", + "type": "arrow" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1728, + "versionNonce": 68985873, + "index": "bAI", + "isDeleted": false, + "id": "0FLCl23y4xKwk6_YX-gJF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1928.5036358879552, + "y": 2573.0471481160407, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 66.34400177001953, + "height": 43.2, + "seed": 469333000, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354261, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "Thread 1\nRouting", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "RR7URnVyH3wS0MYL-pWbS", + "originalText": "Thread 1\nRouting", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 1735, + "versionNonce": 1676689855, + "index": "bAJ", + "isDeleted": false, + "id": "gjAleQ16OavZYYNTGTsVT", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1817.6467906191187, + "y": 2567.6471481160406, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.00000000000006, + "height": 54, + "seed": 465416968, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "66-4Ezj1Z4yvUVOU8rale" + }, + { + "id": "55GVnbJXZxb8yJ4kNzHNa", + "type": "arrow" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1751, + "versionNonce": 1417421183, + "index": "bAK", + "isDeleted": false, + "id": "66-4Ezj1Z4yvUVOU8rale", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1830.474789734109, + "y": 2573.0471481160407, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 66.34400177001953, + "height": 43.2, + "seed": 916591112, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354261, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "Thread 2\nGraphQL", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "gjAleQ16OavZYYNTGTsVT", + "originalText": "Thread 2\nGraphQL", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 1921, + "versionNonce": 2099229151, + "index": "bAL", + "isDeleted": false, + "id": "ssEWb1C7ks-KIUrnc4q7a", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1719.6467906191187, + "y": 2567.6471481160406, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.08333333333327, + "height": 54, + "seed": 1403162888, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "wcytVn8ZKkLDvsSHezrQ1" + } + ], + "updated": 1722613347790, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1941, + "versionNonce": 1508899313, + "index": "bAM", + "isDeleted": false, + "id": "wcytVn8ZKkLDvsSHezrQ1", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1732.5164564007757, + "y": 2573.0471481160407, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 66.34400177001953, + "height": 43.2, + "seed": 440086536, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613354261, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 6, + "text": "Thread 3\nIdle", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ssEWb1C7ks-KIUrnc4q7a", + "originalText": "Thread 3\nIdle", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "arrow", + "version": 1145, + "versionNonce": 2066488831, + "index": "bAN", + "isDeleted": false, + "id": "V1QUCRejM54ut1OvcXxl3", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1954.2710270036418, + "y": 2622.6471481160406, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 469.6242363845231, + "height": 52.946533203125, + "seed": 1650978568, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722613347790, + "link": null, + "locked": false, + "startBinding": { + "elementId": "RR7URnVyH3wS0MYL-pWbS", + "focus": -0.669007785877875, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "VNqO4dksDtEYKlYJhnKS4", + "focus": 0.576213574463261, + "gap": 6.658870673249112, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -176.6242363845231, + 45.946533203125 + ], + [ + -469.6242363845231, + 52.946533203125 + ] + ] + }, + { + "type": "arrow", + "version": 1397, + "versionNonce": 866911601, + "index": "bAO", + "isDeleted": false, + "id": "55GVnbJXZxb8yJ4kNzHNa", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1855.1701715549366, + "y": 2622.6471481160406, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 372.02338093581784, + "height": 26.6163330078125, + "seed": 614780424, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722613347790, + "link": null, + "locked": false, + "startBinding": { + "elementId": "gjAleQ16OavZYYNTGTsVT", + "focus": -0.7265236217354843, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "VNqO4dksDtEYKlYJhnKS4", + "focus": 0.0724484245863557, + "gap": 5.158870673249112, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -108.02338093581784, + 21.6163330078125 + ], + [ + -372.02338093581784, + 26.6163330078125 + ] + ] + }, + { + "type": "rectangle", + "version": 2993, + "versionNonce": 891536575, + "index": "bAP", + "isDeleted": false, + "id": "nTqBmMOAiVd6lPelAvDt7", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1536.8301599391036, + "y": 2894.9675352593104, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 159.99999999999994, + "height": 37, + "seed": 744812040, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "0d_en5Hcgi_08DOzq2j7C", + "type": "text" + }, + { + "id": "8zeMgUPjWB3uFIp-E-0Hm", + "type": "arrow" + } + ], + "updated": 1722613532917, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2945, + "versionNonce": 1937189553, + "index": "bAQ", + "isDeleted": false, + "id": "0d_en5Hcgi_08DOzq2j7C", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1555.6601541407638, + "y": 2899.9675352593104, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 122.34001159667969, + "height": 27, + "seed": 1151986952, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613532917, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "TripTimes A2", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "nTqBmMOAiVd6lPelAvDt7", + "originalText": "TripTimes A2", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 1658, + "versionNonce": 1489665969, + "index": "bAR", + "isDeleted": false, + "id": "ZoLAf9rVAmBkw0S_8n_S-", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1733.6395562633718, + "y": 2894.2048800296498, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 147.5, + "height": 37, + "seed": 704056328, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "q1JBubYVg4uEj-xQM3Lnt", + "type": "text" + }, + { + "id": "8zeMgUPjWB3uFIp-E-0Hm", + "type": "arrow" + } + ], + "updated": 1722613553546, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1537, + "versionNonce": 360754321, + "index": "bAS", + "isDeleted": false, + "id": "q1JBubYVg4uEj-xQM3Lnt", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1773.7595551952566, + "y": 2899.2048800296498, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 67.26000213623047, + "height": 27, + "seed": 1179142920, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613532917, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "Trip A2", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ZoLAf9rVAmBkw0S_8n_S-", + "originalText": "Trip A2", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "arrow", + "version": 4863, + "versionNonce": 2141282705, + "index": "bAT", + "isDeleted": false, + "id": "8zeMgUPjWB3uFIp-E-0Hm", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1697.8895562633727, + "y": 2914.2048800296498, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 33.50760527639227, + "height": 0.2473184567024873, + "seed": 1679403528, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722613553547, + "link": null, + "locked": false, + "startBinding": { + "elementId": "nTqBmMOAiVd6lPelAvDt7", + "focus": 0.06996374748020846, + "gap": 1.0593963242690734, + "fixedPoint": null + }, + "endBinding": { + "elementId": "ZoLAf9rVAmBkw0S_8n_S-", + "focus": -0.03632491991928802, + "gap": 2.2423947236068216, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 33.50760527639227, + -0.2473184567024873 + ] + ] + }, + { + "type": "rectangle", + "version": 2890, + "versionNonce": 806882847, + "index": "bAZ", + "isDeleted": false, + "id": "8BjSVNCUwGyc7plxQpboP", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1514.8480290337036, + "y": 2919.2956082516002, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 159.99999999999994, + "height": 37, + "seed": 510077192, + "groupIds": [ + "1DJub0Taeze7rHAuM1rtc" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "FCjPvM9gwASnA2l9ZIEvV", + "type": "text" + }, + { + "id": "hDzEQtRTrnsYk8sinDNGj", + "type": "arrow" + }, + { + "id": "9a8w7-EJ4i8sFTYahGokw", + "type": "arrow" + } + ], + "updated": 1722613606416, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2833, + "versionNonce": 1885798385, + "index": "bAa", + "isDeleted": false, + "id": "FCjPvM9gwASnA2l9ZIEvV", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1533.6780232353638, + "y": 2924.2956082516002, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 122.34001159667969, + "height": 27, + "seed": 1353500680, + "groupIds": [ + "1DJub0Taeze7rHAuM1rtc" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613583250, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "TripTimes A1", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "8BjSVNCUwGyc7plxQpboP", + "originalText": "TripTimes A1", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "rectangle", + "version": 1553, + "versionNonce": 1836970911, + "index": "bAb", + "isDeleted": false, + "id": "QBSbh4V3DQqgxLapgUPD3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1709.6574253579718, + "y": 2918.5329530219396, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 147.5, + "height": 37, + "seed": 115771144, + "groupIds": [ + "1DJub0Taeze7rHAuM1rtc" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "JLfZ4jnmHlrp76mI61XU5", + "type": "text" + }, + { + "id": "hDzEQtRTrnsYk8sinDNGj", + "type": "arrow" + }, + { + "id": "EDhzB1tAsUuNLibxHypy9", + "type": "arrow" + } + ], + "updated": 1722613583250, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1426, + "versionNonce": 675535313, + "index": "bAc", + "isDeleted": false, + "id": "JLfZ4jnmHlrp76mI61XU5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1749.7774242898565, + "y": 2923.5329530219396, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 67.26000213623047, + "height": 27, + "seed": 1838179848, + "groupIds": [ + "1DJub0Taeze7rHAuM1rtc" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722613583250, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 6, + "text": "Trip A1", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "QBSbh4V3DQqgxLapgUPD3", + "originalText": "Trip A1", + "autoResize": true, + "lineHeight": 1.35 + }, + { + "type": "arrow", + "version": 4510, + "versionNonce": 1373382591, + "index": "bAd", + "isDeleted": false, + "id": "hDzEQtRTrnsYk8sinDNGj", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1675.9074253579722, + "y": 2939.095977136188, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 32.749999999999545, + "height": 0.19584047411944994, + "seed": 1101423880, + "groupIds": [ + "1DJub0Taeze7rHAuM1rtc" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722613583250, + "link": null, + "locked": false, + "startBinding": { + "elementId": "8BjSVNCUwGyc7plxQpboP", + "focus": 0.09927684401939457, + "gap": 1.0593963242686186, + "fixedPoint": null + }, + "endBinding": { + "elementId": "QBSbh4V3DQqgxLapgUPD3", + "focus": -0.14285714285714285, + "gap": 1, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 32.749999999999545, + 0.19584047411944994 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/snapshot-manager-5.svg b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-5.svg new file mode 100644 index 00000000000..dbf91012ffb --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-5.svg @@ -0,0 +1,29 @@ + + + + + + + + TripTimes A3Trip A3TripTimes A1'Timetable CTimetable BTimetableSnapshotMapTripPattern -> TimetablearrivalTimes per stopdepartureTimes per stopTimetable A1:N1:NLIVE SNAPSHOT SLIVE SNAPSHOT TTimetableSnapshotManagerList<TripTimes>TimetableSnapshot'MapTripPattern -> TimetableTimetable A'1:(N-1)List<TripTimes>1:(N-1)departureTimes' per stopBUFFERTimetableSnapshot''MapTripPattern -> Timetable1:(N-1)RequestsThread 1RoutingThread 2GraphQLThread 3IdleTripTimes A2Trip A2TripTimes A1Trip A1 \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/snapshot-manager-6.excalidraw b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-6.excalidraw new file mode 100644 index 00000000000..0224b054ec6 --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-6.excalidraw @@ -0,0 +1,4177 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "arrow", + "version": 9545, + "versionNonce": 1220929599, + "index": "b4u", + "isDeleted": false, + "id": "U9HemjLCcmo1zPfVzZ3E4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 885.5346229653501, + "y": 3652.6091498723067, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 701.0425587913196, + "height": 53.45654322532164, + "seed": 1822000504, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": { + "elementId": "XFOjSfj07E2LvoY0FCqpn", + "focus": 0.6836388927982819, + "gap": 1.0000000000004547, + "fixedPoint": null + }, + "endBinding": { + "elementId": "3ocTIyeHMv1KKfR0RJIbB", + "focus": -0.2598186501508492, + "gap": 2.297650918933641, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 143.42255879131994, + 34.215517241379075 + ], + [ + 701.0425587913196, + 53.45654322532164 + ] + ] + }, + { + "type": "arrow", + "version": 8642, + "versionNonce": 2098921265, + "index": "b4v", + "isDeleted": false, + "id": "1UdfPkFo0qlHjJvI9v201", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 878.2303825314054, + "y": 3652.6091498723063, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 44.919516877770775, + "height": 79.78461886476316, + "seed": 591009400, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": { + "elementId": "XFOjSfj07E2LvoY0FCqpn", + "focus": 0.4384955575098768, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "IY5ybkPrn4Bi4H7k5NGik", + "focus": -0.5900275270581304, + "gap": 2.845251097494156, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 3.0679543777708886, + 68.78947439428521 + ], + [ + 44.919516877770775, + 79.78461886476316 + ] + ] + }, + { + "type": "rectangle", + "version": 3037, + "versionNonce": 897353823, + "index": "b4w", + "isDeleted": false, + "id": "XFOjSfj07E2LvoY0FCqpn", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 832.8271817566704, + "y": 3616.6091498723063, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 159.99999999999994, + "height": 35, + "seed": 966821752, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "FGnzqn7NaKVTkniW6IKMk" + }, + { + "id": "U9HemjLCcmo1zPfVzZ3E4", + "type": "arrow" + }, + { + "id": "12LVyCjDoZiq8qtrgjv_D", + "type": "arrow" + }, + { + "id": "5MDioORQKGkLAbOccElTj", + "type": "arrow" + }, + { + "id": "1UdfPkFo0qlHjJvI9v201", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2914, + "versionNonce": 1421209873, + "index": "b4x", + "isDeleted": false, + "id": "FGnzqn7NaKVTkniW6IKMk", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 848.977179467852, + "y": 3621.6091498723063, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 127.70000457763672, + "height": 25, + "seed": 2081478776, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes A1'", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "XFOjSfj07E2LvoY0FCqpn", + "originalText": "TripTimes A1'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 5468, + "versionNonce": 1856644223, + "index": "b4y", + "isDeleted": false, + "id": "5MDioORQKGkLAbOccElTj", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 993.886578080939, + "y": 3637.4866069349423, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 712.4615902136741, + "height": 25.568801529430402, + "seed": 240060792, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": { + "elementId": "XFOjSfj07E2LvoY0FCqpn", + "focus": -0.005352539817171407, + "gap": 1.0593963242686186, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 593.9406036757315, + 25.568801529430402 + ], + [ + 712.4615902136741, + 12.435221439972793 + ] + ] + }, + { + "type": "rectangle", + "version": 2004, + "versionNonce": 1138206449, + "index": "b59", + "isDeleted": false, + "id": "P4K3ZhEWAiJbGOQOEuZi-", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1466.2795308377379, + "y": 3418.1549662850803, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 847701112, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "htD1c8ERYxjXmsu96gUO4" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1882, + "versionNonce": 1141849247, + "index": "b5A", + "isDeleted": false, + "id": "htD1c8ERYxjXmsu96gUO4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1508.0595296170347, + "y": 3423.1549662850803, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 117.94000244140625, + "height": 25, + "seed": 572757368, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable C", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "P4K3ZhEWAiJbGOQOEuZi-", + "originalText": "Timetable C", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2069, + "versionNonce": 468357795, + "index": "b5B", + "isDeleted": false, + "id": "sBdVGBmsx8tfzCrGLFvMQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1445.5295308377379, + "y": 3439.6549662850803, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 1023551096, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "PoJJGngkf-QK85JwDn4E2" + } + ], + "updated": 1722610272045, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1893, + "versionNonce": 108278413, + "index": "b5C", + "isDeleted": false, + "id": "PoJJGngkf-QK85JwDn4E2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1485.47952778598, + "y": 3444.6549662850803, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 121.60000610351562, + "height": 25, + "seed": 2145818488, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722610013951, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable B", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "sBdVGBmsx8tfzCrGLFvMQ", + "originalText": "Timetable B", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1443, + "versionNonce": 1440571057, + "index": "b5D", + "isDeleted": false, + "id": "qrZ1nMWBuD5UnvhROXjPv", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1235.1248326756029, + "y": 3292.647815690935, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 238.75, + "height": 101.25000000000001, + "seed": 789899384, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "R6YEh_AQ9J7uCJudGxV1t" + }, + { + "id": "6nL37JXxiZunl6ssjSEu2", + "type": "arrow" + }, + { + "id": "CiX0AZeBKijw7sTLJmk2K", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1411, + "versionNonce": 137486559, + "index": "b5E", + "isDeleted": false, + "id": "R6YEh_AQ9J7uCJudGxV1t", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1263.0598302341966, + "y": 3297.647815690935, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 182.8800048828125, + "height": 25, + "seed": 1716590968, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshot", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "qrZ1nMWBuD5UnvhROXjPv", + "originalText": "TimetableSnapshot", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1046, + "versionNonce": 889217169, + "index": "b5F", + "isDeleted": false, + "id": "4CBJZcytUPEiCYj-mkwFY", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1249.5592289998715, + "y": 3331.479988047481, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 212.0000000000001, + "height": 50, + "seed": 685714040, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "dd9qi1_rDU-WkfHiceDV_" + }, + { + "id": "6nL37JXxiZunl6ssjSEu2", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 932, + "versionNonce": 1646292223, + "index": "b5G", + "isDeleted": false, + "id": "dd9qi1_rDU-WkfHiceDV_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1255.5832234456723, + "y": 3336.479988047481, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 199.95201110839844, + "height": 40, + "seed": 1904417656, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "4CBJZcytUPEiCYj-mkwFY", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2194, + "versionNonce": 539502193, + "index": "b5H", + "isDeleted": false, + "id": "3ocTIyeHMv1KKfR0RJIbB", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1588.8748326756033, + "y": 3687.641881770095, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 277.5, + "height": 35, + "seed": 1115745400, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "UrCbq42ZbiU0QBgIqjhDW", + "type": "text" + }, + { + "id": "RThFNLin5V2EmrXUWKl-a", + "type": "arrow" + }, + { + "id": "U9HemjLCcmo1zPfVzZ3E4", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1898, + "versionNonce": 1923642655, + "index": "b5I", + "isDeleted": false, + "id": "UrCbq42ZbiU0QBgIqjhDW", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1621.924820468572, + "y": 3692.641881770095, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 211.4000244140625, + "height": 25, + "seed": 899456376, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "arrivalTimes per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "3ocTIyeHMv1KKfR0RJIbB", + "originalText": "arrivalTimes per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2244, + "versionNonce": 1109143633, + "index": "b5J", + "isDeleted": false, + "id": "XuEJ7Ds_OhgrhPRmZFbTk", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1590.2263951756033, + "y": 3728.897815690935, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 275.66406249999994, + "height": 35, + "seed": 203042424, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "n3HZbnUOlvzYR1mWCEHz8", + "type": "text" + }, + { + "id": "RThFNLin5V2EmrXUWKl-a", + "type": "arrow" + }, + { + "id": "5JBjNfE5LF2yWftBSZLs2", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1851, + "versionNonce": 557480255, + "index": "b5K", + "isDeleted": false, + "id": "n3HZbnUOlvzYR1mWCEHz8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1606.1884160496268, + "y": 3733.897815690935, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 243.74002075195312, + "height": 25, + "seed": 1052614520, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "departureTimes per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "XuEJ7Ds_OhgrhPRmZFbTk", + "originalText": "departureTimes per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1754, + "versionNonce": 11796017, + "index": "b5L", + "isDeleted": false, + "id": "AAbCXY7OTZ2NVLTMEIo08", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1423.8748326756033, + "y": 3460.647815690935, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 231403640, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "5mztcy7Vd0Sje-mWygLSR" + }, + { + "id": "6nL37JXxiZunl6ssjSEu2", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1631, + "versionNonce": 322369887, + "index": "b5M", + "isDeleted": false, + "id": "5mztcy7Vd0Sje-mWygLSR", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1465.5348287083182, + "y": 3465.647815690935, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 118.18000793457031, + "height": 25, + "seed": 285614456, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable A", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "AAbCXY7OTZ2NVLTMEIo08", + "originalText": "Timetable A", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 9611, + "versionNonce": 450301969, + "index": "b5N", + "isDeleted": false, + "id": "TrFpRm_OHaipB942xzu2a", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1456.8740891723796, + "y": 3536.229988047481, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 51.553592223919395, + "height": 95.07400072944347, + "seed": 1202926200, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "FhJwwdqHrYDJmWosjnpJ-" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": { + "elementId": "op6o2jRUAXqxkwKjK0vvP", + "focus": 0.7772933976281441, + "gap": 1, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 3.5474030818141955, + 84.00274556854129 + ], + [ + 51.553592223919395, + 95.07400072944347 + ] + ] + }, + { + "type": "text", + "version": 87, + "versionNonce": 1844344191, + "index": "b5O", + "isDeleted": false, + "id": "FhJwwdqHrYDJmWosjnpJ-", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1502.4154917353949, + "y": 3942.7327336160224, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 18.512001037597656, + "height": 20, + "seed": 680928120, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:N", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "TrFpRm_OHaipB942xzu2a", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 8295, + "versionNonce": 108848625, + "index": "b5P", + "isDeleted": false, + "id": "RThFNLin5V2EmrXUWKl-a", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1525.3561795477929, + "y": 3652.7426432771417, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 62.51865312781001, + "height": 38.84885675576243, + "seed": 838005880, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "3ocTIyeHMv1KKfR0RJIbB", + "focus": -0.4892657821445063, + "gap": 1.0000000000004547, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 17.39865312781035, + 24.965517241379303 + ], + [ + 62.51865312781001, + 38.84885675576243 + ] + ] + }, + { + "type": "arrow", + "version": 8341, + "versionNonce": 1486974367, + "index": "b5Q", + "isDeleted": false, + "id": "5JBjNfE5LF2yWftBSZLs2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1523.5809598705914, + "y": 3652.3805743116236, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 63.64543530501169, + "height": 85.17563736107877, + "seed": 2039326072, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "XuEJ7Ds_OhgrhPRmZFbTk", + "focus": -0.7418501122386231, + "gap": 3.0000000000002274, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 9.293872805011802, + 54.431034482758605 + ], + [ + 63.64543530501169, + 85.17563736107877 + ] + ] + }, + { + "type": "arrow", + "version": 6661, + "versionNonce": 246218705, + "index": "b5R", + "isDeleted": false, + "id": "6nL37JXxiZunl6ssjSEu2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1316.721137060804, + "y": 3382.479988047481, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 102.15369561479929, + "height": 117.21041353830879, + "seed": 1017788024, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "tTg1BfsLSq76mpOsecXCu" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": { + "elementId": "4CBJZcytUPEiCYj-mkwFY", + "focus": 0.3890582261519381, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "AAbCXY7OTZ2NVLTMEIo08", + "focus": -0.28923171675537335, + "gap": 5, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 14.785416969799371, + 100.16782764345407 + ], + [ + 102.15369561479929, + 117.21041353830879 + ] + ] + }, + { + "type": "text", + "version": 41, + "versionNonce": 1048601023, + "index": "b5S", + "isDeleted": false, + "id": "tTg1BfsLSq76mpOsecXCu", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1373.5005535118046, + "y": 3805.147815690935, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 18.512001037597656, + "height": 20, + "seed": 1879877496, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:N", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "6nL37JXxiZunl6ssjSEu2", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 818, + "versionNonce": 830397873, + "index": "b5T", + "isDeleted": false, + "id": "igNZiEH-k9w_pEDCaEv7n", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 1212.9936253241406, + "y": 3246.463733659685, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 529.0916748046875, + "seed": 1341716600, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 529.0916748046875 + ] + ] + }, + { + "type": "text", + "version": 582, + "versionNonce": 1353437663, + "index": "b5U", + "isDeleted": false, + "id": "0wxuiqnmKRHXBXP_Z9R1J", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 1495.7436253241406, + "y": 3254.602721354365, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 147.21600341796875, + "height": 20, + "seed": 1882401144, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "LIVE SNAPSHOT S", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "LIVE SNAPSHOT S", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 635, + "versionNonce": 1041891217, + "index": "b5V", + "isDeleted": false, + "id": "NCxiokyjjxTkCuH_RtIYQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 862.7436253241403, + "y": 3254.602721354365, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 150.3520050048828, + "height": 20, + "seed": 1275017848, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "LIVE SNAPSHOT T", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "LIVE SNAPSHOT T", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 484, + "versionNonce": 1211383295, + "index": "b5W", + "isDeleted": false, + "id": "9_ZyXl20-eROCoJqQkaxh", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 551.7436253241405, + "y": 3192.963733659685, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 211.0240020751953, + "height": 20, + "seed": 1445014392, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "TimetableSnapshotManager", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "TimetableSnapshotManager", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1415, + "versionNonce": 850781553, + "index": "b5X", + "isDeleted": false, + "id": "op6o2jRUAXqxkwKjK0vvP", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1436.934228999872, + "y": 3495.229988047481, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 148700280, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "0rFWWpfZ-pSrCBqiLjrD2" + }, + { + "id": "TrFpRm_OHaipB942xzu2a", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1387, + "versionNonce": 1746544159, + "index": "b5Y", + "isDeleted": false, + "id": "0rFWWpfZ-pSrCBqiLjrD2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1463.7702313497255, + "y": 3505.229988047481, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.32799530029297, + "height": 20, + "seed": 1979100536, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "op6o2jRUAXqxkwKjK0vvP", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1880, + "versionNonce": 1350352771, + "index": "b5e", + "isDeleted": false, + "id": "IBLmqpELY-Pk0S_LG3wD4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 689.0295308377374, + "y": 3295.9049662850803, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 238.75, + "height": 101.25000000000001, + "seed": 35926904, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "M-S_cJvb1c4sRm_Vp-7Bs" + }, + { + "id": "mq9-peL8OP64Kkf-nArjI", + "type": "arrow" + }, + { + "id": "0CosfiI9HbMzEOQ9l0xYa", + "type": "arrow" + } + ], + "updated": 1722610350358, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1843, + "versionNonce": 256277859, + "index": "b5f", + "isDeleted": false, + "id": "M-S_cJvb1c4sRm_Vp-7Bs", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 713.3045247342218, + "y": 3300.9049662850803, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 190.20001220703125, + "height": 25, + "seed": 1329529976, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722610349425, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshot'", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "IBLmqpELY-Pk0S_LG3wD4", + "originalText": "TimetableSnapshot'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1479, + "versionNonce": 740162865, + "index": "b5g", + "isDeleted": false, + "id": "wSBk1zLLQlMHTk804pohy", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 703.463927162006, + "y": 3334.737138641626, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 212.0000000000001, + "height": 50, + "seed": 852997496, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "xK4HqPtVFphSTu9fHYBch" + }, + { + "id": "0viPEoXFlEhnIHqT2BHGy", + "type": "arrow" + }, + { + "id": "mq9-peL8OP64Kkf-nArjI", + "type": "arrow" + }, + { + "id": "0CosfiI9HbMzEOQ9l0xYa", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1351, + "versionNonce": 716204639, + "index": "b5h", + "isDeleted": false, + "id": "xK4HqPtVFphSTu9fHYBch", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 709.4879216078068, + "y": 3339.737138641626, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 199.95201110839844, + "height": 40, + "seed": 1647462008, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "wSBk1zLLQlMHTk804pohy", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2141, + "versionNonce": 1722363665, + "index": "b5i", + "isDeleted": false, + "id": "hkWQ4aFOPxB45iExD8tC-", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 755.0295308377372, + "y": 3460.9049662850803, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 1242740600, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "7S0BlMwTReU5vchv3vM3i" + }, + { + "id": "mq9-peL8OP64Kkf-nArjI", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2028, + "versionNonce": 387040895, + "index": "b5j", + "isDeleted": false, + "id": "7S0BlMwTReU5vchv3vM3i", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 794.0295270230399, + "y": 3465.9049662850803, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.50000762939453, + "height": 25, + "seed": 515704952, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable A'", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "hkWQ4aFOPxB45iExD8tC-", + "originalText": "Timetable A'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 11339, + "versionNonce": 1890788593, + "index": "b5k", + "isDeleted": false, + "id": "iBEW8bpmwwj0oxolEhxTL", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 790.6147495942214, + "y": 3536.487138641626, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 738.2027110496729, + "height": 61.20950151153875, + "seed": 1811233144, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "2mcZAKniM8ose9jw9CWN9" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": { + "elementId": "MrUiVLDEcgVT4WC-zy2m6", + "focus": 0.899764521551228, + "gap": 1, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 249.96144082210685, + 55.002745568541286 + ], + [ + 738.2027110496729, + 61.20950151153875 + ] + ] + }, + { + "type": "text", + "version": 107, + "versionNonce": 53649055, + "index": "b5l", + "isDeleted": false, + "id": "2mcZAKniM8ose9jw9CWN9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1071.090190355293, + "y": 3913.9898842101675, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 41.47200012207031, + "height": 20, + "seed": 620710520, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:(N-1)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "iBEW8bpmwwj0oxolEhxTL", + "originalText": "1:(N-1)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 8972, + "versionNonce": 1975738065, + "index": "b5m", + "isDeleted": false, + "id": "mq9-peL8OP64Kkf-nArjI", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 724.0120424008126, + "y": 3385.737138641626, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 48.538509471962925, + "height": 113.27907708693374, + "seed": 882850680, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": { + "elementId": "wSBk1zLLQlMHTk804pohy", + "focus": 0.7215373527557638, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "MrUiVLDEcgVT4WC-zy2m6", + "focus": -0.4828830088289787, + "gap": 14.309396324268619, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -18.771021035038075, + 92.16782764345362 + ], + [ + 29.76748843692485, + 113.27907708693374 + ] + ] + }, + { + "type": "rectangle", + "version": 1842, + "versionNonce": 1103327935, + "index": "b5n", + "isDeleted": false, + "id": "MrUiVLDEcgVT4WC-zy2m6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 768.088927162006, + "y": 3495.487138641626, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 219745400, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "9jJuwsSGzREdP_5dJRnYZ" + }, + { + "id": "iBEW8bpmwwj0oxolEhxTL", + "type": "arrow" + }, + { + "id": "12LVyCjDoZiq8qtrgjv_D", + "type": "arrow" + }, + { + "id": "Wh9kQ-Rzrpw5PJhENm7q_", + "type": "arrow" + }, + { + "id": "mq9-peL8OP64Kkf-nArjI", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1789, + "versionNonce": 1148279985, + "index": "b5o", + "isDeleted": false, + "id": "9jJuwsSGzREdP_5dJRnYZ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 794.9249295118597, + "y": 3505.487138641626, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.32799530029297, + "height": 20, + "seed": 942718328, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "MrUiVLDEcgVT4WC-zy2m6", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 9665, + "versionNonce": 1300267629, + "index": "b5p", + "isDeleted": false, + "id": "0viPEoXFlEhnIHqT2BHGy", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 726.9011137430363, + "y": 3385.737138641626, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 545.1901741102439, + "height": 64.95136663695985, + "seed": 1730825848, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "z5Nq7fenoUQDootG3JbEt" + } + ], + "updated": 1722610276344, + "link": null, + "locked": false, + "startBinding": { + "elementId": "wSBk1zLLQlMHTk804pohy", + "focus": 0.8661810641797802, + "gap": 1, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 102.27777383421642, + 48.035789141170426 + ], + [ + 545.1901741102439, + 64.95136663695985 + ] + ] + }, + { + "type": "text", + "version": 53, + "versionNonce": 1518601229, + "index": "b5q", + "isDeleted": false, + "id": "z5Nq7fenoUQDootG3JbEt", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 920.9428875162175, + "y": 3757.5229277827966, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 42.47200012207031, + "height": 20, + "seed": 46776184, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722610013954, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:(N-1)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "0viPEoXFlEhnIHqT2BHGy", + "originalText": "1:(N-1)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 1118, + "versionNonce": 1889996543, + "index": "b5r", + "isDeleted": false, + "id": "12LVyCjDoZiq8qtrgjv_D", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 787.3883949383539, + "y": 3536.487138641626, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 41.93878681831643, + "height": 94.52348671237723, + "seed": 995299448, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": { + "elementId": "MrUiVLDEcgVT4WC-zy2m6", + "focus": 0.7849018878692015, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "XFOjSfj07E2LvoY0FCqpn", + "focus": -0.6122467605988904, + "gap": 3.5, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 3.93878681831643, + 79.31826982274652 + ], + [ + 41.93878681831643, + 94.52348671237723 + ] + ] + }, + { + "type": "rectangle", + "version": 2406, + "versionNonce": 475309169, + "index": "b5s", + "isDeleted": false, + "id": "IY5ybkPrn4Bi4H7k5NGik", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 925.9951505066704, + "y": 3719.5554084643727, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 264.41406249999994, + "height": 35, + "seed": 411713912, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "sgE3BtCkGJrfLzzNjNGS8", + "type": "text" + }, + { + "id": "1UdfPkFo0qlHjJvI9v201", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1996, + "versionNonce": 2081085215, + "index": "b5t", + "isDeleted": false, + "id": "sgE3BtCkGJrfLzzNjNGS8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 933.6721677185844, + "y": 3724.5554084643727, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 249.06002807617188, + "height": 25, + "seed": 162747000, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "departureTimes' per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "IY5ybkPrn4Bi4H7k5NGik", + "originalText": "departureTimes' per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 935, + "versionNonce": 1074327121, + "index": "b5u", + "isDeleted": false, + "id": "fhM4kO5LjlUHv9DXKFKV1", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 655.4022519680917, + "y": 3248.9036930009725, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 529.0916748046875, + "seed": 1972519800, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 529.0916748046875 + ] + ] + }, + { + "type": "text", + "version": 204, + "versionNonce": 1409325887, + "index": "b5v", + "isDeleted": false, + "id": "w4eH3_EBMUuq63x9Gjozi", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 382.38070537827093, + "y": 3254.602721354365, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 63.05599594116211, + "height": 20, + "seed": 1248531576, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "BUFFER", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "BUFFER", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2133, + "versionNonce": 1582890033, + "index": "b5w", + "isDeleted": false, + "id": "jFiha0andxIhCJAXlk11Y", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 169.25570537827093, + "y": 3299.227721354365, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 238.75, + "height": 101.25000000000001, + "seed": 234997112, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "uZahpTWntlhfnp88vAS_m" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2109, + "versionNonce": 216587103, + "index": "b5x", + "isDeleted": false, + "id": "uZahpTWntlhfnp88vAS_m", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 191.87069561264593, + "y": 3304.227721354365, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 193.52001953125, + "height": 25, + "seed": 1838831224, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshot''", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "jFiha0andxIhCJAXlk11Y", + "originalText": "TimetableSnapshot''", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 9813, + "versionNonce": 925650449, + "index": "b60", + "isDeleted": false, + "id": "Wh9kQ-Rzrpw5PJhENm7q_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 210.4441154564566, + "y": 3386.5598937109107, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 542.9768573919487, + "height": 111.15541916543862, + "seed": 429345144, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": { + "elementId": "hzUzClXhAbR5yk_LSMaPH", + "focus": 0.8344825924732999, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "MrUiVLDEcgVT4WC-zy2m6", + "focus": 0.18368086337272974, + "gap": 14.66795431360083, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 279.4383479199856, + 80.04416972195713 + ], + [ + 542.9768573919487, + 111.15541916543862 + ] + ] + }, + { + "type": "arrow", + "version": 10697, + "versionNonce": 1080391245, + "index": "b61", + "isDeleted": false, + "id": "HcZjVQoM8MscAEitFxX8T", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 210.0316379366526, + "y": 3387.8098937109107, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1067.6500873243763, + "height": 62.870224495968614, + "seed": 1944017528, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "kgGxBY2N8AUEuLf-F7GEy" + } + ], + "updated": 1722610284363, + "link": null, + "locked": false, + "startBinding": { + "elementId": "hzUzClXhAbR5yk_LSMaPH", + "focus": 0.8916244879302868, + "gap": 1, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 315.03869165126787, + 52.16213121967394 + ], + [ + 1067.6500873243763, + 62.870224495968614 + ] + ] + }, + { + "type": "text", + "version": 59, + "versionNonce": 1760699597, + "index": "b62", + "isDeleted": false, + "id": "kgGxBY2N8AUEuLf-F7GEy", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 585.806327741607, + "y": 3436.2220249305847, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 49.52800369262695, + "height": 20, + "seed": 1291668344, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722610013954, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:(N-2)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "HcZjVQoM8MscAEitFxX8T", + "originalText": "1:(N-2)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 10139, + "versionNonce": 348311455, + "index": "b63", + "isDeleted": false, + "id": "bQSirSEpNQMHI0Z11glM8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 333.1282372135084, + "y": 3652.5579175757052, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 29.946813597494042, + "height": 81.03699231515611, + "seed": 1392925704, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": { + "elementId": "JFihnE-OKz-miCkUpC84v", + "focus": 0.6739531127679016, + "gap": 2.2500000000004547, + "fixedPoint": null + }, + "endBinding": { + "elementId": "_QAXxz7Tg3GYTkfr92g47", + "focus": -0.6830614997827712, + "gap": 2.25, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -5.305479577998369, + 72.53947439428475 + ], + [ + 24.641334019495673, + 81.03699231515611 + ] + ] + }, + { + "type": "rectangle", + "version": 1743, + "versionNonce": 692764113, + "index": "b63G", + "isDeleted": false, + "id": "hzUzClXhAbR5yk_LSMaPH", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 183.69010170253955, + "y": 3338.0598937109107, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 212.0000000000001, + "height": 50, + "seed": 1308149624, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "uBP8qUeiY0zUeyiVmEv2d" + }, + { + "id": "HcZjVQoM8MscAEitFxX8T", + "type": "arrow" + }, + { + "id": "Wh9kQ-Rzrpw5PJhENm7q_", + "type": "arrow" + }, + { + "id": "7J05Unj0TLSsyk2VKQaxH", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1605, + "versionNonce": 1948615615, + "index": "b63V", + "isDeleted": false, + "id": "uBP8qUeiY0zUeyiVmEv2d", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 189.7140961483404, + "y": 3343.0598937109107, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 199.95201110839844, + "height": 40, + "seed": 1533089912, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "hzUzClXhAbR5yk_LSMaPH", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 3196, + "versionNonce": 1840569265, + "index": "b64", + "isDeleted": false, + "id": "JFihnE-OKz-miCkUpC84v", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 309.35160248300406, + "y": 3615.307917575705, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 159.99999999999994, + "height": 35, + "seed": 820878088, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "f9NjTPTggmVQB9qceFE7H" + }, + { + "id": "bQSirSEpNQMHI0Z11glM8", + "type": "arrow" + }, + { + "id": "53VoFjnK_y84jlMfgCQJ4", + "type": "arrow" + }, + { + "id": "8rq3mRf6mv-ffQJBhKjty", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 3079, + "versionNonce": 425505759, + "index": "b65", + "isDeleted": false, + "id": "f9NjTPTggmVQB9qceFE7H", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 320.6915988208947, + "y": 3620.307917575705, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 137.32000732421875, + "height": 25, + "seed": 1838479880, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes B3'", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "JFihnE-OKz-miCkUpC84v", + "originalText": "TripTimes B3'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2494, + "versionNonce": 1054883217, + "index": "b66", + "isDeleted": false, + "id": "0Lg1yeE31EwDFCenBo1mI", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 235.05395156407087, + "y": 3460.603733988479, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 201.5, + "height": 83.75000000000006, + "seed": 2079881480, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "h-Zn0TqSqrmCG4qn3PgWk" + }, + { + "id": "7J05Unj0TLSsyk2VKQaxH", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2378, + "versionNonce": 1282990079, + "index": "b67", + "isDeleted": false, + "id": "h-Zn0TqSqrmCG4qn3PgWk", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 273.34394866490095, + "y": 3465.603733988479, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 124.92000579833984, + "height": 25, + "seed": 261363720, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable B'", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "0Lg1yeE31EwDFCenBo1mI", + "originalText": "Timetable B'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2146, + "versionNonce": 1126204589, + "index": "b68", + "isDeleted": false, + "id": "_yn2BD2rhuEgAIDWXDKZ4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 248.11334788833972, + "y": 3495.1859063450247, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 207869704, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "LWY7SQKzdj9cNRV4FFsxh" + }, + { + "id": "8rq3mRf6mv-ffQJBhKjty", + "type": "arrow" + }, + { + "id": "7J05Unj0TLSsyk2VKQaxH", + "type": "arrow" + }, + { + "id": "ir7hLghaxZygbZAx9sO4J", + "type": "arrow" + } + ], + "updated": 1722610014983, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2089, + "versionNonce": 732587299, + "index": "b69", + "isDeleted": false, + "id": "LWY7SQKzdj9cNRV4FFsxh", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 274.44935023819335, + "y": 3505.1859063450247, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 124.32799530029297, + "height": 20, + "seed": 1994634760, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722610013951, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "_yn2BD2rhuEgAIDWXDKZ4", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 2861, + "versionNonce": 827464771, + "index": "b6A", + "isDeleted": false, + "id": "8rq3mRf6mv-ffQJBhKjty", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 260.85482115033005, + "y": 3536.1859063450247, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 46.24678133267395, + "height": 97.38347328639247, + "seed": 1678750984, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722610014985, + "link": null, + "locked": false, + "startBinding": { + "elementId": "_yn2BD2rhuEgAIDWXDKZ4", + "focus": 0.8564092525909824, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "JFihnE-OKz-miCkUpC84v", + "focus": -0.572700448181531, + "gap": 2.2500000000000853, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0.7467813326739474, + 85.81826982274652 + ], + [ + 46.24678133267395, + 97.38347328639247 + ] + ] + }, + { + "type": "rectangle", + "version": 2696, + "versionNonce": 273820735, + "index": "b6B", + "isDeleted": false, + "id": "_QAXxz7Tg3GYTkfr92g47", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 360.01957123300406, + "y": 3717.004176167771, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 271.85727889788814, + "height": 35, + "seed": 1768150024, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "3Xslv3TN-jjyICkxaNrY0", + "type": "text" + }, + { + "id": "bQSirSEpNQMHI0Z11glM8", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2284, + "versionNonce": 1986329393, + "index": "b6C", + "isDeleted": false, + "id": "3Xslv3TN-jjyICkxaNrY0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 371.4181966438622, + "y": 3722.004176167771, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 249.06002807617188, + "height": 25, + "seed": 215749384, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "departureTimes' per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "_QAXxz7Tg3GYTkfr92g47", + "originalText": "departureTimes' per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 13831, + "versionNonce": 2105843437, + "index": "b6EG", + "isDeleted": false, + "id": "ir7hLghaxZygbZAx9sO4J", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 265.85500046546053, + "y": 3536.1859063450247, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 1008.4777380946286, + "height": 76.93985828874702, + "seed": 2100523272, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "H9Au7LBeHetab91U4-R5m" + } + ], + "updated": 1722610225325, + "link": null, + "locked": false, + "startBinding": { + "elementId": "_yn2BD2rhuEgAIDWXDKZ4", + "focus": 0.8967121158033918, + "gap": 1, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 141.99887748034791, + 50.61627820682406 + ], + [ + 1008.4777380946286, + 76.93985828874702 + ] + ] + }, + { + "type": "text", + "version": 119, + "versionNonce": 1868916493, + "index": "b6EV", + "isDeleted": false, + "id": "H9Au7LBeHetab91U4-R5m", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 667.8678778847733, + "y": 3596.8021845518488, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 42.47200012207031, + "height": 20, + "seed": 514854920, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722610014984, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:(N-1)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ir7hLghaxZygbZAx9sO4J", + "originalText": "1:(N-1)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 2449, + "versionNonce": 1370966381, + "index": "b6F", + "isDeleted": false, + "id": "53VoFjnK_y84jlMfgCQJ4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 334.10160248300406, + "y": 3650.3777750192876, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 941.0272168256289, + "height": 60.677726689274095, + "seed": 1213926008, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722610232860, + "link": null, + "locked": false, + "startBinding": { + "elementId": "JFihnE-OKz-miCkUpC84v", + "focus": 0.8154446146104855, + "gap": 1, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 129.14219016553375, + 42.67303674829054 + ], + [ + 941.0272168256289, + 60.677726689274095 + ] + ] + }, + { + "type": "arrow", + "version": 1490, + "versionNonce": 1374395363, + "index": "b6J", + "isDeleted": false, + "id": "7J05Unj0TLSsyk2VKQaxH", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 210.35885349760304, + "y": 3389.0598937109107, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 37.92927298303522, + "height": 111.83159276278366, + "seed": 1969017208, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722610014985, + "link": null, + "locked": false, + "startBinding": { + "elementId": "hzUzClXhAbR5yk_LSMaPH", + "focus": 0.6875769442081417, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "_yn2BD2rhuEgAIDWXDKZ4", + "focus": -0.32628010072350794, + "gap": 14.94028225676692, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -15.115060849065458, + 101.2409180566674 + ], + [ + 22.81421213396976, + 111.83159276278366 + ] + ] + }, + { + "type": "rectangle", + "version": 3155, + "versionNonce": 1009732639, + "index": "b8O", + "isDeleted": false, + "id": "H3RwlSEgr7nuS4JsJ7kbw", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1552.3301599391036, + "y": 3567.5832579155604, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 159.99999999999994, + "height": 35, + "seed": 1990005256, + "groupIds": [ + "YLKbWh4tsth8VX6NSVzho", + "dCkmEueMCcBqZHb3-T--t" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "RhmnTCVuyEmkBAyOUZIZR", + "type": "text" + }, + { + "id": "-DRnRiGcLhdba9OpXE4nx", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 3065, + "versionNonce": 1515342161, + "index": "b8P", + "isDeleted": false, + "id": "RhmnTCVuyEmkBAyOUZIZR", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1567.0401590235763, + "y": 3572.5832579155604, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 130.5800018310547, + "height": 25, + "seed": 1547538696, + "groupIds": [ + "YLKbWh4tsth8VX6NSVzho", + "dCkmEueMCcBqZHb3-T--t" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes A3", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "H3RwlSEgr7nuS4JsJ7kbw", + "originalText": "TripTimes A3", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1810, + "versionNonce": 1640919103, + "index": "b8Q", + "isDeleted": false, + "id": "ltXbXR71pNmZ2TyGqp-EY", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1747.1395562633718, + "y": 3566.8206026858998, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 147.5, + "height": 35, + "seed": 1467363336, + "groupIds": [ + "YLKbWh4tsth8VX6NSVzho", + "dCkmEueMCcBqZHb3-T--t" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "dQ71VSFtz8lSc4Y1oRLzt", + "type": "text" + }, + { + "id": "-DRnRiGcLhdba9OpXE4nx", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1656, + "versionNonce": 1019148081, + "index": "b8R", + "isDeleted": false, + "id": "dQ71VSFtz8lSc4Y1oRLzt", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1783.059554432317, + "y": 3571.8206026858998, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 75.66000366210938, + "height": 25, + "seed": 1078128392, + "groupIds": [ + "YLKbWh4tsth8VX6NSVzho", + "dCkmEueMCcBqZHb3-T--t" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Trip A3", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ltXbXR71pNmZ2TyGqp-EY", + "originalText": "Trip A3", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 5530, + "versionNonce": 741609567, + "index": "b8S", + "isDeleted": false, + "id": "-DRnRiGcLhdba9OpXE4nx", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1713.3895562633727, + "y": 3586.8206026858998, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 32.74999999999909, + "height": 0, + "seed": 253742600, + "groupIds": [ + "YLKbWh4tsth8VX6NSVzho", + "dCkmEueMCcBqZHb3-T--t" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "N1xK5u3RWsyqqZ17h1agW", + "focus": 1.1857142857142857, + "gap": 3.25, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 32.74999999999909, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 3026, + "versionNonce": 1830480145, + "index": "b8T", + "isDeleted": false, + "id": "0WPSWqKSim7xyp00H2xm6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1531.8301599391036, + "y": 3590.8332579155604, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 159.99999999999994, + "height": 35, + "seed": 1292422408, + "groupIds": [ + "OJGL0mPy44tbB2RbFXPMd", + "dCkmEueMCcBqZHb3-T--t" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "z-_upArq8guFOQvveidgE", + "type": "text" + }, + { + "id": "IrPSz1lRXUthxsHHVEeUB", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2932, + "versionNonce": 892382335, + "index": "b8U", + "isDeleted": false, + "id": "z-_upArq8guFOQvveidgE", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1546.230153835588, + "y": 3595.8332579155604, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 131.20001220703125, + "height": 25, + "seed": 58321928, + "groupIds": [ + "OJGL0mPy44tbB2RbFXPMd", + "dCkmEueMCcBqZHb3-T--t" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes A2", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "0WPSWqKSim7xyp00H2xm6", + "originalText": "TripTimes A2", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1688, + "versionNonce": 1960681201, + "index": "b8V", + "isDeleted": false, + "id": "N1xK5u3RWsyqqZ17h1agW", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1728.6395562633718, + "y": 3590.0706026858998, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 147.5, + "height": 35, + "seed": 745483016, + "groupIds": [ + "OJGL0mPy44tbB2RbFXPMd", + "dCkmEueMCcBqZHb3-T--t" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "CKBl8Y7AW9ubcpOHXlCiv", + "type": "text" + }, + { + "id": "IrPSz1lRXUthxsHHVEeUB", + "type": "arrow" + }, + { + "id": "-DRnRiGcLhdba9OpXE4nx", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1522, + "versionNonce": 142918815, + "index": "b8W", + "isDeleted": false, + "id": "CKBl8Y7AW9ubcpOHXlCiv", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1764.2495568737233, + "y": 3595.0706026858998, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 76.27999877929688, + "height": 25, + "seed": 577014280, + "groupIds": [ + "OJGL0mPy44tbB2RbFXPMd", + "dCkmEueMCcBqZHb3-T--t" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Trip A2", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "N1xK5u3RWsyqqZ17h1agW", + "originalText": "Trip A2", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 4896, + "versionNonce": 1116641489, + "index": "b8X", + "isDeleted": false, + "id": "IrPSz1lRXUthxsHHVEeUB", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1692.8895562633727, + "y": 3610.0706026858998, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 34.74999999999909, + "height": 0, + "seed": 274073864, + "groupIds": [ + "OJGL0mPy44tbB2RbFXPMd", + "dCkmEueMCcBqZHb3-T--t" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "OFeXVDEWAl37ggDxjyE4E", + "focus": 1.2473184567022797, + "gap": 4.32807299228989, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 34.74999999999909, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 2928, + "versionNonce": 1709234367, + "index": "b8Y", + "isDeleted": false, + "id": "8pfyaJ2A_7vTARYBesT5A", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1509.8480290337036, + "y": 3615.1613309078502, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 159.99999999999994, + "height": 35, + "seed": 1639950344, + "groupIds": [ + "NLJz2SpoJ7Ms7Qln__nzR", + "dCkmEueMCcBqZHb3-T--t" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "wKMqbH7QepSYQyMQS6pdN", + "type": "text" + }, + { + "id": "z3BoCv0mDL67tzeFGQThQ", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2826, + "versionNonce": 130373297, + "index": "b8Z", + "isDeleted": false, + "id": "wKMqbH7QepSYQyMQS6pdN", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1528.6580265922973, + "y": 3620.1613309078502, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 122.3800048828125, + "height": 25, + "seed": 849176328, + "groupIds": [ + "NLJz2SpoJ7Ms7Qln__nzR", + "dCkmEueMCcBqZHb3-T--t" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes A1", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "8pfyaJ2A_7vTARYBesT5A", + "originalText": "TripTimes A1", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1590, + "versionNonce": 1122330847, + "index": "b8a", + "isDeleted": false, + "id": "OFeXVDEWAl37ggDxjyE4E", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1704.6574253579718, + "y": 3614.3986756781896, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 147.5, + "height": 35, + "seed": 1063994888, + "groupIds": [ + "NLJz2SpoJ7Ms7Qln__nzR", + "dCkmEueMCcBqZHb3-T--t" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "9Y5Tj5U76yBMALLtpFlIP", + "type": "text" + }, + { + "id": "z3BoCv0mDL67tzeFGQThQ", + "type": "arrow" + }, + { + "id": "IrPSz1lRXUthxsHHVEeUB", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1419, + "versionNonce": 1462327441, + "index": "b8b", + "isDeleted": false, + "id": "9Y5Tj5U76yBMALLtpFlIP", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1744.6774258157354, + "y": 3619.3986756781896, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 67.45999908447266, + "height": 25, + "seed": 1428505864, + "groupIds": [ + "NLJz2SpoJ7Ms7Qln__nzR", + "dCkmEueMCcBqZHb3-T--t" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Trip A1", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "OFeXVDEWAl37ggDxjyE4E", + "originalText": "Trip A1", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 4698, + "versionNonce": 220394751, + "index": "b8c", + "isDeleted": false, + "id": "z3BoCv0mDL67tzeFGQThQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1670.9074253579722, + "y": 3634.3986756781896, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 32.749999999999545, + "height": 0, + "seed": 1095197704, + "groupIds": [ + "NLJz2SpoJ7Ms7Qln__nzR", + "dCkmEueMCcBqZHb3-T--t" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": { + "elementId": "8pfyaJ2A_7vTARYBesT5A", + "focus": 0.09927684401939457, + "gap": 1.0593963242686186, + "fixedPoint": null + }, + "endBinding": { + "elementId": "OFeXVDEWAl37ggDxjyE4E", + "focus": -0.14285714285714285, + "gap": 1, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 32.749999999999545, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 1761, + "versionNonce": 178968127, + "index": "bAP", + "isDeleted": false, + "id": "QP6q3nSwWRoCojTNN5az0", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1703.3967906191187, + "y": 3241.8888753255214, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 313.00000000000006, + "height": 94.9999999999999, + "seed": 1513266952, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "QdjgGLaNGbZoQ8oki5VOv" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1694, + "versionNonce": 1791226161, + "index": "bAQ", + "isDeleted": false, + "id": "QdjgGLaNGbZoQ8oki5VOv", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1823.6007929994898, + "y": 3246.8888753255214, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 72.59199523925781, + "height": 20, + "seed": 798588424, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Requests", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "QP6q3nSwWRoCojTNN5az0", + "originalText": "Requests", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1788, + "versionNonce": 143898207, + "index": "bAR", + "isDeleted": false, + "id": "klIkVxJ3umra7Eb0tVBOt", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1912.3967906191187, + "y": 3275.8888753255214, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.05769230769234, + "height": 50, + "seed": 315391240, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "ikfMd-4cIoWTdXc2pWTPy" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1745, + "versionNonce": 1148232465, + "index": "bAS", + "isDeleted": false, + "id": "ikfMd-4cIoWTdXc2pWTPy", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1924.1696400688634, + "y": 3280.8888753255214, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 68.51199340820312, + "height": 40, + "seed": 893885448, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Thread 1\nIdle", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "klIkVxJ3umra7Eb0tVBOt", + "originalText": "Thread 1\nIdle", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1794, + "versionNonce": 1923846783, + "index": "bAT", + "isDeleted": false, + "id": "ffOVMbx0PfmTjw7yhhlAl", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1814.3967906191187, + "y": 3275.8888753255214, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.00000000000006, + "height": 50, + "seed": 956750600, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "YZAGaXGoLZd--hp-ciuio" + }, + { + "id": "CiX0AZeBKijw7sTLJmk2K", + "type": "arrow" + } + ], + "updated": 1721897849831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1764, + "versionNonce": 225188081, + "index": "bAU", + "isDeleted": false, + "id": "YZAGaXGoLZd--hp-ciuio", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1822.6127940370875, + "y": 3280.8888753255214, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 75.5679931640625, + "height": 40, + "seed": 545667592, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Thread 2\nGraphQL", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ffOVMbx0PfmTjw7yhhlAl", + "originalText": "Thread 2\nGraphQL", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1993, + "versionNonce": 1090564045, + "index": "bAV", + "isDeleted": false, + "id": "XGy0_rXBh7cutbuO56P81", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1716.3967906191187, + "y": 3275.8888753255214, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.08333333333327, + "height": 50, + "seed": 1052883208, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "9yonn8p7NXKeQ9k0U6d1W" + }, + { + "id": "0CosfiI9HbMzEOQ9l0xYa", + "type": "arrow" + } + ], + "updated": 1722610350358, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1963, + "versionNonce": 454673389, + "index": "bAW", + "isDeleted": false, + "id": "9yonn8p7NXKeQ9k0U6d1W", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1724.402461802387, + "y": 3280.8888753255214, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 76.07199096679688, + "height": 40, + "seed": 657807368, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722610349425, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Thread 3\nRouting", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "XGy0_rXBh7cutbuO56P81", + "originalText": "Thread 3\nRouting", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 1532, + "versionNonce": 39468735, + "index": "bAY", + "isDeleted": false, + "id": "CiX0AZeBKijw7sTLJmk2K", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1864.8967906191187, + "y": 3325.505208333334, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 385, + "height": 32, + "seed": 120911368, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897849831, + "link": null, + "locked": false, + "startBinding": { + "elementId": "ffOVMbx0PfmTjw7yhhlAl", + "focus": -0.7213445428660961, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "qrZ1nMWBuD5UnvhROXjPv", + "focus": 0.30776867516018896, + "gap": 6.021957943515872, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -122, + 28 + ], + [ + -385, + 32 + ] + ] + }, + { + "type": "arrow", + "version": 998, + "versionNonce": 4090051, + "index": "bAZ", + "isDeleted": false, + "id": "0CosfiI9HbMzEOQ9l0xYa", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1714.146790619119, + "y": 3299.740071614584, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 782.0000000000003, + "height": 61.25, + "seed": 842414344, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722610377484, + "link": null, + "locked": false, + "startBinding": { + "elementId": "XGy0_rXBh7cutbuO56P81", + "focus": -0.04606463466980531, + "gap": 2.249999999999659, + "fixedPoint": null + }, + "endBinding": { + "elementId": "IBLmqpELY-Pk0S_LG3wD4", + "focus": 0.2040943929075621, + "gap": 4.367259781381222, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -426.5000000000002, + -21.25 + ], + [ + -782.0000000000003, + 40 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/snapshot-manager-6.svg b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-6.svg new file mode 100644 index 00000000000..2fe7ef61716 --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-6.svg @@ -0,0 +1,13 @@ + + + + + + + + TripTimes A1'Timetable CTimetable BTimetableSnapshotMapTripPattern -> TimetablearrivalTimes per stopdepartureTimes per stopTimetable A1:N1:NLIVE SNAPSHOT SLIVE SNAPSHOT TTimetableSnapshotManagerList<TripTimes>TimetableSnapshot'MapTripPattern -> TimetableTimetable A'1:(N-1)List<TripTimes>1:(N-1)departureTimes' per stopBUFFERTimetableSnapshot''1:(N-2)MapTripPattern -> TimetableTripTimes B3'Timetable B'List<TripTimes>departureTimes' per stop1:(N-1)TripTimes A3Trip A3TripTimes A2Trip A2TripTimes A1Trip A1RequestsThread 1IdleThread 2GraphQLThread 3Routing \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/snapshot-manager-7.excalidraw b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-7.excalidraw new file mode 100644 index 00000000000..b8e239509b6 --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-7.excalidraw @@ -0,0 +1,4082 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "m36NsYlpuX6ScM-jScigG", + "type": "arrow", + "x": 988.0816660836274, + "y": 4358.262167739868, + "width": 422, + "height": 2, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b6J", + "roundness": null, + "seed": 980683555, + "version": 140, + "versionNonce": 1593830541, + "isDeleted": false, + "boundElements": null, + "updated": 1722611531003, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 422, + -2 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "T0fBO9HgjrpjYcRnYd32c", + "focus": 0.05894058145756906, + "gap": 2.254484326957538, + "fixedPoint": null + }, + "endBinding": { + "elementId": "xseBKAqjfVCnc0n1kaeTI", + "focus": -0.035313640889478914, + "gap": 4.25, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "type": "arrow", + "version": 10071, + "versionNonce": 532133795, + "index": "b6K", + "isDeleted": false, + "id": "8lmenDS1Kf8jfCdOyXhuH", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 878.5346229653505, + "y": 4376.098192103746, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 411.0425587913187, + "height": 50.63683271422633, + "seed": 1717763448, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722611333064, + "link": null, + "locked": false, + "startBinding": { + "elementId": "T0fBO9HgjrpjYcRnYd32c", + "focus": 0.5319368548104111, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "KYp9JX7coB4_88rjI_Dbw", + "focus": -0.03906544600993551, + "gap": 2.2976509189338685, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 73.42255879131915, + 44.21551724137953 + ], + [ + 411.0425587913187, + 50.63683271422633 + ] + ] + }, + { + "type": "arrow", + "version": 8943, + "versionNonce": 938633955, + "index": "b6L", + "isDeleted": false, + "id": "qhburfe3IrPlMsncO2Xl5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 871.230382531405, + "y": 4376.098192103746, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 44.919516877770775, + "height": 89.05415530593928, + "seed": 353668728, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722611333064, + "link": null, + "locked": false, + "startBinding": { + "elementId": "T0fBO9HgjrpjYcRnYd32c", + "focus": 0.43849555750987707, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "OVUvG3Nf0-7eqYPcTTtKA", + "focus": -0.8219419899689812, + "gap": 2.8452510974944403, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 3.0679543777710023, + 68.78947439428521 + ], + [ + 44.919516877770775, + 89.05415530593928 + ] + ] + }, + { + "type": "rectangle", + "version": 3090, + "versionNonce": 1571837731, + "index": "b6M", + "isDeleted": false, + "id": "T0fBO9HgjrpjYcRnYd32c", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 825.82718175667, + "y": 4340.098192103746, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 159.99999999999994, + "height": 35, + "seed": 2037120888, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "1wsW-35X41Bl_mQ0FwILO" + }, + { + "id": "8lmenDS1Kf8jfCdOyXhuH", + "type": "arrow" + }, + { + "id": "52TPbAX-TQHnA_lf_8kxN", + "type": "arrow" + }, + { + "id": "qhburfe3IrPlMsncO2Xl5", + "type": "arrow" + }, + { + "id": "m36NsYlpuX6ScM-jScigG", + "type": "arrow" + } + ], + "updated": 1722611485446, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2983, + "versionNonce": 1061553059, + "index": "b6N", + "isDeleted": false, + "id": "1wsW-35X41Bl_mQ0FwILO", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 840.9771794678517, + "y": 4345.098192103746, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 129.70000457763672, + "height": 25, + "seed": 598560888, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722611323167, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes A1'", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "T0fBO9HgjrpjYcRnYd32c", + "originalText": "TripTimes A1'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2256, + "versionNonce": 1996523373, + "index": "b6Z", + "isDeleted": false, + "id": "fjNSDkRN2ZZN8ej7D8ool", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1253.2795308377372, + "y": 4115.64400851652, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 1244617848, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "wUKHj3U1gMSa5QrKH3PiI" + } + ], + "updated": 1722611022526, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2133, + "versionNonce": 364319181, + "index": "b6a", + "isDeleted": false, + "id": "wUKHj3U1gMSa5QrKH3PiI", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1295.059529617034, + "y": 4120.64400851652, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 117.94000244140625, + "height": 25, + "seed": 503123320, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722611022526, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable C", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "fjNSDkRN2ZZN8ej7D8ool", + "originalText": "Timetable C", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2295, + "versionNonce": 1009811565, + "index": "b6b", + "isDeleted": false, + "id": "kNYagFX_A60xiU0-HnMxP", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1232.5295308377372, + "y": 4137.14400851652, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 1861000824, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "1aFlCPq7lB5Df1t6j76BW" + }, + { + "id": "Fc6OFOAYN3KBgdVnPheAp", + "type": "arrow" + }, + { + "id": "BnnN9xZBuIjWfEiNq7MVp", + "type": "arrow" + } + ], + "updated": 1722611132544, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2143, + "versionNonce": 1703723661, + "index": "b6c", + "isDeleted": false, + "id": "1aFlCPq7lB5Df1t6j76BW", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1273.4795277859794, + "y": 4142.14400851652, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 119.60000610351562, + "height": 25, + "seed": 2109869944, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722611022526, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable B", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "kNYagFX_A60xiU0-HnMxP", + "originalText": "Timetable B", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1768, + "versionNonce": 1854321681, + "index": "b6d", + "isDeleted": false, + "id": "auoJA7PLZIlb1tFKHcLwb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1708.1248326756022, + "y": 4018.6368579223754, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "width": 238.75, + "height": 101.25000000000001, + "seed": 1388404856, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "aUG8PHFlVjdefDzdgMRC6" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1743, + "versionNonce": 695931263, + "index": "b6e", + "isDeleted": false, + "id": "aUG8PHFlVjdefDzdgMRC6", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1736.059830234196, + "y": 4023.6368579223754, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 182.8800048828125, + "height": 25, + "seed": 1303286136, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshot", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "auoJA7PLZIlb1tFKHcLwb", + "originalText": "TimetableSnapshot", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1375, + "versionNonce": 211290609, + "index": "b6f", + "isDeleted": false, + "id": "7f2tfeZEFiY-2l5IU603E", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1722.5592289998713, + "y": 4057.4690302789213, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "width": 212.0000000000001, + "height": 50, + "seed": 902668920, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "mTmfsJhvO3wim7Um6mk3W" + }, + { + "id": "dP5SUVWzZublpEVXF9iyF", + "type": "arrow" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1265, + "versionNonce": 809142687, + "index": "b6g", + "isDeleted": false, + "id": "mTmfsJhvO3wim7Um6mk3W", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1728.583223445672, + "y": 4062.4690302789213, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 199.95201110839844, + "height": 40, + "seed": 1239585656, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "7f2tfeZEFiY-2l5IU603E", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2322, + "versionNonce": 1841538001, + "index": "b6h", + "isDeleted": false, + "id": "KYp9JX7coB4_88rjI_Dbw", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1291.874832675603, + "y": 4411.130924001535, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 277.5, + "height": 35, + "seed": 1345414264, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "WL9AGz2prDCr4ibApw2u7", + "type": "text" + }, + { + "id": "8lmenDS1Kf8jfCdOyXhuH", + "type": "arrow" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2035, + "versionNonce": 2090204607, + "index": "b6i", + "isDeleted": false, + "id": "WL9AGz2prDCr4ibApw2u7", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1324.9248204685719, + "y": 4416.130924001535, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 211.4000244140625, + "height": 25, + "seed": 645797240, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "arrivalTimes per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "KYp9JX7coB4_88rjI_Dbw", + "originalText": "arrivalTimes per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 1142, + "versionNonce": 1042350513, + "index": "b6t", + "isDeleted": false, + "id": "AmlWZ8pnpuloW86GJ6aoi", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 1630.9936253241403, + "y": 3969.9527758911254, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 529.0916748046875, + "seed": 1337290872, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 529.0916748046875 + ] + ] + }, + { + "type": "text", + "version": 807, + "versionNonce": 1907224031, + "index": "b6u", + "isDeleted": false, + "id": "1fHwqVMsjuCKCqVluuqnn", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 1676.2436253241403, + "y": 3974.466763585805, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 295.66400146484375, + "height": 20, + "seed": 832610680, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "GARBAGE COLLECTED SNAPSHOT S", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "GARBAGE COLLECTED SNAPSHOT S", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 751, + "versionNonce": 987210641, + "index": "b6v", + "isDeleted": false, + "id": "ECmpyWn9nUioi4YYRlKdc", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 1045.74362532414, + "y": 3974.466763585805, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 150.3520050048828, + "height": 20, + "seed": 1895674488, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "LIVE SNAPSHOT T", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "LIVE SNAPSHOT T", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 561, + "versionNonce": 982702591, + "index": "b6w", + "isDeleted": false, + "id": "Adk_QHgDHj49S_-1Bm-ZS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 544.74362532414, + "y": 3916.4527758911254, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 211.0240020751953, + "height": 20, + "seed": 1150329720, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "TimetableSnapshotManager", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "TimetableSnapshotManager", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1939, + "versionNonce": 270469489, + "index": "b74", + "isDeleted": false, + "id": "oBLckJSnaUNO81TQ2L-4W", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 682.0295308377371, + "y": 4019.3940085165204, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 238.75, + "height": 101.25000000000001, + "seed": 1872472952, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "H1HR9XVK4I5bYD4KixTd3" + }, + { + "id": "IzaVktnQxyblcMhxBW4Za", + "type": "arrow" + }, + { + "id": "zHAjVbfPpALT9hJOxF9di", + "type": "arrow" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1910, + "versionNonce": 1279607327, + "index": "b75", + "isDeleted": false, + "id": "H1HR9XVK4I5bYD4KixTd3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 707.3045247342214, + "y": 4024.3940085165204, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 188.20001220703125, + "height": 25, + "seed": 252148856, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshot'", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "oBLckJSnaUNO81TQ2L-4W", + "originalText": "TimetableSnapshot'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1536, + "versionNonce": 2000597841, + "index": "b76", + "isDeleted": false, + "id": "vOodK3Xsv5VGAegYKYTzQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 696.4639271620057, + "y": 4058.2261808730664, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 212.0000000000001, + "height": 50, + "seed": 1389440376, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Fn1D5VJvVVvt60VUgwLjY" + }, + { + "id": "BnnN9xZBuIjWfEiNq7MVp", + "type": "arrow" + }, + { + "id": "IzaVktnQxyblcMhxBW4Za", + "type": "arrow" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1421, + "versionNonce": 666215999, + "index": "b77", + "isDeleted": false, + "id": "Fn1D5VJvVVvt60VUgwLjY", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 702.4879216078065, + "y": 4063.2261808730664, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 199.95201110839844, + "height": 40, + "seed": 876396152, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "vOodK3Xsv5VGAegYKYTzQ", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2212, + "versionNonce": 344858929, + "index": "b78", + "isDeleted": false, + "id": "PtA0ttCnSIvUcY7Eg0p4_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 748.0295308377368, + "y": 4184.39400851652, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 1678651256, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "XVrkjaoY40SfV7hKlcotx" + }, + { + "id": "IzaVktnQxyblcMhxBW4Za", + "type": "arrow" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2096, + "versionNonce": 1539606111, + "index": "b79", + "isDeleted": false, + "id": "XVrkjaoY40SfV7hKlcotx", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 787.0295270230396, + "y": 4189.39400851652, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.50000762939453, + "height": 25, + "seed": 1518960760, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable A'", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "PtA0ttCnSIvUcY7Eg0p4_", + "originalText": "Timetable A'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 12658, + "versionNonce": 359977745, + "index": "b7A", + "isDeleted": false, + "id": "wSRAZDDe5sgZei_Gf9Hcg", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 783.614749594225, + "y": 4259.976180873066, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 448.7027110496688, + "height": 59.15224118576771, + "seed": 1735569784, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "rKzRJwgAHZQ5et6SrV7iD" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false, + "startBinding": { + "elementId": "FeUVva2kt8v2YJ3tyUww7", + "focus": 0.8997645215512046, + "gap": 1, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 249.96144082210287, + 55.002745568541286 + ], + [ + 448.7027110496688, + 59.15224118576771 + ] + ] + }, + { + "type": "text", + "version": 127, + "versionNonce": 1195532927, + "index": "b7B", + "isDeleted": false, + "id": "rKzRJwgAHZQ5et6SrV7iD", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1012.8401903552927, + "y": 4304.978926441608, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 41.47200012207031, + "height": 20, + "seed": 1108198008, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:(N-1)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "wSRAZDDe5sgZei_Gf9Hcg", + "originalText": "1:(N-1)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 9429, + "versionNonce": 1785029325, + "index": "b7C", + "isDeleted": false, + "id": "IzaVktnQxyblcMhxBW4Za", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 717.0120424008122, + "y": 4109.226180873066, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 49.538509471962925, + "height": 111.2790770869342, + "seed": 421442424, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722611200978, + "link": null, + "locked": false, + "startBinding": { + "elementId": "vOodK3Xsv5VGAegYKYTzQ", + "focus": 0.7224341472396456, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "FeUVva2kt8v2YJ3tyUww7", + "focus": -0.20126190215461456, + "gap": 14.309396324268619, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -19.771021035038075, + 98.16782764345317 + ], + [ + 29.76748843692485, + 111.2790770869342 + ] + ] + }, + { + "type": "rectangle", + "version": 1883, + "versionNonce": 451918495, + "index": "b7D", + "isDeleted": false, + "id": "FeUVva2kt8v2YJ3tyUww7", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 761.0889271620057, + "y": 4218.976180873066, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 585729144, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "-6QOg3YO7zzIsYJ9Av2pm" + }, + { + "id": "52TPbAX-TQHnA_lf_8kxN", + "type": "arrow" + }, + { + "id": "vFA0W03hnAoy9SafOWCta", + "type": "arrow" + }, + { + "id": "IzaVktnQxyblcMhxBW4Za", + "type": "arrow" + }, + { + "id": "wSRAZDDe5sgZei_Gf9Hcg", + "type": "arrow" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1859, + "versionNonce": 418775761, + "index": "b7E", + "isDeleted": false, + "id": "-6QOg3YO7zzIsYJ9Av2pm", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 787.9249295118593, + "y": 4228.976180873066, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.32799530029297, + "height": 20, + "seed": 758265208, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "FeUVva2kt8v2YJ3tyUww7", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 11323, + "versionNonce": 584323, + "index": "b7F", + "isDeleted": false, + "id": "BnnN9xZBuIjWfEiNq7MVp", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 719.901113743039, + "y": 4109.226180873066, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 503.0671946744179, + "height": 46.88664530551523, + "seed": 758957688, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "vFWe5sdUydOmyUKBZhXz4" + } + ], + "updated": 1722611367235, + "link": null, + "locked": false, + "startBinding": { + "elementId": "vOodK3Xsv5VGAegYKYTzQ", + "focus": 0.8592954704224408, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "kNYagFX_A60xiU0-HnMxP", + "focus": 0.4860623460457817, + "gap": 9.561222420280274, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 75.52777383421324, + 40.035789141170426 + ], + [ + 503.0671946744179, + 46.88664530551523 + ] + ] + }, + { + "type": "text", + "version": 76, + "versionNonce": 898543875, + "index": "b7G", + "isDeleted": false, + "id": "vFWe5sdUydOmyUKBZhXz4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 887.6928875162171, + "y": 4137.261970014237, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 42.47200012207031, + "height": 20, + "seed": 1338849144, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722611028536, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:(N-1)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "BnnN9xZBuIjWfEiNq7MVp", + "originalText": "1:(N-1)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 1405, + "versionNonce": 1271319363, + "index": "b7H", + "isDeleted": false, + "id": "52TPbAX-TQHnA_lf_8kxN", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 780.3883949383536, + "y": 4259.976180873066, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 41.93878681831643, + "height": 94.52348671237723, + "seed": 153585784, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722611333064, + "link": null, + "locked": false, + "startBinding": { + "elementId": "FeUVva2kt8v2YJ3tyUww7", + "focus": 0.7849018878692015, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "T0fBO9HgjrpjYcRnYd32c", + "focus": -0.6122467605988894, + "gap": 3.499999999999943, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 3.93878681831643, + 79.31826982274652 + ], + [ + 41.93878681831643, + 94.52348671237723 + ] + ] + }, + { + "type": "rectangle", + "version": 2472, + "versionNonce": 1306876973, + "index": "b7I", + "isDeleted": false, + "id": "OVUvG3Nf0-7eqYPcTTtKA", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 918.9951505066703, + "y": 4446.044450695813, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 264.41406249999994, + "height": 35, + "seed": 621787512, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "GIbDGPAm5blpDcVCSYv5c", + "type": "text" + }, + { + "id": "qhburfe3IrPlMsncO2Xl5", + "type": "arrow" + } + ], + "updated": 1722610878831, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2067, + "versionNonce": 867431661, + "index": "b7J", + "isDeleted": false, + "id": "GIbDGPAm5blpDcVCSYv5c", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 926.6721677185843, + "y": 4451.044450695813, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 249.06002807617188, + "height": 25, + "seed": 1456065144, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722610878832, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "departureTimes' per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "OVUvG3Nf0-7eqYPcTTtKA", + "originalText": "departureTimes' per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 1012, + "versionNonce": 1666828401, + "index": "b7K", + "isDeleted": false, + "id": "7FYGTiap7i-d6iJi2RGVw", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 648.4022519680913, + "y": 3972.3927352324126, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 529.0916748046875, + "seed": 1226423160, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 529.0916748046875 + ] + ] + }, + { + "type": "text", + "version": 285, + "versionNonce": 1851388703, + "index": "b7L", + "isDeleted": false, + "id": "x-kiTAxMWVdBr9kiIIBBp", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 375.3807053782706, + "y": 3974.466763585805, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 63.05599594116211, + "height": 20, + "seed": 776178808, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "BUFFER", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "BUFFER", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2208, + "versionNonce": 852070993, + "index": "b7M", + "isDeleted": false, + "id": "ZkxoNKEHZvEXMtdrd9jN5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 142.2557053782706, + "y": 4022.716763585805, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 238.75, + "height": 101.25000000000001, + "seed": 1342913912, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "kwPecR9wOO03Fj2Fco6R9" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2183, + "versionNonce": 1712076607, + "index": "b7N", + "isDeleted": false, + "id": "kwPecR9wOO03Fj2Fco6R9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 164.8706956126456, + "y": 4027.716763585805, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 193.52001953125, + "height": 25, + "seed": 531943032, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshot''", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "ZkxoNKEHZvEXMtdrd9jN5", + "originalText": "TimetableSnapshot''", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 10170, + "versionNonce": 1225547981, + "index": "b7O", + "isDeleted": false, + "id": "vFA0W03hnAoy9SafOWCta", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 193.4145618529211, + "y": 4112.548935942351, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 553.0064109954837, + "height": 108.65541916543862, + "seed": 1174683512, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722611181596, + "link": null, + "locked": false, + "startBinding": { + "elementId": "dKKA7p6mdTFywuebkUffo", + "focus": 0.83108659406428, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "FeUVva2kt8v2YJ3tyUww7", + "focus": 0.3850066057289207, + "gap": 14.66795431360083, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 333.4679015235207, + 92.54416972195668 + ], + [ + 553.0064109954837, + 108.65541916543862 + ] + ] + }, + { + "type": "arrow", + "version": 12820, + "versionNonce": 1395553165, + "index": "b7P", + "isDeleted": false, + "id": "Fc6OFOAYN3KBgdVnPheAp", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 192.4232473027888, + "y": 4112.548935942351, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 925.0875089411023, + "height": 65.80355223974675, + "seed": 859672696, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "UrMx9JtmX1EU9nURBmYgH" + } + ], + "updated": 1722611162345, + "link": null, + "locked": false, + "startBinding": { + "elementId": "dKKA7p6mdTFywuebkUffo", + "focus": 0.8671725859637449, + "gap": 1, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 253.89708228513143, + 50.66213121967394 + ], + [ + 925.0875089411023, + 65.80355223974675 + ] + ] + }, + { + "type": "text", + "version": 81, + "versionNonce": 1951059853, + "index": "b7Q", + "isDeleted": false, + "id": "UrMx9JtmX1EU9nURBmYgH", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 565.0563277416068, + "y": 4162.211067162025, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 49.52800369262695, + "height": 20, + "seed": 790937976, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722610976707, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:(N-2)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Fc6OFOAYN3KBgdVnPheAp", + "originalText": "1:(N-2)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1797, + "versionNonce": 468235135, + "index": "b7S", + "isDeleted": false, + "id": "dKKA7p6mdTFywuebkUffo", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 156.6901017025392, + "y": 4061.548935942351, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 212.0000000000001, + "height": 50, + "seed": 126665592, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "1uyZGajDTU3jUy2eTZCnF" + }, + { + "id": "Fc6OFOAYN3KBgdVnPheAp", + "type": "arrow" + }, + { + "id": "vFA0W03hnAoy9SafOWCta", + "type": "arrow" + }, + { + "id": "aAzZ831AIbPm0jF_9HTGo", + "type": "arrow" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1681, + "versionNonce": 2060290033, + "index": "b7T", + "isDeleted": false, + "id": "1uyZGajDTU3jUy2eTZCnF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 162.71409614834, + "y": 4066.548935942351, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 199.95201110839844, + "height": 40, + "seed": 957715576, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "dKKA7p6mdTFywuebkUffo", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 3252, + "versionNonce": 144960415, + "index": "b7U", + "isDeleted": false, + "id": "kciMVuvMM_O6AF61MK8N0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 282.35160248300383, + "y": 4338.796959807145, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 159.99999999999994, + "height": 35, + "seed": 1130120568, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "O7HkBxnsKLsrmcHk7BePy" + }, + { + "id": "EGpxgg0tyXe6CRjPwbVRo", + "type": "arrow" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 3153, + "versionNonce": 882467281, + "index": "b7V", + "isDeleted": false, + "id": "O7HkBxnsKLsrmcHk7BePy", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 293.69159882089446, + "y": 4343.796959807145, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 137.32000732421875, + "height": 25, + "seed": 329569912, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes B3'", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "kciMVuvMM_O6AF61MK8N0", + "originalText": "TripTimes B3'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2569, + "versionNonce": 92479423, + "index": "b7W", + "isDeleted": false, + "id": "_ZSpXUEOA9saZCgmQN8bA", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 208.05395156407042, + "y": 4184.092776219919, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 201.5, + "height": 83.75000000000006, + "seed": 1320372088, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "iHEDLF8fkFY7pKix3vyj4" + }, + { + "id": "aAzZ831AIbPm0jF_9HTGo", + "type": "arrow" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2452, + "versionNonce": 1603207089, + "index": "b7X", + "isDeleted": false, + "id": "iHEDLF8fkFY7pKix3vyj4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 246.3439486649005, + "y": 4189.092776219919, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 124.92000579833984, + "height": 25, + "seed": 1623955576, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable B'", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "_ZSpXUEOA9saZCgmQN8bA", + "originalText": "Timetable B'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2198, + "versionNonce": 1688653791, + "index": "b7Y", + "isDeleted": false, + "id": "pC5lSd9GKiP_iF-voqacy", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 221.1133478883395, + "y": 4218.674948576465, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 229755256, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Zf_MYB6kvax73gyUEGxlx" + }, + { + "id": "EGpxgg0tyXe6CRjPwbVRo", + "type": "arrow" + }, + { + "id": "aAzZ831AIbPm0jF_9HTGo", + "type": "arrow" + }, + { + "id": "ZzFUKJcQusPbTxmaBHXzQ", + "type": "arrow" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2163, + "versionNonce": 970318225, + "index": "b7Z", + "isDeleted": false, + "id": "Zf_MYB6kvax73gyUEGxlx", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 247.94935023819312, + "y": 4228.674948576465, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.32799530029297, + "height": 20, + "seed": 11131512, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "pC5lSd9GKiP_iF-voqacy", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 3175, + "versionNonce": 1612676095, + "index": "b7a", + "isDeleted": false, + "id": "EGpxgg0tyXe6CRjPwbVRo", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 233.8548211503297, + "y": 4259.674948576465, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 46.24678133267395, + "height": 97.38347328639247, + "seed": 1551354744, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "startBinding": { + "elementId": "pC5lSd9GKiP_iF-voqacy", + "focus": 0.8564092525909839, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "kciMVuvMM_O6AF61MK8N0", + "focus": -0.5727004481815319, + "gap": 2.250000000000199, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0.7467813326739474, + 85.81826982274652 + ], + [ + 46.24678133267395, + 97.38347328639247 + ] + ] + }, + { + "type": "arrow", + "version": 15089, + "versionNonce": 1099984419, + "index": "b7d", + "isDeleted": false, + "id": "ZzFUKJcQusPbTxmaBHXzQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 235.3005932619447, + "y": 4259.674948576465, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 879.3980956844919, + "height": 71.91289663680072, + "seed": 286056056, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "jG6vWkyASdCwVuaPRWYCw" + } + ], + "updated": 1722611169795, + "link": null, + "locked": false, + "startBinding": { + "elementId": "pC5lSd9GKiP_iF-voqacy", + "focus": 0.9153790667146192, + "gap": 1, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 150.80328468386296, + 60.61627820682406 + ], + [ + 879.3980956844919, + 71.91289663680072 + ] + ] + }, + { + "type": "text", + "version": 140, + "versionNonce": 1170467875, + "index": "b7e", + "isDeleted": false, + "id": "jG6vWkyASdCwVuaPRWYCw", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 807.3678778847725, + "y": 4300.291226783289, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 42.47200012207031, + "height": 20, + "seed": 1910132600, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722611055279, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:(N-1)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ZzFUKJcQusPbTxmaBHXzQ", + "originalText": "1:(N-1)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 1804, + "versionNonce": 507867473, + "index": "b7g", + "isDeleted": false, + "id": "aAzZ831AIbPm0jF_9HTGo", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 183.35885349760258, + "y": 4112.548935942351, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 37.92927298303525, + "height": 111.83159276278366, + "seed": 1219940728, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "startBinding": { + "elementId": "dKKA7p6mdTFywuebkUffo", + "focus": 0.6875769442081427, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "pC5lSd9GKiP_iF-voqacy", + "focus": -0.32628010072350894, + "gap": 14.94028225676712, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -15.115060849065458, + 101.2409180566674 + ], + [ + 22.81421213396979, + 111.83159276278366 + ] + ] + }, + { + "type": "arrow", + "version": 983, + "versionNonce": 1744947263, + "index": "b7h", + "isDeleted": false, + "id": "dP5SUVWzZublpEVXF9iyF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1824.2437926485377, + "y": 4119.7249755859375, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 55.62914191995719, + "height": 81, + "seed": 123408648, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "pltwtkLpIocsL6gxpzw9G" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false, + "startBinding": { + "elementId": "7f2tfeZEFiY-2l5IU603E", + "focus": -0.22799127226475469, + "gap": 12.25594530701619, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n0LHPhfk8Ki4Hx3qHc5Og", + "focus": -0.19980692066112177, + "gap": 5.411882336437884, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -34.75, + 38.5 + ], + [ + -55.62914191995719, + 81 + ] + ] + }, + { + "type": "text", + "version": 62, + "versionNonce": 1745690417, + "index": "b7i", + "isDeleted": false, + "id": "pltwtkLpIocsL6gxpzw9G", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1787.121792907937, + "y": 4146.7249755859375, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 16.743999481201172, + "height": 35, + "seed": 1424614920, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 28, + "fontFamily": 1, + "text": "X", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "dP5SUVWzZublpEVXF9iyF", + "originalText": "X", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 3215, + "versionNonce": 1065754161, + "index": "b8n", + "isDeleted": false, + "id": "YZFm7t4O3H-9L337Lhgjk", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1680.5980290337036, + "y": 4347.41133090785, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "width": 159.99999999999994, + "height": 35, + "seed": 512901752, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "9pTCYbD2EB2muW8oUiLDp", + "type": "text" + }, + { + "id": "1CxLeMwxOAI5lYmnKZ61c", + "type": "arrow" + }, + { + "id": "cmv2Nov77KnAbkpGAIMLx", + "type": "arrow" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 3114, + "versionNonce": 2048317791, + "index": "b8o", + "isDeleted": false, + "id": "9pTCYbD2EB2muW8oUiLDp", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1699.4080265922973, + "y": 4352.41133090785, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 122.3800048828125, + "height": 25, + "seed": 1897083768, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes A1", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "YZFm7t4O3H-9L337Lhgjk", + "originalText": "TripTimes A1", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 3174, + "versionNonce": 1119981315, + "index": "b8o8", + "isDeleted": false, + "id": "MuKPNZil6V-J7WDTin5IS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1258.0801599391036, + "y": 4291.83325791556, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 159.99999999999994, + "height": 35, + "seed": 126351480, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "R1FstgH-lTSRt28fDpiFE", + "type": "text" + }, + { + "id": "iMOoaMzpvEjuK2akWs5pk", + "type": "arrow" + } + ], + "updated": 1722611346613, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 3077, + "versionNonce": 1864701613, + "index": "b8oG", + "isDeleted": false, + "id": "R1FstgH-lTSRt28fDpiFE", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1271.7901590235763, + "y": 4296.83325791556, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 132.5800018310547, + "height": 25, + "seed": 1575102840, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722611346613, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes A3", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "MuKPNZil6V-J7WDTin5IS", + "originalText": "TripTimes A3", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1831, + "versionNonce": 1381938851, + "index": "b8oO", + "isDeleted": false, + "id": "5ZIvvAS0Q_qQFzXgHdRi7", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1452.8895562633718, + "y": 4291.0706026859, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 147.5, + "height": 35, + "seed": 734935672, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "4Mne7TyJow6pAsh-hKPuK", + "type": "text" + }, + { + "id": "iMOoaMzpvEjuK2akWs5pk", + "type": "arrow" + } + ], + "updated": 1722611346613, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1668, + "versionNonce": 285718797, + "index": "b8oV", + "isDeleted": false, + "id": "4Mne7TyJow6pAsh-hKPuK", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1487.809554432317, + "y": 4296.0706026859, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 77.66000366210938, + "height": 25, + "seed": 1678781304, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722611346613, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Trip A3", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "5ZIvvAS0Q_qQFzXgHdRi7", + "originalText": "Trip A3", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 5988, + "versionNonce": 1613255789, + "index": "b8od", + "isDeleted": false, + "id": "iMOoaMzpvEjuK2akWs5pk", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1419.6394452270192, + "y": 4308.378797156383, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 31.75011055580444, + "height": 0.30819447048361326, + "seed": 15465592, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722611380903, + "link": null, + "locked": false, + "startBinding": { + "elementId": "RrtJyLdwFAelPMFkSjMhA", + "focus": -1.334048220503359, + "gap": 14.750111036352564, + "fixedPoint": null + }, + "endBinding": { + "elementId": "RrtJyLdwFAelPMFkSjMhA", + "focus": 1.185714285714286, + "gap": 3.25, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 31.75011055580444, + -0.30819447048361326 + ] + ] + }, + { + "type": "rectangle", + "version": 3077, + "versionNonce": 1999048557, + "index": "b8ol", + "isDeleted": false, + "id": "23rGuHqVP2mMyhkiIdrBH", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1237.5801599391036, + "y": 4315.08325791556, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 159.99999999999994, + "height": 35, + "seed": 338013560, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "FeigBurHZjpUJ_M7OH-WC", + "type": "text" + }, + { + "id": "4vtP7uMfEz2WQVL4pJ3iB", + "type": "arrow" + } + ], + "updated": 1722611346613, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2982, + "versionNonce": 1042655715, + "index": "b8p", + "isDeleted": false, + "id": "FeigBurHZjpUJ_M7OH-WC", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1250.980153835588, + "y": 4320.08325791556, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 133.20001220703125, + "height": 25, + "seed": 1459699320, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722611346613, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes A2", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "23rGuHqVP2mMyhkiIdrBH", + "originalText": "TripTimes A2", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1757, + "versionNonce": 1607244237, + "index": "b8pG", + "isDeleted": false, + "id": "RrtJyLdwFAelPMFkSjMhA", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1434.3895562633718, + "y": 4314.3206026859, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 147.5, + "height": 35, + "seed": 977448824, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "aA2Exu3ESiTY98OtKnplW", + "type": "text" + }, + { + "id": "4vtP7uMfEz2WQVL4pJ3iB", + "type": "arrow" + }, + { + "id": "iMOoaMzpvEjuK2akWs5pk", + "type": "arrow" + } + ], + "updated": 1722611346613, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1568, + "versionNonce": 1144812931, + "index": "b8pV", + "isDeleted": false, + "id": "aA2Exu3ESiTY98OtKnplW", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1468.9995568737233, + "y": 4319.3206026859, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 78.27999877929688, + "height": 25, + "seed": 1982726264, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722611346613, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Trip A2", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "RrtJyLdwFAelPMFkSjMhA", + "originalText": "Trip A2", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 5632, + "versionNonce": 1621686957, + "index": "b8q", + "isDeleted": false, + "id": "4vtP7uMfEz2WQVL4pJ3iB", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1399.147161539765, + "y": 4332.3206026859, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 34.299262000653016, + "height": 0.0297577129340425, + "seed": 445067640, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722611443878, + "link": null, + "locked": false, + "startBinding": { + "elementId": "23rGuHqVP2mMyhkiIdrBH", + "focus": -0.01897741788477643, + "gap": 1.5670016006613423, + "fixedPoint": null + }, + "endBinding": { + "elementId": "RrtJyLdwFAelPMFkSjMhA", + "focus": -0.03385113174052319, + "gap": 1, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 34.299262000653016, + 0.0297577129340425 + ] + ] + }, + { + "type": "rectangle", + "version": 1811, + "versionNonce": 1089549741, + "index": "b9b", + "isDeleted": false, + "id": "c7PBVGIHwoX35i_A1bagd", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1244.6467906191187, + "y": 4172.705769856772, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 1417494648, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "srFCb33tJkKtnb-fwhlSu" + } + ], + "updated": 1722611126312, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1778, + "versionNonce": 851264333, + "index": "b9c", + "isDeleted": false, + "id": "srFCb33tJkKtnb-fwhlSu", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1271.4827929689723, + "y": 4182.705769856772, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.32799530029297, + "height": 20, + "seed": 1827565944, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722611022526, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "c7PBVGIHwoX35i_A1bagd", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2392, + "versionNonce": 396291409, + "index": "b9cG", + "isDeleted": false, + "id": "n0LHPhfk8Ki4Hx3qHc5Og", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1668.874832675603, + "y": 4206.136857922375, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "width": 201.5, + "height": 83.75000000000006, + "seed": 1669294200, + "groupIds": [ + "Fvd1I_yaDedCq9XLD3rD6" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Y7sBxUlcPAYfZ1O7cEqAL" + }, + { + "id": "BnnN9xZBuIjWfEiNq7MVp", + "type": "arrow" + }, + { + "id": "Fc6OFOAYN3KBgdVnPheAp", + "type": "arrow" + }, + { + "id": "dP5SUVWzZublpEVXF9iyF", + "type": "arrow" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2266, + "versionNonce": 1745751103, + "index": "b9cV", + "isDeleted": false, + "id": "Y7sBxUlcPAYfZ1O7cEqAL", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1710.534828708318, + "y": 4211.136857922375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 118.18000793457031, + "height": 25, + "seed": 694917496, + "groupIds": [ + "Fvd1I_yaDedCq9XLD3rD6" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable A", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n0LHPhfk8Ki4Hx3qHc5Og", + "originalText": "Timetable A", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1842, + "versionNonce": 1893145393, + "index": "b9d", + "isDeleted": false, + "id": "kWEUbmd9BElG1hAoLf-Wt", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1681.9342289998713, + "y": 4240.719030278921, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 1788703864, + "groupIds": [ + "Fvd1I_yaDedCq9XLD3rD6" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "EUOfbZSU0mGzNFe935EKA" + }, + { + "id": "1CxLeMwxOAI5lYmnKZ61c", + "type": "arrow" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1811, + "versionNonce": 2108511, + "index": "b9e", + "isDeleted": false, + "id": "EUOfbZSU0mGzNFe935EKA", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1708.7702313497248, + "y": 4250.719030278921, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.32799530029297, + "height": 20, + "seed": 954738040, + "groupIds": [ + "Fvd1I_yaDedCq9XLD3rD6" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "kWEUbmd9BElG1hAoLf-Wt", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2481, + "versionNonce": 848203025, + "index": "b9h", + "isDeleted": false, + "id": "6YSRCBznBFitbCHTs48x9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1592.226395175603, + "y": 4447.386857922375, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "width": 275.66406249999994, + "height": 35, + "seed": 1376560760, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "BD4AHbGVZw3nAT-TFHqUu", + "type": "text" + }, + { + "id": "cmv2Nov77KnAbkpGAIMLx", + "type": "arrow" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2086, + "versionNonce": 1832470655, + "index": "b9i", + "isDeleted": false, + "id": "BD4AHbGVZw3nAT-TFHqUu", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1608.1884160496265, + "y": 4452.386857922375, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 243.74002075195312, + "height": 25, + "seed": 262215544, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "departureTimes per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "6YSRCBznBFitbCHTs48x9", + "originalText": "departureTimes per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 417, + "versionNonce": 339526385, + "index": "b9j", + "isDeleted": false, + "id": "1CxLeMwxOAI5lYmnKZ61c", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1806.1519678645916, + "y": 4281.719030278921, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 10.35741146116743, + "height": 60.69230062892893, + "seed": 419830536, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "startBinding": { + "elementId": "kWEUbmd9BElG1hAoLf-Wt", + "focus": -0.3952561383097287, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "YZFm7t4O3H-9L337Lhgjk", + "focus": 0.288111759624464, + "gap": 5, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 0.9948227545271493, + 38.98673957785013 + ], + [ + -9.362588706640281, + 60.69230062892893 + ] + ] + }, + { + "type": "arrow", + "version": 296, + "versionNonce": 1830412447, + "index": "b9k", + "isDeleted": false, + "id": "cmv2Nov77KnAbkpGAIMLx", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1768.3199257947663, + "y": 4383.41133090785, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 79.24483985246252, + "height": 62.97552701452514, + "seed": 743677816, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "startBinding": { + "elementId": "YZFm7t4O3H-9L337Lhgjk", + "focus": -0.3417503644837799, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "6YSRCBznBFitbCHTs48x9", + "focus": -0.37619871945656735, + "gap": 1, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -52.17313517564753, + 33.2944389489212 + ], + [ + -79.24483985246252, + 62.97552701452514 + ] + ] + }, + { + "type": "rectangle", + "version": 1833, + "versionNonce": 1044219661, + "index": "bAa", + "isDeleted": false, + "id": "RLtMFQHQ1CMfIsvZnMHk9", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1291.6467906191187, + "y": 3955.6159261067714, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 313.00000000000006, + "height": 94.9999999999999, + "seed": 608392968, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "zCesaPX0Zb-YFnqFR14b0" + } + ], + "updated": 1722610931159, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1765, + "versionNonce": 1483939181, + "index": "bAb", + "isDeleted": false, + "id": "zCesaPX0Zb-YFnqFR14b0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1411.8507929994898, + "y": 3960.6159261067714, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 72.59199523925781, + "height": 20, + "seed": 1725117960, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722610931159, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Requests", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "RLtMFQHQ1CMfIsvZnMHk9", + "originalText": "Requests", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1859, + "versionNonce": 409397197, + "index": "bAc", + "isDeleted": false, + "id": "0sRTfuhtx8Os-UcWhBKex", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1500.6467906191187, + "y": 3989.6159261067714, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.05769230769234, + "height": 50, + "seed": 705639688, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "96_H-DvK780Nmuxrj1Hze" + } + ], + "updated": 1722610931159, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1816, + "versionNonce": 271738413, + "index": "bAd", + "isDeleted": false, + "id": "96_H-DvK780Nmuxrj1Hze", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1512.4196400688634, + "y": 3994.6159261067714, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 68.51199340820312, + "height": 40, + "seed": 1131631624, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722610931159, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Thread 1\nIdle", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "0sRTfuhtx8Os-UcWhBKex", + "originalText": "Thread 1\nIdle", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1858, + "versionNonce": 1388255373, + "index": "bAe", + "isDeleted": false, + "id": "vPR-8aW3nQNW4sMuvPFxd", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1402.6467906191187, + "y": 3989.6159261067714, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.00000000000006, + "height": 50, + "seed": 2029753096, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "f3bJhtfsZ7XUWeEh_Q0M0" + } + ], + "updated": 1722610931159, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1839, + "versionNonce": 1506180845, + "index": "bAf", + "isDeleted": false, + "id": "f3bJhtfsZ7XUWeEh_Q0M0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1410.8627940370875, + "y": 3994.6159261067714, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 75.5679931640625, + "height": 40, + "seed": 1898181128, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722610931160, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Thread 2\nIdle", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "vPR-8aW3nQNW4sMuvPFxd", + "originalText": "Thread 2\nIdle", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2054, + "versionNonce": 735189325, + "index": "bAg", + "isDeleted": false, + "id": "sMi1X9Npv8ktGyp7rzgDX", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1304.6467906191187, + "y": 3989.6159261067714, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.08333333333327, + "height": 50, + "seed": 585658632, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "jkVEfKA-PrjVUeSmbXcAL" + }, + { + "id": "zHAjVbfPpALT9hJOxF9di", + "type": "arrow" + } + ], + "updated": 1722610931160, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2034, + "versionNonce": 791407117, + "index": "bAh", + "isDeleted": false, + "id": "jkVEfKA-PrjVUeSmbXcAL", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1313.152461802387, + "y": 3994.6159261067714, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 75.07199096679688, + "height": 40, + "seed": 1170542600, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722610931160, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Thread 3\nRouting", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "sMi1X9Npv8ktGyp7rzgDX", + "originalText": "Thread 3\nRouting", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 411, + "versionNonce": 1486667277, + "index": "bAi", + "isDeleted": false, + "id": "zHAjVbfPpALT9hJOxF9di", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1353.1198780620657, + "y": 4038.6159261067714, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 427.9730874429471, + "height": 44.5, + "seed": 452521992, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722610958123, + "link": null, + "locked": false, + "startBinding": { + "elementId": "sMi1X9Npv8ktGyp7rzgDX", + "focus": -0.45639587731983255, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "oBLckJSnaUNO81TQ2L-4W", + "focus": 0.286612650864895, + "gap": 4.367259781381563, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -57.03821197843831, + 38.646241633096906 + ], + [ + -427.9730874429471, + 44.5 + ] + ] + }, + { + "type": "rectangle", + "version": 3032, + "versionNonce": 1970148493, + "index": "bCS", + "isDeleted": false, + "id": "rMNn_6_fhDwV7wR5CW7Ii", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 350.807876044023, + "y": 4454.7999230425985, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 271.85727889788814, + "height": 35, + "seed": 1190701944, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "JHQ2GOiTNL3UfPEQqoViL", + "type": "text" + }, + { + "id": "OmcvBoM8oN4QloSf7ziRQ", + "type": "arrow" + } + ], + "updated": 1722610886238, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2626, + "versionNonce": 1161165549, + "index": "bCT", + "isDeleted": false, + "id": "JHQ2GOiTNL3UfPEQqoViL", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 362.2065014548811, + "y": 4459.7999230425985, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 249.06002807617188, + "height": 25, + "seed": 1925479544, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722610886238, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "departureTimes' per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "rMNn_6_fhDwV7wR5CW7Ii", + "originalText": "departureTimes' per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 3068, + "versionNonce": 166655921, + "index": "bCU", + "isDeleted": false, + "id": "O5B21AV3INv7xBmEmBYLE", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 350.94133144570236, + "y": 4412.95065309847, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 271.85727889788814, + "height": 35, + "seed": 921578872, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "dJhaaBlZ3l6bIdcXYawBc", + "type": "text" + }, + { + "id": "4d7tlgalMZnYayWDJFI1d", + "type": "arrow" + } + ], + "updated": 1721897719600, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2667, + "versionNonce": 174217183, + "index": "bCV", + "isDeleted": false, + "id": "dJhaaBlZ3l6bIdcXYawBc", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 378.50996265490033, + "y": 4417.95065309847, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 216.7200164794922, + "height": 25, + "seed": 213499512, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "arrivalTimes' per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "O5B21AV3INv7xBmEmBYLE", + "originalText": "arrivalTimes' per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 12664, + "versionNonce": 91675025, + "index": "bCW", + "isDeleted": false, + "id": "4d7tlgalMZnYayWDJFI1d", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 300.9235423232188, + "y": 4375.155102766562, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 48.178571428570194, + "height": 54.624825064200195, + "seed": 619392888, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1721897719600, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "O5B21AV3INv7xBmEmBYLE", + "focus": -0.6808758663412032, + "gap": 1.8392176939134117, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 14.844285714285206, + 45.34636673420846 + ], + [ + 48.178571428570194, + 54.624825064200195 + ] + ] + }, + { + "type": "arrow", + "version": 11874, + "versionNonce": 1945245005, + "index": "bCX", + "isDeleted": false, + "id": "OmcvBoM8oN4QloSf7ziRQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 300.9235423232188, + "y": 4377.255095176052, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 47.037003366792135, + "height": 92.7809706771859, + "seed": 2009497720, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722610886239, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "rMNn_6_fhDwV7wR5CW7Ii", + "focus": -0.7301207884223261, + "gap": 2.8473303540121435, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 11.471155152505958, + 79.24890290619624 + ], + [ + 47.037003366792135, + 92.7809706771859 + ] + ] + }, + { + "type": "rectangle", + "version": 1709, + "versionNonce": 1357458019, + "index": "bCb", + "isDeleted": false, + "id": "xseBKAqjfVCnc0n1kaeTI", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1414.3316660836274, + "y": 4337.762167739868, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 147.5, + "height": 35, + "seed": 1006760813, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "F9m9F9UuizfFBw7hq5P1N", + "type": "text" + }, + { + "id": "m36NsYlpuX6ScM-jScigG", + "type": "arrow" + } + ], + "updated": 1722611485447, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1517, + "versionNonce": 1367217283, + "index": "bCc", + "isDeleted": false, + "id": "F9m9F9UuizfFBw7hq5P1N", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1453.351666541391, + "y": 4342.762167739868, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 69.45999908447266, + "height": 25, + "seed": 262864333, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722611464847, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Trip A1", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "xseBKAqjfVCnc0n1kaeTI", + "originalText": "Trip A1", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/snapshot-manager-7.svg b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-7.svg new file mode 100644 index 00000000000..c41c7b90430 --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-7.svg @@ -0,0 +1,13 @@ + + + + + + + + TripTimes A1'Timetable CTimetable BTimetableSnapshotMapTripPattern -> TimetablearrivalTimes per stopGARBAGE COLLECTED SNAPSHOT SLIVE SNAPSHOT TTimetableSnapshotManagerTimetableSnapshot'MapTripPattern -> TimetableTimetable A'1:(N-1)List<TripTimes>1:(N-1)departureTimes' per stopBUFFERTimetableSnapshot''1:(N-2)MapTripPattern -> TimetableTripTimes B3'Timetable B'List<TripTimes>1:(N-1)XTripTimes A1TripTimes A3Trip A3TripTimes A2Trip A2List<TripTimes>Timetable AList<TripTimes>departureTimes per stopRequestsThread 1IdleThread 2IdleThread 3RoutingdepartureTimes' per stoparrivalTimes' per stopTrip A1 \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/snapshot-manager-8.excalidraw b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-8.excalidraw new file mode 100644 index 00000000000..e245ede1ff8 --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-8.excalidraw @@ -0,0 +1,3115 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "type": "arrow", + "version": 12225, + "versionNonce": 1405755768, + "index": "bAj", + "isDeleted": false, + "id": "et-jjXyrBCTVdhIz5_8J2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 997.8006516966368, + "y": 5135.603875693866, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 46.41308316695222, + "height": 51.33862689455509, + "seed": 896194936, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1719564120995, + "link": null, + "locked": false, + "startBinding": { + "elementId": "MSSwkfn9q-khwQHGKLFla", + "focus": 0.7045699676811383, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "0puDj3KX9zzglmpXd6N20", + "focus": -0.502327224891324, + "gap": 2.2976509189343233, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 14.078797452667231, + 47.07266009852083 + ], + [ + 46.41308316695222, + 51.33862689455509 + ] + ] + }, + { + "type": "arrow", + "version": 11435, + "versionNonce": 993564936, + "index": "bAk", + "isDeleted": false, + "id": "p91Sr2LGdNSTmvnlOMrlj", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 997.1026478328511, + "y": 5133.461018551008, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 45.96951896895962, + "height": 94.62226727162215, + "seed": 1858136696, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1719564139366, + "link": null, + "locked": false, + "startBinding": { + "elementId": "MSSwkfn9q-khwQHGKLFla", + "focus": 0.6980874845683128, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "ZUnsjJKp6zyhV7AKtQceV", + "focus": -0.689165037186275, + "gap": 2.845251097494497, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 12.832242183244944, + 84.50376010857053 + ], + [ + 45.96951896895962, + 94.62226727162215 + ] + ] + }, + { + "type": "rectangle", + "version": 2511, + "versionNonce": 269090312, + "index": "bAo", + "isDeleted": false, + "id": "RPTsk1-34cICoZKhHjxFB", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 924.9160839446574, + "y": 4902.578263535211, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 1931210360, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "O61ZGWaqBaGf1Z9W_XLQF" + }, + { + "id": "6CkeAFNIVwfpWrKtpD15J", + "type": "arrow" + } + ], + "updated": 1719563181628, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2387, + "versionNonce": 1824124936, + "index": "bAp", + "isDeleted": false, + "id": "O61ZGWaqBaGf1Z9W_XLQF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 966.6960827239543, + "y": 4907.578263535211, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 117.94000244140625, + "height": 25, + "seed": 1602707320, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719563151759, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable C", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "RPTsk1-34cICoZKhHjxFB", + "originalText": "Timetable C", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2550, + "versionNonce": 355394925, + "index": "bAq", + "isDeleted": false, + "id": "9WgYmoawkgl_Qu_9DSElx", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 904.1660839446574, + "y": 4924.078263535211, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 1868345464, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Hu51i7eqpNy74O32Soz01" + } + ], + "updated": 1722612218619, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2397, + "versionNonce": 1987284488, + "index": "bAr", + "isDeleted": false, + "id": "Hu51i7eqpNy74O32Soz01", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 945.1160808928996, + "y": 4929.078263535211, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 119.60000610351562, + "height": 25, + "seed": 1516233080, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719563151759, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable B", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "9WgYmoawkgl_Qu_9DSElx", + "originalText": "Timetable B", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2712, + "versionNonce": 578920312, + "index": "bAw", + "isDeleted": false, + "id": "0puDj3KX9zzglmpXd6N20", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1046.5113857825233, + "y": 5170.065179020224, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 277.5, + "height": 35, + "seed": 904656504, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "eRdnJ2F1kXVUQzav0SZBA", + "type": "text" + }, + { + "id": "et-jjXyrBCTVdhIz5_8J2", + "type": "arrow" + } + ], + "updated": 1719564120995, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2425, + "versionNonce": 301412984, + "index": "bAx", + "isDeleted": false, + "id": "eRdnJ2F1kXVUQzav0SZBA", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1079.561373575492, + "y": 5175.065179020224, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 211.4000244140625, + "height": 25, + "seed": 1528194936, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719564120995, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "arrivalTimes per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "0puDj3KX9zzglmpXd6N20", + "originalText": "arrivalTimes per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 832, + "versionNonce": 472982280, + "index": "bB0", + "isDeleted": false, + "id": "kj6Xn5qLCkRRCaMRW9W_n", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 1052.8087498596315, + "y": 4709.686732890207, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 150.3520050048828, + "height": 20, + "seed": 912325240, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719562801045, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "LIVE SNAPSHOT T", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "LIVE SNAPSHOT T", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 642, + "versionNonce": 925336072, + "index": "bB1", + "isDeleted": false, + "id": "KS8i1FS-2-MmCzTGmOLJD", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 551.8087498596313, + "y": 4651.672745195529, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 211.0240020751953, + "height": 20, + "seed": 239790968, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719562801045, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "TimetableSnapshotManager", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "TimetableSnapshotManager", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2022, + "versionNonce": 1453940232, + "index": "bB2", + "isDeleted": false, + "id": "PNbwzZX7FC2TGjF0aCqdZ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 689.0946553732282, + "y": 4754.613977820924, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 238.75, + "height": 101.25000000000001, + "seed": 365870200, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "5Z18ehZ8fWKGmOJcHlyP_" + }, + { + "id": "G6zekguWswrA5JhKt_dcK", + "type": "arrow" + } + ], + "updated": 1719564233980, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1991, + "versionNonce": 363028232, + "index": "bB3", + "isDeleted": false, + "id": "5Z18ehZ8fWKGmOJcHlyP_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 714.3696492697126, + "y": 4759.613977820924, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 188.20001220703125, + "height": 25, + "seed": 2125491576, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719562801045, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshot'", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "PNbwzZX7FC2TGjF0aCqdZ", + "originalText": "TimetableSnapshot'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1618, + "versionNonce": 1410239240, + "index": "bB4", + "isDeleted": false, + "id": "PbJmhoHc3kcZwiar9XZHC", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 703.5290516974968, + "y": 4793.44615017747, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 212.0000000000001, + "height": 50, + "seed": 573832824, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Hp767-gf6VbeLtPVso7mO" + }, + { + "id": "6CkeAFNIVwfpWrKtpD15J", + "type": "arrow" + } + ], + "updated": 1719563123293, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1502, + "versionNonce": 1444387592, + "index": "bB5", + "isDeleted": false, + "id": "Hp767-gf6VbeLtPVso7mO", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 709.5530461432976, + "y": 4798.44615017747, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 199.95201110839844, + "height": 40, + "seed": 287141752, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719562801045, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "PbJmhoHc3kcZwiar9XZHC", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 11683, + "versionNonce": 456895496, + "index": "bBD", + "isDeleted": false, + "id": "6CkeAFNIVwfpWrKtpD15J", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 751.2519525642446, + "y": 4843.017578748899, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 127.92433753156081, + "height": 142.0295024483721, + "seed": 161249144, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "geXucDETRtuR9PNLMFTO0" + } + ], + "updated": 1719563236352, + "link": null, + "locked": false, + "startBinding": { + "elementId": "PbJmhoHc3kcZwiar9XZHC", + "focus": 0.562932516248695, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "ELHKEzfV8JLB2fQxcC0Du", + "focus": -0.3125407665313471, + "gap": 4.061222420280046, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 15.670630977070573, + 118.03578914117224 + ], + [ + 127.92433753156081, + 142.0295024483721 + ] + ] + }, + { + "type": "text", + "version": 93, + "versionNonce": 632676104, + "index": "bBE", + "isDeleted": false, + "id": "geXucDETRtuR9PNLMFTO0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 763.3808687368021, + "y": 4958.196225032928, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 18.512001037597656, + "height": 20, + "seed": 1192151160, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719563219023, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:N", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "6CkeAFNIVwfpWrKtpD15J", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2930, + "versionNonce": 1827978616, + "index": "bBG", + "isDeleted": false, + "id": "ZUnsjJKp6zyhV7AKtQceV", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1045.917417899305, + "y": 5212.5501342859325, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 277.27120535714255, + "height": 35, + "seed": 870386296, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "Xv6jD5U83u2iKIw-gm4ci", + "type": "text" + }, + { + "id": "p91Sr2LGdNSTmvnlOMrlj", + "type": "arrow" + } + ], + "updated": 1719564127699, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2525, + "versionNonce": 1452130168, + "index": "bBH", + "isDeleted": false, + "id": "Xv6jD5U83u2iKIw-gm4ci", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1060.0230065397905, + "y": 5217.5501342859325, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 249.06002807617188, + "height": 25, + "seed": 21560184, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719564127699, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "departureTimes' per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ZUnsjJKp6zyhV7AKtQceV", + "originalText": "departureTimes' per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 1113, + "versionNonce": 1104866680, + "index": "bBI", + "isDeleted": false, + "id": "YyJWlu1AVEvOmUZgF_sGM", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 655.4673765035826, + "y": 4707.612704536815, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 0, + "height": 560.3376738811558, + "seed": 512412792, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1719563747894, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 0, + 560.3376738811558 + ] + ] + }, + { + "type": "text", + "version": 366, + "versionNonce": 633931784, + "index": "bBJ", + "isDeleted": false, + "id": "hV2RxZVKCVrNYw0yCZTp7", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "angle": 0, + "x": 382.4458299137618, + "y": 4709.686732890207, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 63.05599594116211, + "height": 20, + "seed": 896171384, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719562801046, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "BUFFER", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "BUFFER", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2310, + "versionNonce": 1211892232, + "index": "bBK", + "isDeleted": false, + "id": "L8q58UCreJYNs95YldtMI", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 122.32082991376194, + "y": 4753.936732890207, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 238.75, + "height": 101.25000000000001, + "seed": 388763256, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "FfPGJLDL7-SxECvmg9-MF" + } + ], + "updated": 1719563880330, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2285, + "versionNonce": 2088665352, + "index": "bBL", + "isDeleted": false, + "id": "FfPGJLDL7-SxECvmg9-MF", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 144.93582014813694, + "y": 4758.936732890207, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 193.52001953125, + "height": 25, + "seed": 1284734840, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719563880331, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TimetableSnapshot''", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "L8q58UCreJYNs95YldtMI", + "originalText": "TimetableSnapshot''", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 12612, + "versionNonce": 491653475, + "index": "bBM", + "isDeleted": false, + "id": "KaNryYRGDdfoD2Y-xh3NH", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 166.02587662360617, + "y": 4843.768905246753, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 561.6071376295936, + "height": 87.51073769786763, + "seed": 635752568, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "kqwHbdFfq4kftTFgs6Wdz" + } + ], + "updated": 1722612369746, + "link": null, + "locked": false, + "startBinding": { + "elementId": "dy74wxh2s3QKemp9OAmxQ", + "focus": 0.7818967405964088, + "gap": 1, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 74.06456843118394, + 77.68702686481447 + ], + [ + 561.6071376295936, + 87.51073769786763 + ] + ] + }, + { + "type": "text", + "version": 14, + "versionNonce": 815277389, + "index": "bBMV", + "isDeleted": false, + "id": "kqwHbdFfq4kftTFgs6Wdz", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 316.35444499375507, + "y": 4886.741646397281, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 42.47200012207031, + "height": 20, + "seed": 2102556680, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722612210118, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:(N-1)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "KaNryYRGDdfoD2Y-xh3NH", + "originalText": "1:(N-1)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1900, + "versionNonce": 419946248, + "index": "bBQ", + "isDeleted": false, + "id": "dy74wxh2s3QKemp9OAmxQ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 136.75522623803056, + "y": 4792.768905246753, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 212.0000000000001, + "height": 50, + "seed": 2115818616, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "mTbjDdaJfpOpNckxzPr3f" + }, + { + "id": "KaNryYRGDdfoD2Y-xh3NH", + "type": "arrow" + }, + { + "id": "_62pPmM_uyGRy9KLu3wQB", + "type": "arrow" + } + ], + "updated": 1719563880331, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1783, + "versionNonce": 1128012552, + "index": "bBR", + "isDeleted": false, + "id": "mTbjDdaJfpOpNckxzPr3f", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 142.77922068383134, + "y": 4797.768905246753, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 199.95201110839844, + "height": 40, + "seed": 1702593912, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719563880333, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Map\nTripPattern -> Timetable", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "dy74wxh2s3QKemp9OAmxQ", + "originalText": "Map\nTripPattern -> Timetable", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 3347, + "versionNonce": 732480520, + "index": "bBS", + "isDeleted": false, + "id": "6jy1zYZ38Ki_kVKeAxITM", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 262.4167270184952, + "y": 5099.016929111548, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 159.99999999999994, + "height": 35, + "seed": 802952824, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "-Z8osxT02UkiKw1gwkYDS" + }, + { + "id": "CMq2qWYx4Fd-ndryMvVy5", + "type": "arrow" + }, + { + "id": "rJLvNBiuU0fq88ygHGFU4", + "type": "arrow" + }, + { + "id": "-jc9-rWRSefKWA3X-w1HD", + "type": "arrow" + } + ], + "updated": 1719564156321, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 3246, + "versionNonce": 1131566856, + "index": "bBT", + "isDeleted": false, + "id": "-Z8osxT02UkiKw1gwkYDS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 273.7567233563858, + "y": 5104.016929111548, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 137.32000732421875, + "height": 25, + "seed": 1644332920, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719563880333, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes B3'", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "6jy1zYZ38Ki_kVKeAxITM", + "originalText": "TripTimes B3'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2662, + "versionNonce": 1210201608, + "index": "bBU", + "isDeleted": false, + "id": "-i6gUsqK5ksGo-UkYmXDi", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 188.11907609956177, + "y": 4944.312745524322, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 201.5, + "height": 83.75000000000006, + "seed": 1211291768, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "CDMteA8bRCgc5GoQ4_0uS" + }, + { + "id": "_62pPmM_uyGRy9KLu3wQB", + "type": "arrow" + } + ], + "updated": 1719563880333, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2545, + "versionNonce": 159266056, + "index": "bBV", + "isDeleted": false, + "id": "CDMteA8bRCgc5GoQ4_0uS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 226.40907320039184, + "y": 4949.312745524322, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 124.92000579833984, + "height": 25, + "seed": 103876984, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719563880333, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable B'", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "-i6gUsqK5ksGo-UkYmXDi", + "originalText": "Timetable B'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2313, + "versionNonce": 1048856141, + "index": "bBW", + "isDeleted": false, + "id": "GF-3wMYk2CDtLKiLga7v0", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 201.17847242383084, + "y": 4978.894917880868, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 658578040, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Kq-G44GVW-eG-CC0jz4en" + }, + { + "id": "CMq2qWYx4Fd-ndryMvVy5", + "type": "arrow" + }, + { + "id": "_62pPmM_uyGRy9KLu3wQB", + "type": "arrow" + }, + { + "id": "fFbJK5E1YKcZdLBiePEY2", + "type": "arrow" + } + ], + "updated": 1722612172620, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2263, + "versionNonce": 1443030787, + "index": "bBX", + "isDeleted": false, + "id": "Kq-G44GVW-eG-CC0jz4en", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 227.51447477368447, + "y": 4988.894917880868, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 124.32799530029297, + "height": 20, + "seed": 774310776, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722612084150, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "GF-3wMYk2CDtLKiLga7v0", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 3583, + "versionNonce": 1163067651, + "index": "bBY", + "isDeleted": false, + "id": "CMq2qWYx4Fd-ndryMvVy5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 225.1676929092184, + "y": 5019.894917880868, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 45.5, + "height": 97.38347328639247, + "seed": 623303800, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722612109247, + "link": null, + "locked": false, + "startBinding": { + "elementId": "GF-3wMYk2CDtLKiLga7v0", + "focus": 0.6794641117210051, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "6jy1zYZ38Ki_kVKeAxITM", + "focus": -0.6260825136222776, + "gap": 2.250000000000199, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -10.500965890723393, + 82.96112696560431 + ], + [ + 34.99903410927661, + 97.38347328639247 + ] + ] + }, + { + "type": "rectangle", + "version": 2897, + "versionNonce": 1631134472, + "index": "bBZ", + "isDeleted": false, + "id": "JFYEjpER5r0C2fyVIcH7l", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 329.0846957684953, + "y": 5212.427473417901, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 271.85727889788814, + "height": 35, + "seed": 772050296, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "QSR8DM7bUTrPqf5nQtfoY", + "type": "text" + }, + { + "id": "rJLvNBiuU0fq88ygHGFU4", + "type": "arrow" + } + ], + "updated": 1719564149003, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2491, + "versionNonce": 1113280008, + "index": "bBa", + "isDeleted": false, + "id": "QSR8DM7bUTrPqf5nQtfoY", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 340.4833211793534, + "y": 5217.427473417901, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 249.06002807617188, + "height": 25, + "seed": 302259832, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719564145633, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "departureTimes' per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "JFYEjpER5r0C2fyVIcH7l", + "originalText": "departureTimes' per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 2305, + "versionNonce": 636610723, + "index": "bBe", + "isDeleted": false, + "id": "_62pPmM_uyGRy9KLu3wQB", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 165.93076143804433, + "y": 4843.768905246753, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 42.28201797885811, + "height": 133.7123569682326, + "seed": 1361388152, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722612109247, + "link": null, + "locked": false, + "startBinding": { + "elementId": "dy74wxh2s3QKemp9OAmxQ", + "focus": 0.6512121561076193, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "GF-3wMYk2CDtLKiLga7v0", + "focus": -0.2933292530945755, + "gap": 14.940282256766977, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -21.90755853973019, + 118.81234662809584 + ], + [ + 20.374459439127918, + 133.7123569682326 + ] + ] + }, + { + "type": "rectangle", + "version": 3531, + "versionNonce": 1191118344, + "index": "bBj", + "isDeleted": false, + "id": "QIM7HXsmtV7wV0EMQeSo8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1013.7167130460236, + "y": 5052.767512934249, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 159.99999999999994, + "height": 35, + "seed": 608737144, + "groupIds": [ + "a2x_iat4HelK3oew2FmHR" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "vS2nO5557H03JapfA7K_y", + "type": "text" + }, + { + "id": "jipn04Lsc4jWTYmL86TPE", + "type": "arrow" + } + ], + "updated": 1719563151759, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 3433, + "versionNonce": 876676360, + "index": "bBk", + "isDeleted": false, + "id": "vS2nO5557H03JapfA7K_y", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1028.4267121304963, + "y": 5057.767512934249, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 130.5800018310547, + "height": 25, + "seed": 2000087160, + "groupIds": [ + "a2x_iat4HelK3oew2FmHR" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719563151759, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes A3", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "QIM7HXsmtV7wV0EMQeSo8", + "originalText": "TripTimes A3", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2188, + "versionNonce": 1189815304, + "index": "bBl", + "isDeleted": false, + "id": "v3n7nsi3cjRYORIEfqeWC", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1208.5261093702918, + "y": 5052.004857704588, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 147.5, + "height": 35, + "seed": 1513298296, + "groupIds": [ + "a2x_iat4HelK3oew2FmHR" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "AByIurhk9ON3iaFt-h-_w", + "type": "text" + }, + { + "id": "jipn04Lsc4jWTYmL86TPE", + "type": "arrow" + } + ], + "updated": 1719563151759, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2024, + "versionNonce": 1559847688, + "index": "bBm", + "isDeleted": false, + "id": "AByIurhk9ON3iaFt-h-_w", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1244.4461075392371, + "y": 5057.004857704588, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 75.66000366210938, + "height": 25, + "seed": 1350642296, + "groupIds": [ + "a2x_iat4HelK3oew2FmHR" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719563151759, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Trip A3", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "v3n7nsi3cjRYORIEfqeWC", + "originalText": "Trip A3", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 6712, + "versionNonce": 181365368, + "index": "bBn", + "isDeleted": false, + "id": "jipn04Lsc4jWTYmL86TPE", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1174.7761093702927, + "y": 5072.004857704588, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 33.00723435574605, + "height": 0, + "seed": 1424300920, + "groupIds": [ + "a2x_iat4HelK3oew2FmHR" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1719563152030, + "link": null, + "locked": false, + "startBinding": { + "elementId": "7EZQT4joabVnMZfD4jRaI", + "focus": -1.185714285714286, + "gap": 15.24999999999909, + "fixedPoint": null + }, + "endBinding": { + "elementId": "7EZQT4joabVnMZfD4jRaI", + "focus": 1.185714285714286, + "gap": 3.25, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 33.00723435574605, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 3434, + "versionNonce": 1571963144, + "index": "bBo", + "isDeleted": false, + "id": "aXuxl970tptB5tbUM6e5b", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 993.2167130460236, + "y": 5076.017512934249, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 159.99999999999994, + "height": 35, + "seed": 1772285048, + "groupIds": [ + "a2x_iat4HelK3oew2FmHR" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "O3YH65B2T3oMFLwkPnhyo", + "type": "text" + }, + { + "id": "T6rlkrv4QjMmPtl8MjsO3", + "type": "arrow" + } + ], + "updated": 1719563151759, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 3338, + "versionNonce": 651485192, + "index": "bBp", + "isDeleted": false, + "id": "O3YH65B2T3oMFLwkPnhyo", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1007.616706942508, + "y": 5081.017512934249, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 131.20001220703125, + "height": 25, + "seed": 679386488, + "groupIds": [ + "a2x_iat4HelK3oew2FmHR" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719563151759, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes A2", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "aXuxl970tptB5tbUM6e5b", + "originalText": "TripTimes A2", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2114, + "versionNonce": 273391368, + "index": "bBq", + "isDeleted": false, + "id": "7EZQT4joabVnMZfD4jRaI", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1190.0261093702918, + "y": 5075.254857704588, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 147.5, + "height": 35, + "seed": 1388064376, + "groupIds": [ + "a2x_iat4HelK3oew2FmHR" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "l2fU9BKu2cbrlAYj-3Xqz", + "type": "text" + }, + { + "id": "T6rlkrv4QjMmPtl8MjsO3", + "type": "arrow" + }, + { + "id": "jipn04Lsc4jWTYmL86TPE", + "type": "arrow" + } + ], + "updated": 1719563151759, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1924, + "versionNonce": 982679048, + "index": "bBr", + "isDeleted": false, + "id": "l2fU9BKu2cbrlAYj-3Xqz", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1225.6361099806434, + "y": 5080.254857704588, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 76.27999877929688, + "height": 25, + "seed": 1629462392, + "groupIds": [ + "a2x_iat4HelK3oew2FmHR" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719563151759, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Trip A2", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "7EZQT4joabVnMZfD4jRaI", + "originalText": "Trip A2", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 6009, + "versionNonce": 728456056, + "index": "bBs", + "isDeleted": false, + "id": "T6rlkrv4QjMmPtl8MjsO3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1154.2761093702927, + "y": 5095.254857704588, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 32.50723435574605, + "height": 0, + "seed": 334295160, + "groupIds": [ + "a2x_iat4HelK3oew2FmHR" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1719563152030, + "link": null, + "locked": false, + "startBinding": { + "elementId": "ERi5auHqptyba5RKypuy5", + "focus": -1.2473184567023836, + "gap": 11.767869094599064, + "fixedPoint": null + }, + "endBinding": { + "elementId": "ERi5auHqptyba5RKypuy5", + "focus": 1.2473184567022797, + "gap": 4.32807299228989, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 32.50723435574605, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 1988, + "versionNonce": 844340232, + "index": "bBt", + "isDeleted": false, + "id": "ERi5auHqptyba5RKypuy5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1166.0439784648918, + "y": 5099.58293069688, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 147.5, + "height": 35, + "seed": 48767352, + "groupIds": [ + "a2x_iat4HelK3oew2FmHR" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "WJuImq68A43ZB17QXKruz", + "type": "text" + }, + { + "id": "T6rlkrv4QjMmPtl8MjsO3", + "type": "arrow" + }, + { + "id": "MAyeT5mpQDxxHyI6odLXr", + "type": "arrow" + } + ], + "updated": 1719564375767, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1809, + "versionNonce": 1728002568, + "index": "bBu", + "isDeleted": false, + "id": "WJuImq68A43ZB17QXKruz", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1206.0639789226555, + "y": 5104.58293069688, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 67.45999908447266, + "height": 25, + "seed": 760558200, + "groupIds": [ + "a2x_iat4HelK3oew2FmHR" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719563151759, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Trip A1", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ERi5auHqptyba5RKypuy5", + "originalText": "Trip A1", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2065, + "versionNonce": 1668175880, + "index": "bBv", + "isDeleted": false, + "id": "IeXu1jbEeujeQRyqYRYhy", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 916.283343726039, + "y": 4959.640024875461, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 167256952, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "JcIycFrnQZnK7pTtfCKaS" + } + ], + "updated": 1719563151759, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2032, + "versionNonce": 1794512648, + "index": "bBw", + "isDeleted": false, + "id": "JcIycFrnQZnK7pTtfCKaS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 943.1193460758925, + "y": 4969.640024875461, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.32799530029297, + "height": 20, + "seed": 387249272, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719563151759, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "IeXu1jbEeujeQRyqYRYhy", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2330, + "versionNonce": 195888717, + "index": "bC5", + "isDeleted": false, + "id": "guIqabckUxkIK3NX02mvF", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1294.4262008688966, + "y": 4697.835895411174, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 313.00000000000006, + "height": 94.9999999999999, + "seed": 1691921784, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "I90nby3P9NdSmJZ-eBrFK" + } + ], + "updated": 1722612283303, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2263, + "versionNonce": 573617325, + "index": "bC6", + "isDeleted": false, + "id": "I90nby3P9NdSmJZ-eBrFK", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1414.6302032492677, + "y": 4702.835895411174, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 72.59199523925781, + "height": 20, + "seed": 424951416, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722612283303, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Requests", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "guIqabckUxkIK3NX02mvF", + "originalText": "Requests", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2390, + "versionNonce": 1559205645, + "index": "bC7", + "isDeleted": false, + "id": "e9N_uxCrB3xfX1Ognug7L", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1503.426200868896, + "y": 4731.835895411174, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.05769230769234, + "height": 50, + "seed": 976596856, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "KIeBijDRRBIA0QkjhqWaz" + } + ], + "updated": 1722612283303, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2314, + "versionNonce": 1986211181, + "index": "bC8", + "isDeleted": false, + "id": "KIeBijDRRBIA0QkjhqWaz", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1515.1990503186407, + "y": 4736.835895411174, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 68.51199340820312, + "height": 40, + "seed": 2143355000, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722612283303, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Thread 1\nIdle", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "e9N_uxCrB3xfX1Ognug7L", + "originalText": "Thread 1\nIdle", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2389, + "versionNonce": 500496333, + "index": "bC9", + "isDeleted": false, + "id": "SL6iHWkIm2__r4pG0PJdb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1405.426200868896, + "y": 4731.835895411174, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.00000000000006, + "height": 50, + "seed": 2042245496, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "BTpDN5ezz9ll3U8UW_Ea_" + } + ], + "updated": 1722612283303, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2336, + "versionNonce": 1566471725, + "index": "bCA", + "isDeleted": false, + "id": "BTpDN5ezz9ll3U8UW_Ea_", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1413.6422042868649, + "y": 4736.835895411174, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 75.5679931640625, + "height": 40, + "seed": 526729848, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722612283303, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Thread 2\nIdle", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "SL6iHWkIm2__r4pG0PJdb", + "originalText": "Thread 2\nIdle", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2587, + "versionNonce": 1072244877, + "index": "bCB", + "isDeleted": false, + "id": "9UydzQq9iXpptzkld40RC", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1307.4262008688966, + "y": 4731.835895411174, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 92.08333333333327, + "height": 50, + "seed": 1602630520, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "DWi7ArttSWFAahqMZpjAS" + }, + { + "id": "G6zekguWswrA5JhKt_dcK", + "type": "arrow" + } + ], + "updated": 1722612283303, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2533, + "versionNonce": 1102588237, + "index": "bCC", + "isDeleted": false, + "id": "DWi7ArttSWFAahqMZpjAS", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1315.9318720521649, + "y": 4736.835895411174, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 75.07199096679688, + "height": 40, + "seed": 811664504, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722612283304, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Thread 3\nRouting", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "9UydzQq9iXpptzkld40RC", + "originalText": "Thread 3\nRouting", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2866, + "versionNonce": 1496441208, + "index": "bCE", + "isDeleted": false, + "id": "ELHKEzfV8JLB2fQxcC0Du", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 883.2375125160854, + "y": 4945.756834963782, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 201.5, + "height": 83.75000000000006, + "seed": 598786168, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "-_JgoubRLP5ayalbFnlBX" + }, + { + "id": "6CkeAFNIVwfpWrKtpD15J", + "type": "arrow" + } + ], + "updated": 1719563285795, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2747, + "versionNonce": 1930556680, + "index": "bCF", + "isDeleted": false, + "id": "-_JgoubRLP5ayalbFnlBX", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 922.2375087013882, + "y": 4950.756834963782, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.50000762939453, + "height": 25, + "seed": 162656632, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719563151759, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "Timetable A'", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "ELHKEzfV8JLB2fQxcC0Du", + "originalText": "Timetable A'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2537, + "versionNonce": 1740465160, + "index": "bCG", + "isDeleted": false, + "id": "WCFksK9Lq3RJgSH_zWpQ2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 896.296908840354, + "y": 4980.339007320328, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 177.0000000000002, + "height": 39.99999999999998, + "seed": 366048632, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "Gbc4jTtVlRn0l8inr7yRC" + }, + { + "id": "DQor7q4GrW5zFa8jjiUpA", + "type": "arrow" + } + ], + "updated": 1719563206624, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2511, + "versionNonce": 1884181256, + "index": "bCH", + "isDeleted": false, + "id": "Gbc4jTtVlRn0l8inr7yRC", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 923.1329111902076, + "y": 4990.339007320328, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 123.32799530029297, + "height": 20, + "seed": 1072957048, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719563151759, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "List", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "WCFksK9Lq3RJgSH_zWpQ2", + "originalText": "List", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 3718, + "versionNonce": 1067810312, + "index": "bCI", + "isDeleted": false, + "id": "MSSwkfn9q-khwQHGKLFla", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 972.320877720733, + "y": 5099.603875693866, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 159.99999999999994, + "height": 35, + "seed": 355261304, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "nW1utnjNrYtTnwmM0O13V" + }, + { + "id": "et-jjXyrBCTVdhIz5_8J2", + "type": "arrow" + }, + { + "id": "DQor7q4GrW5zFa8jjiUpA", + "type": "arrow" + }, + { + "id": "p91Sr2LGdNSTmvnlOMrlj", + "type": "arrow" + }, + { + "id": "MAyeT5mpQDxxHyI6odLXr", + "type": "arrow" + } + ], + "updated": 1719564375766, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 3620, + "versionNonce": 1792110088, + "index": "bCJ", + "isDeleted": false, + "id": "nW1utnjNrYtTnwmM0O13V", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 988.4708754319147, + "y": 5104.603875693866, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "width": 127.70000457763672, + "height": 25, + "seed": 1257417848, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719563151760, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "TripTimes A1'", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "MSSwkfn9q-khwQHGKLFla", + "originalText": "TripTimes A1'", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 4712, + "versionNonce": 1270413832, + "index": "bCK", + "isDeleted": false, + "id": "DQor7q4GrW5zFa8jjiUpA", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 926.7254802689256, + "y": 5021.144752837834, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 42.09539745180791, + "height": 102.881718823538, + "seed": 604961144, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1719563252703, + "link": null, + "locked": false, + "startBinding": { + "elementId": "WCFksK9Lq3RJgSH_zWpQ2", + "focus": 0.6580179561488724, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "MSSwkfn9q-khwQHGKLFla", + "focus": -0.8313753148304869, + "gap": 3.4999999999995453, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 1.8096831660932367, + 84.7982385909545 + ], + [ + 42.09539745180791, + 102.881718823538 + ] + ] + }, + { + "type": "arrow", + "version": 1448, + "versionNonce": 1567036781, + "index": "bCL", + "isDeleted": false, + "id": "fFbJK5E1YKcZdLBiePEY2", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 227.63599622030813, + "y": 5019.894917880868, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 496.90494277188316, + "height": 52.289988352680666, + "seed": 612626808, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "type": "text", + "id": "wQ7L2Wi0uNR6gA2_hqw5X" + } + ], + "updated": 1722612240521, + "link": null, + "locked": false, + "startBinding": { + "elementId": "GF-3wMYk2CDtLKiLga7v0", + "focus": 0.7854708517575925, + "gap": 1, + "fixedPoint": null + }, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 66.71805325185642, + 47.24194909737162 + ], + [ + 496.90494277188316, + 52.289988352680666 + ] + ], + "elbowed": false + }, + { + "type": "text", + "version": 24, + "versionNonce": 2075792973, + "index": "bCM", + "isDeleted": false, + "id": "wQ7L2Wi0uNR6gA2_hqw5X", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 438.68584597122157, + "y": 5063.3361038641115, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 42.47200012207031, + "height": 20, + "seed": 1189648136, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1722612109246, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "1:(N-1)", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "fFbJK5E1YKcZdLBiePEY2", + "originalText": "1:(N-1)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 2934, + "versionNonce": 2126340616, + "index": "bCN", + "isDeleted": false, + "id": "mb6Xh7sm21Bc1e6KKJpCk", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 329.2181511701747, + "y": 5171.578203473773, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 271.85727889788814, + "height": 35, + "seed": 1150373240, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "r1u2T1cz84kUqHj1tBhrf", + "type": "text" + }, + { + "id": "-jc9-rWRSefKWA3X-w1HD", + "type": "arrow" + } + ], + "updated": 1719564156321, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 2533, + "versionNonce": 708760696, + "index": "bCO", + "isDeleted": false, + "id": "r1u2T1cz84kUqHj1tBhrf", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 356.78678237937265, + "y": 5176.578203473773, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 216.7200164794922, + "height": 25, + "seed": 955833976, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719564101666, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "arrivalTimes' per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "mb6Xh7sm21Bc1e6KKJpCk", + "originalText": "arrivalTimes' per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "arrow", + "version": 12328, + "versionNonce": 419627784, + "index": "bCP", + "isDeleted": false, + "id": "-jc9-rWRSefKWA3X-w1HD", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 279.2003620476911, + "y": 5133.782653141864, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 48.178571428570194, + "height": 54.624825064200195, + "seed": 1405412360, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1719564156321, + "link": null, + "locked": false, + "startBinding": { + "elementId": "6jy1zYZ38Ki_kVKeAxITM", + "focus": 0.8033292324862549, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "mb6Xh7sm21Bc1e6KKJpCk", + "focus": -0.6808758663411866, + "gap": 1.8392176939133833, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 14.844285714285206, + 45.34636673420846 + ], + [ + 48.178571428570194, + 54.624825064200195 + ] + ] + }, + { + "type": "arrow", + "version": 11537, + "versionNonce": 806684536, + "index": "bCQ", + "isDeleted": false, + "id": "rJLvNBiuU0fq88ygHGFU4", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 279.2003620476911, + "y": 5135.8826455513545, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 47.03700336679208, + "height": 92.30737499540646, + "seed": 681255688, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1719564151647, + "link": null, + "locked": false, + "startBinding": { + "elementId": "6jy1zYZ38Ki_kVKeAxITM", + "focus": 0.7999157193964552, + "gap": 1.8657164398064197, + "fixedPoint": null + }, + "endBinding": { + "elementId": "JFYEjpER5r0C2fyVIcH7l", + "focus": -0.7301207884223256, + "gap": 2.847330354012115, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 11.471155152505958, + 79.24890290619624 + ], + [ + 47.03700336679208, + 92.30737499540646 + ] + ] + }, + { + "type": "arrow", + "version": 341, + "versionNonce": 1337783533, + "index": "bCR", + "isDeleted": false, + "id": "G6zekguWswrA5JhKt_dcK", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1357.4262008688963, + "y": 4782.273233151974, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 425.5651245354918, + "height": 40.1328511811771, + "seed": 1199187464, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1722612337572, + "link": null, + "locked": false, + "startBinding": { + "elementId": "9UydzQq9iXpptzkld40RC", + "focus": -0.6286566818247163, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "PNbwzZX7FC2TGjF0aCqdZ", + "focus": 0.35968571722837256, + "gap": 4.016420960176333, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -92.05268547756714, + 35.81343223721797 + ], + [ + -425.5651245354918, + 40.1328511811771 + ] + ] + }, + { + "type": "arrow", + "version": 33, + "versionNonce": 2104411512, + "index": "bCY", + "isDeleted": false, + "id": "MAyeT5mpQDxxHyI6odLXr", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1133.289647761976, + "y": 5117.71335274833, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 31.42857142857133, + "height": 0, + "seed": 556552456, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1719564384224, + "link": null, + "locked": false, + "startBinding": { + "elementId": "MSSwkfn9q-khwQHGKLFla", + "focus": 0.034827260255094326, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "ERi5auHqptyba5RKypuy5", + "focus": -0.03602411722573119, + "gap": 1.3257592743443638, + "fixedPoint": null + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 31.42857142857133, + 0 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/snapshot-manager-8.svg b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-8.svg new file mode 100644 index 00000000000..be7067e9b93 --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/snapshot-manager-8.svg @@ -0,0 +1,13 @@ + + + + + + + + Timetable CTimetable BarrivalTimes per stopLIVE SNAPSHOT TTimetableSnapshotManagerTimetableSnapshot'MapTripPattern -> Timetable1:NdepartureTimes' per stopBUFFERTimetableSnapshot''1:(N-1)MapTripPattern -> TimetableTripTimes B3'Timetable B'List<TripTimes>departureTimes' per stopTripTimes A3Trip A3TripTimes A2Trip A2Trip A1List<TripTimes>RequestsThread 1IdleThread 2IdleThread 3RoutingTimetable A'List<TripTimes>TripTimes A1'1:(N-1)arrivalTimes' per stop \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/timetable-lookup.excalidraw b/src/main/java/org/opentripplanner/updater/images/timetable-lookup.excalidraw new file mode 100644 index 00000000000..187f9fd423b --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/timetable-lookup.excalidraw @@ -0,0 +1,1588 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "y_1dF3bWuAcjtLlQGSXFu", + "type": "arrow", + "x": 1075, + "y": 395.267333984375, + "width": 135.0000070765995, + "height": 272.00000000000006, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "Zz", + "roundness": { + "type": 2 + }, + "seed": 2090931009, + "version": 390, + "versionNonce": 650819119, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "1Ns_Z758QgiWGnOjiTG7r" + } + ], + "updated": 1719545411854, + "link": null, + "locked": false, + "points": [ + [ + -0.9999929234004981, + 0 + ], + [ + -13.904405364546038, + 184.22695035460995 + ], + [ + -136, + 272.00000000000006 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "fU-lbdUKNp3YiZSV6XH2M", + "focus": -0.9945830991579151, + "gap": 1 + }, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "1Ns_Z758QgiWGnOjiTG7r", + "type": "text", + "x": 1002.6080093383789, + "y": 571.267333984375, + "width": 116.78398132324219, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 2, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ZzV", + "roundness": null, + "seed": 1356735105, + "version": 17, + "versionNonce": 1653000289, + "isDeleted": false, + "boundElements": null, + "updated": 1719545392668, + "link": null, + "locked": false, + "text": "parallel arrays", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "y_1dF3bWuAcjtLlQGSXFu", + "originalText": "parallel arrays", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "0pJJ0P3IcmodwiKkdR2kq", + "type": "rectangle", + "x": 334, + "y": 427.267333984375, + "width": 280, + "height": 46.00000000000001, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a3", + "roundness": { + "type": 3 + }, + "seed": 1718390863, + "version": 718, + "versionNonce": 621954241, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "Rsqx3K7Zier_pluAIttuF" + }, + { + "id": "uaZ9cZoHWJ7RQE9WQPtl_", + "type": "arrow" + }, + { + "id": "elK9c6a-qvHea0vjWPRlk", + "type": "arrow" + } + ], + "updated": 1719544751853, + "link": null, + "locked": false + }, + { + "id": "Rsqx3K7Zier_pluAIttuF", + "type": "text", + "x": 382.55999755859375, + "y": 437.767333984375, + "width": 182.8800048828125, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a3V", + "roundness": null, + "seed": 1841444161, + "version": 689, + "versionNonce": 114164897, + "isDeleted": false, + "boundElements": null, + "updated": 1719544751853, + "link": null, + "locked": false, + "text": "TimetableSnapshot", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "0pJJ0P3IcmodwiKkdR2kq", + "originalText": "TimetableSnapshot", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "FYdjRq1ZxZRKW71xenQPk", + "type": "rectangle", + "x": 530, + "y": 576.1121615705819, + "width": 395, + "height": 49, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a6", + "roundness": { + "type": 3 + }, + "seed": 1714663023, + "version": 1019, + "versionNonce": 31584431, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "QKOKv1iEnscsKH2KpNiIM" + }, + { + "id": "eEB94w4lmv7yE_xEghxha", + "type": "arrow" + }, + { + "id": "Y9Qpdb9Axyp6RdbhZhFJG", + "type": "arrow" + }, + { + "id": "3huuRNhb5Un8r4E2lJUDZ", + "type": "arrow" + } + ], + "updated": 1719544894275, + "link": null, + "locked": false + }, + { + "id": "QKOKv1iEnscsKH2KpNiIM", + "type": "text", + "x": 541.1699981689453, + "y": 588.1121615705819, + "width": 372.6600036621094, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a7", + "roundness": null, + "seed": 2030252975, + "version": 1029, + "versionNonce": 1867987393, + "isDeleted": false, + "boundElements": null, + "updated": 1719545703086, + "link": null, + "locked": false, + "text": "Scheduled/RealTimeTripTimes per Trip", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "FYdjRq1ZxZRKW71xenQPk", + "originalText": "Scheduled/RealTimeTripTimes per Trip", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "CusrFkXELH9Cjjcvg8Hnq", + "type": "rectangle", + "x": 606, + "y": 635.819058122306, + "width": 320, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a8", + "roundness": { + "type": 3 + }, + "seed": 934414479, + "version": 651, + "versionNonce": 1561802401, + "isDeleted": false, + "boundElements": [ + { + "id": "vTgJ0m4aQadBbPLk0OitF", + "type": "text" + }, + { + "id": "Y9Qpdb9Axyp6RdbhZhFJG", + "type": "arrow" + }, + { + "id": "y_1dF3bWuAcjtLlQGSXFu", + "type": "arrow" + } + ], + "updated": 1719545335845, + "link": null, + "locked": false + }, + { + "id": "vTgJ0m4aQadBbPLk0OitF", + "type": "text", + "x": 660.2999877929688, + "y": 640.819058122306, + "width": 211.4000244140625, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a8V", + "roundness": null, + "seed": 679236687, + "version": 495, + "versionNonce": 45146913, + "isDeleted": false, + "boundElements": null, + "updated": 1719545455341, + "link": null, + "locked": false, + "text": "arrivalTimes per stop", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "CusrFkXELH9Cjjcvg8Hnq", + "originalText": "arrivalTimes per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "MTFaZJib6cYVpQ6DLyS5o", + "type": "rectangle", + "x": 609, + "y": 678.267333984375, + "width": 314.99999999999994, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a9", + "roundness": { + "type": 3 + }, + "seed": 413146799, + "version": 688, + "versionNonce": 663885903, + "isDeleted": false, + "boundElements": [ + { + "id": "7gcSXXHKJD4Uob2QZR1nS", + "type": "text" + }, + { + "id": "3huuRNhb5Un8r4E2lJUDZ", + "type": "arrow" + } + ], + "updated": 1719545411855, + "link": null, + "locked": false + }, + { + "id": "7gcSXXHKJD4Uob2QZR1nS", + "type": "text", + "x": 644.6299896240234, + "y": 683.267333984375, + "width": 243.74002075195312, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a9G", + "roundness": null, + "seed": 1192793903, + "version": 466, + "versionNonce": 438402095, + "isDeleted": false, + "boundElements": null, + "updated": 1719545459559, + "link": null, + "locked": false, + "text": "departureTimes per stop", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "MTFaZJib6cYVpQ6DLyS5o", + "originalText": "departureTimes per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "vRqFGy9E7C8hw8JAoQn2j", + "type": "rectangle", + "x": 465, + "y": 330.267333984375, + "width": 149, + "height": 37, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aD", + "roundness": { + "type": 3 + }, + "seed": 2074505423, + "version": 1076, + "versionNonce": 1464113999, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "bDvvJR67MYMJs8XoL4JeN" + }, + { + "id": "sDeOEGkwsEXHOK01q8bmi", + "type": "arrow" + }, + { + "id": "zYrivxxrMHiHrsCFI0P5h", + "type": "arrow" + }, + { + "id": "elK9c6a-qvHea0vjWPRlk", + "type": "arrow" + } + ], + "updated": 1719545028199, + "link": null, + "locked": false + }, + { + "id": "bDvvJR67MYMJs8XoL4JeN", + "type": "text", + "x": 470, + "y": 336.267333984375, + "width": 116.96000671386719, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aE", + "roundness": null, + "seed": 1765551617, + "version": 1027, + "versionNonce": 902977903, + "isDeleted": false, + "boundElements": null, + "updated": 1719545028199, + "link": null, + "locked": false, + "text": "TripPattern", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": "vRqFGy9E7C8hw8JAoQn2j", + "originalText": "TripPattern", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "HPxB5t7bbywsYcTM412DR", + "type": "rectangle", + "x": 420, + "y": 505.267333984375, + "width": 274, + "height": 43, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aF", + "roundness": { + "type": 3 + }, + "seed": 1784512239, + "version": 705, + "versionNonce": 746382753, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "B0teZHtNYXfEFNm303Oyn" + }, + { + "id": "uaZ9cZoHWJ7RQE9WQPtl_", + "type": "arrow" + }, + { + "id": "eEB94w4lmv7yE_xEghxha", + "type": "arrow" + }, + { + "id": "elK9c6a-qvHea0vjWPRlk", + "type": "arrow" + } + ], + "updated": 1719544946378, + "link": null, + "locked": false + }, + { + "id": "B0teZHtNYXfEFNm303Oyn", + "type": "text", + "x": 426.28997802734375, + "y": 514.267333984375, + "width": 261.4200439453125, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aG", + "roundness": null, + "seed": 184319055, + "version": 616, + "versionNonce": 709276865, + "isDeleted": false, + "boundElements": null, + "updated": 1719544946379, + "link": null, + "locked": false, + "text": "Timetable per TripPattern", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "HPxB5t7bbywsYcTM412DR", + "originalText": "Timetable per TripPattern", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "eEB94w4lmv7yE_xEghxha", + "type": "arrow", + "x": 467.936471701833, + "y": 550.0259546740302, + "width": 60.101458104323626, + "height": 41.40329748658348, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aH", + "roundness": { + "type": 2 + }, + "seed": 659409167, + "version": 3538, + "versionNonce": 1140444225, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "BJIpIGdtso6mDq1miGabC" + } + ], + "updated": 1719544946411, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 6.110187876757607, + 27.826297235432207 + ], + [ + 60.101458104323626, + 41.40329748658348 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "HPxB5t7bbywsYcTM412DR", + "gap": 1.7586206896551744, + "focus": 0.6644795410106797 + }, + "endBinding": { + "elementId": "FYdjRq1ZxZRKW71xenQPk", + "gap": 1.962070193843374, + "focus": -0.5524887633998372 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "BJIpIGdtso6mDq1miGabC", + "type": "text", + "x": 464.7906590597918, + "y": 567.8522519094624, + "width": 18.512001037597656, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aHV", + "roundness": null, + "seed": 1898592129, + "version": 33, + "versionNonce": 1177860545, + "isDeleted": false, + "boundElements": null, + "updated": 1719544534926, + "link": null, + "locked": false, + "text": "1:N", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "eEB94w4lmv7yE_xEghxha", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "Y9Qpdb9Axyp6RdbhZhFJG", + "type": "arrow", + "x": 574.6470582794315, + "y": 626.1121615705819, + "width": 30.352941720568538, + "height": 33.800532717867554, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aJ", + "roundness": { + "type": 2 + }, + "seed": 267374383, + "version": 1856, + "versionNonce": 775645633, + "isDeleted": false, + "boundElements": null, + "updated": 1719545006780, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 7.232941720568533, + 20.96551724137931 + ], + [ + 30.352941720568538, + 33.800532717867554 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "FYdjRq1ZxZRKW71xenQPk", + "gap": 1, + "focus": 0.7848916296363468 + }, + "endBinding": { + "elementId": "CusrFkXELH9Cjjcvg8Hnq", + "gap": 1, + "focus": -0.9026442619452091 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "3huuRNhb5Un8r4E2lJUDZ", + "type": "arrow", + "x": 573.2650260423957, + "y": 626.7500926050647, + "width": 61, + "height": 72.47476991280541, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aK", + "roundness": { + "type": 2 + }, + "seed": 1051464015, + "version": 1873, + "versionNonce": 175007759, + "isDeleted": false, + "boundElements": null, + "updated": 1719545011080, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -26.265026042395675, + 41.43103448275865 + ], + [ + 34.734973957604325, + 72.47476991280541 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "FYdjRq1ZxZRKW71xenQPk", + "focus": 0.6462182140034153, + "gap": 1.63793103448279 + }, + "endBinding": { + "elementId": "MTFaZJib6cYVpQ6DLyS5o", + "focus": -0.8614130434782634, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "7Al5NgyrMBkxBd4TMaoIj", + "type": "rectangle", + "x": 640, + "y": 329.267333984375, + "width": 199.99999999999991, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aL", + "roundness": { + "type": 3 + }, + "seed": 1826295663, + "version": 972, + "versionNonce": 812288065, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "x512m3Ao9J0eeYycZcDa5" + }, + { + "id": "zYrivxxrMHiHrsCFI0P5h", + "type": "arrow" + }, + { + "id": "iVtTJnJ4JSuDo9Uxrmw2-", + "type": "arrow" + }, + { + "id": "6l7gZeXoF0UYyh-L9VQiD", + "type": "arrow" + } + ], + "updated": 1719545258461, + "link": null, + "locked": false + }, + { + "id": "x512m3Ao9J0eeYycZcDa5", + "type": "text", + "x": 678.7799911499023, + "y": 334.267333984375, + "width": 122.44001770019531, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aM", + "roundness": null, + "seed": 2068313473, + "version": 751, + "versionNonce": 2077464513, + "isDeleted": false, + "boundElements": null, + "updated": 1719545168404, + "link": null, + "locked": false, + "text": "StopPattern", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "7Al5NgyrMBkxBd4TMaoIj", + "originalText": "StopPattern", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "6kbeMTyP3x0yCr5HNEF9X", + "type": "rectangle", + "x": 394, + "y": 272.267333984375, + "width": 196, + "height": 38, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aN", + "roundness": { + "type": 3 + }, + "seed": 348473743, + "version": 384, + "versionNonce": 54433793, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "MBQc9NCm-70PVX4pehxCP" + }, + { + "id": "sDeOEGkwsEXHOK01q8bmi", + "type": "arrow" + } + ], + "updated": 1719545032834, + "link": null, + "locked": false + }, + { + "id": "MBQc9NCm-70PVX4pehxCP", + "type": "text", + "x": 462.88000106811523, + "y": 278.767333984375, + "width": 58.23999786376953, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aO", + "roundness": null, + "seed": 160884143, + "version": 335, + "versionNonce": 1519251425, + "isDeleted": false, + "boundElements": null, + "updated": 1719545032834, + "link": null, + "locked": false, + "text": "Route", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "6kbeMTyP3x0yCr5HNEF9X", + "originalText": "Route", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "sDeOEGkwsEXHOK01q8bmi", + "type": "arrow", + "x": 417.61807795641226, + "y": 311.267333984375, + "width": 42.60841569826499, + "height": 33.95296300881773, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aP", + "roundness": { + "type": 2 + }, + "seed": 1403207599, + "version": 2187, + "versionNonce": 1270534945, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "3ESiMCnrFbbspbU4N4mH0" + } + ], + "updated": 1719545032865, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 8.381922043587736, + 21 + ], + [ + 42.60841569826499, + 33.95296300881773 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "6kbeMTyP3x0yCr5HNEF9X", + "gap": 1, + "focus": 0.7800896238659986 + }, + "endBinding": { + "elementId": "vRqFGy9E7C8hw8JAoQn2j", + "gap": 4.773506345322744, + "focus": -0.5665324474205826 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "3ESiMCnrFbbspbU4N4mH0", + "type": "text", + "x": 350.7439994812012, + "y": 301.267333984375, + "width": 18.512001037597656, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aQ", + "roundness": null, + "seed": 600798223, + "version": 17, + "versionNonce": 446080353, + "isDeleted": false, + "boundElements": null, + "updated": 1719544534926, + "link": null, + "locked": false, + "text": "1:N", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "sDeOEGkwsEXHOK01q8bmi", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "zYrivxxrMHiHrsCFI0P5h", + "type": "arrow", + "x": 615, + "y": 346.47315612564773, + "width": 24, + "height": 0.5282817280233871, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aR", + "roundness": { + "type": 2 + }, + "seed": 194867663, + "version": 1382, + "versionNonce": 1863564257, + "isDeleted": false, + "boundElements": null, + "updated": 1719545168404, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 24, + -0.5282817280233871 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "vRqFGy9E7C8hw8JAoQn2j", + "gap": 1, + "focus": -0.031461182205112403 + }, + "endBinding": { + "elementId": "7Al5NgyrMBkxBd4TMaoIj", + "gap": 1, + "focus": 0.15459206709101056 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "uaZ9cZoHWJ7RQE9WQPtl_", + "type": "arrow", + "x": 364.3172701638245, + "y": 474.267333984375, + "width": 51.6827298361755, + "height": 39.86858296195999, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aS", + "roundness": { + "type": 2 + }, + "seed": 1171021807, + "version": 2487, + "versionNonce": 362756225, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "3kK4yY_F9HXkvjUIPAvYs" + } + ], + "updated": 1719544946411, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 12.682729836175497, + 30 + ], + [ + 51.6827298361755, + 39.86858296195999 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "0pJJ0P3IcmodwiKkdR2kq", + "gap": 1, + "focus": 0.8003351027971978 + }, + "endBinding": { + "elementId": "HPxB5t7bbywsYcTM412DR", + "gap": 4, + "focus": -0.4103385663452218 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "3kK4yY_F9HXkvjUIPAvYs", + "type": "text", + "x": 367.7439994812012, + "y": 494.267333984375, + "width": 18.512001037597656, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aT", + "roundness": null, + "seed": 2087772257, + "version": 14, + "versionNonce": 2073664815, + "isDeleted": false, + "boundElements": null, + "updated": 1719544534926, + "link": null, + "locked": false, + "text": "1:N", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "uaZ9cZoHWJ7RQE9WQPtl_", + "originalText": "1:N", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "elK9c6a-qvHea0vjWPRlk", + "type": "arrow", + "x": 586.0156362978425, + "y": 369.267333984375, + "width": 10.873390490976135, + "height": 134, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aU", + "roundness": { + "type": 2 + }, + "seed": 1412991503, + "version": 2259, + "versionNonce": 1364343343, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "VzXf00JaLCuALUBjym7f7" + } + ], + "updated": 1719545028199, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 2.9843637021574523, + 36 + ], + [ + 10.873390490976135, + 134 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "vRqFGy9E7C8hw8JAoQn2j", + "gap": 2, + "focus": -0.5894261022855855 + }, + "endBinding": { + "elementId": "HPxB5t7bbywsYcTM412DR", + "gap": 2, + "focus": 0.3011645323238165 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "VzXf00JaLCuALUBjym7f7", + "type": "text", + "x": 542.3679962158203, + "y": 395.267333984375, + "width": 129.26400756835938, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aV", + "roundness": null, + "seed": 1648608623, + "version": 58, + "versionNonce": 1301192399, + "isDeleted": false, + "boundElements": null, + "updated": 1719544704934, + "link": null, + "locked": false, + "text": "look up (resolve)", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "elK9c6a-qvHea0vjWPRlk", + "originalText": "look up (resolve)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 866, + "versionNonce": 980531937, + "index": "aZ", + "isDeleted": false, + "id": "fU-lbdUKNp3YiZSV6XH2M", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 754, + "y": 372.73553799456954, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 320, + "height": 35, + "seed": 2068643823, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "YtnqnMvGCgw3WD0zasvz5", + "type": "text" + }, + { + "id": "iVtTJnJ4JSuDo9Uxrmw2-", + "type": "arrow" + }, + { + "id": "y_1dF3bWuAcjtLlQGSXFu", + "type": "arrow" + } + ], + "updated": 1719545335845, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 706, + "versionNonce": 2065824719, + "index": "aa", + "isDeleted": false, + "id": "YtnqnMvGCgw3WD0zasvz5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 804.2799911499023, + "y": 377.73553799456954, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 219.4400177001953, + "height": 25, + "seed": 1353001487, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719545444938, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "StopLocation per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "fU-lbdUKNp3YiZSV6XH2M", + "originalText": "StopLocation per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 939, + "versionNonce": 612641793, + "index": "ab", + "isDeleted": false, + "id": "u5pfT19f2ODRj5O5pw4ye", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 754, + "y": 411.99147191540953, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 319.99999999999994, + "height": 35, + "seed": 968036399, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "OJ2EeaIQNGQl7w5zPF4fo", + "type": "text" + }, + { + "id": "6l7gZeXoF0UYyh-L9VQiD", + "type": "arrow" + } + ], + "updated": 1719545258461, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 647, + "versionNonce": 1927058575, + "index": "ac", + "isDeleted": false, + "id": "OJ2EeaIQNGQl7w5zPF4fo", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 826.4899978637695, + "y": 416.99147191540953, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 175.02000427246094, + "height": 25, + "seed": 811545167, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1719545449170, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "PickDrop per stop", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "u5pfT19f2ODRj5O5pw4ye", + "originalText": "PickDrop per stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "iVtTJnJ4JSuDo9Uxrmw2-", + "type": "arrow", + "x": 710, + "y": 364.267333984375, + "width": 45, + "height": 28, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ad", + "roundness": { + "type": 2 + }, + "seed": 1136411457, + "version": 50, + "versionNonce": 1772084815, + "isDeleted": false, + "boundElements": null, + "updated": 1719545265378, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 12, + 22 + ], + [ + 45, + 28 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "7Al5NgyrMBkxBd4TMaoIj", + "focus": 0.36099585062240674, + "gap": 1 + }, + "endBinding": { + "elementId": "fU-lbdUKNp3YiZSV6XH2M", + "focus": -0.664097084659239, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + }, + { + "id": "6l7gZeXoF0UYyh-L9VQiD", + "type": "arrow", + "x": 711, + "y": 364.267333984375, + "width": 44, + "height": 69, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ae", + "roundness": { + "type": 2 + }, + "seed": 723648321, + "version": 93, + "versionNonce": 922430689, + "isDeleted": false, + "boundElements": null, + "updated": 1719545272494, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -2, + 54 + ], + [ + 42, + 69 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "7Al5NgyrMBkxBd4TMaoIj", + "focus": 0.2816927322907085, + "gap": 1 + }, + "endBinding": { + "elementId": "u5pfT19f2ODRj5O5pw4ye", + "focus": -0.8142390949635586, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow" + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/images/timetable-lookup.svg b/src/main/java/org/opentripplanner/updater/images/timetable-lookup.svg new file mode 100644 index 00000000000..5d1b06ca392 --- /dev/null +++ b/src/main/java/org/opentripplanner/updater/images/timetable-lookup.svg @@ -0,0 +1,21 @@ + + + + + + + + parallel arraysTimetableSnapshotScheduled/RealTimeTripTimes per TriparrivalTimes per stopdepartureTimes per stopTripPatternTimetable per TripPattern1:NStopPatternRoute1:N1:Nlook up (resolve)StopLocation per stopPickDrop per stop \ No newline at end of file diff --git a/src/main/java/org/opentripplanner/updater/package.md b/src/main/java/org/opentripplanner/updater/package.md index 50c425bb6eb..1062c8f205d 100644 --- a/src/main/java/org/opentripplanner/updater/package.md +++ b/src/main/java/org/opentripplanner/updater/package.md @@ -26,9 +26,9 @@ On 11 January 2024 a team of OTP developers reviewed this realtime concurrency a In OTP's internal transit model, realtime data is currently stored separately from the scheduled data. This is only because realtime was originally introduced as an optional extra feature. Now that realtime is very commonly used, we intend to create a single unified transit model that will nonetheless continue to apply the same concurrency approach. -The following is a sequence diagram showing how threads are intended to communicate. Unlike some common forms of sequence diagrams, time is on the horizontal axis here. Each horizontal line represents either a thread of execution (handling incoming realtime messages or routing requests) or a queue or buffer data structure. Dotted lines represent object references being handed off, and solid lines represent data being copied. +The following is a sequence diagram showing how threads are intended to communicate. Unlike some common forms of sequence diagrams, time is on the horizontal axis here. Each horizontal line represents either a thread of execution (handling incoming realtime messages or routing requests) or a queue or buffer data structure. Dotted arrows represent object references being handed off, and solid arrows represent data being copied. -![Realtime sequence diagram](images/updater-threads-queues.svg) +![Realtime Sequence Diagram](images/updater-threads-queues.svg) At the top of the diagram are the GraphUpdater implementations. These fall broadly into two categories: polling updaters and streaming updaters. Polling updaters periodically send a request to server (often just a simple HTTP server) which returns a file containing the latest version of the updates. Streaming updaters are generally built around libraries implementing message-oriented protocols such as MQTT or AMQP, which fire a callback each time a new message is received. Polling updaters tend to return a full dataset describing the entire system state on each polling operation, while streaming updaters tend to receive incremental messages targeting individual transit trips. As such, polling updaters execute relatively infrequently (perhaps every minute or two) and process large responses, while streaming updaters execute very frequently (often many times per second) and operate on small messages in short bursts. Polling updaters are simpler in many ways and make use of common HTTP server components, but they introduce significant latency and redundant communication. Streaming updaters require more purpose-built or custom-configured components including message brokers, but bandwidth consumption and latency are lower, allowing routing results to reflect vehicle delays and positions immediately after they're reported. @@ -36,11 +36,11 @@ The GraphUpdaterManager coordinates all these updaters, and each runs freely in As mentioned above, these GraphWriterRunnable instances must write to the transit data model in a very controlled way, following specific rules. They operate on a buffer containing a shallow copy of the whole transit data structure, and apply a copy-on-write strategy to avoid corrupting existing objects that may be visible to other parts of the system. When an instance is copied for writing, any references to it in parent objects must also be updated. Therefore, writes cause cascading copy operations, and all instances in the object tree back up to the root of the transit data structure must also be copied. As an optimization, if a GraphWriterRunnable is able to determine that the protective copy has already been made in this buffer (the part of the structure it needs to modify is somehow marked as being "dirty") it does not need to make another copy. If the update involves reading the existing data structure before making a change, those reads should be performed within the same contiguous chunk of deferred logic that performs the corresponding write, ensuring that there are no data races between write operations. -This writable buffer of transit data is periodically made immutable and swapped into the role of a live snapshot, which is ready to be handed off to any incoming routing requests. Each time an immutable snapshot is created, a new writable buffer is created by making a shallow copy of the root instance in the transit data aggreagate. This functions like a double-buffering system, except that any number of snapshots can exist at once, and large subsets of the data can be shared across snapshots. As older snapshots (and their component parts) fall out of use, they are dereferenced and become eligible for garbage collection. Although the buffer swap could in principle occur after every write operation, it can incur significant copying and indexing overhead. When incremental message-oriented updaters are present this overhead would be incurred more often than necesary. Snapshots can be throttled to occur at most every few seconds, thereby reducing the total overhead at no perceptible cost to realtime visibility latency. +This writable buffer of transit data is periodically made immutable and swapped into the role of a live snapshot, which is ready to be handed off to any incoming routing requests. Each time an immutable snapshot is created, a new writable buffer is created by making a shallow copy of the root instance in the transit data aggreagate. This functions like a double-buffering system, except that any number of snapshots can exist at once, and large subsets of the data can be shared across snapshots. As older snapshots (and their component parts) fall out of use, they are dereferenced and become eligible for garbage collection. Although the buffer swap could in principle occur after every write operation, it can incur significant copying and indexing overhead. When incremental message-oriented updaters are present this overhead would be incurred far more often than necessary. Therefore, snapshot publication is scheduled to occur regularly every few seconds, thereby reducing the total overhead without perceptibly increasing the latency of realtime information becoming visible to end users. This is essentially a multi-version snapshot concurrency control system, inspired by widely used database engines (and in fact informed by books on transactional database design). The end result is a system where: -1. Writing operations are simple to reason about and cannot conflict because only one write happens at a time. +1. Write operations are simple to reason about and cannot conflict because only one write happens at a time. 1. Multiple read operations (including routing requests) can occur concurrently. 1. Read operations do not need to pause while writes are happening. 1. Read operations see only fully completed write operations, never partial writes. @@ -50,7 +50,57 @@ This is essentially a multi-version snapshot concurrency control system, inspire An important characteristic of this approach is that _no locking is necessary_. However, some form of synchronization is used during the buffer swap operation to impose a consistent view of the whole data structure via a happens-before relationship as defined by the Java memory model. While pointers to objects can be handed between threads with no read-tearing of the pointer itself, there is no guarantee that the web of objects pointed to will be consistent without some explicit synchronization at the hand-off. -Arguably the process of creating an immutable live snapshot (and a corresponding new writable buffer) should be handled by a GraphWriterRunnable on the single graph updater thread. This would serve to defer any queued modifications until the new buffer is in place, without introducing any further locking mechanisms. +For simplicity, the process of creating an immutable live snapshot (and a corresponding new writable buffer) is handled by Runnable on the single graph writer thread. This serves to defer any queued modifications until the new buffer is in place, without introducing any further locking mechanisms. + +## Copy-on-Write Strategy in Timetable Snapshots + +Below is a high-level diagram of how the timetable snapshots are used to look up arrival and departure times for a particular TripPattern. Each Route is associated with N different TripPatterns (essentially, N different unique sequences of stops). The TimetableSnapshot is used to look up a Timetable for a given TripPattern. That Timetable provides an unchanging view of both realtime-updated and original scheduled arrival/departure times for all trips that match the given TripPattern. The arrays of arrival and departure times for all these trips are parallel to the array of stops for the TripPattern used to look them up. They have the same length, and a given index corresponds to the same stop in any of these arrays. Note that this is a simplification; in reality the data structures are further broken down to reflect which services are running on each specific date. + +Timetable Lookup Diagram + +Next, we will walk through a step-by-step diagram of how successive timetable snapshots are built up in a buffer, using a copy-on-write strategy to reuse as many object instances as possible (minimizing time and memory spent on copies) while ensuring that any existing snapshots visible to routing or API threads are consistent and unchanging. This is a more detailed look at the bottom half of the "Threads, Queues, and Buffers" diagram above, and particularly the yellow boxes near its center representing the buffered and live transit data. Each of the following diagrams shows the references between a tree of objects on the heap at a single moment in time, with time progressing as we move down the page. + +In the first four diagrams below, each blue box represents a shallow-copied object, maintaining a protective copy of the minimal subtree at and above any leaf nodes that are changed by incoming realtime messages. These shallow copies are being created in a private buffer that is visible only to a single thread that is sequentially executing GraphWriterRunnables enqueued by GraphUpdaters. + + + +
+Snapshot Copy-on-Write Diagram 1
+ +In preparation for applying updates, the collections in the root object of the buffer are shallow-copied. The collection objects themselves are duplicated, but the references they contain remain unchanged: they continue to reference the same existing Timetable instances. + +Snapshot Copy-on-Write Diagram 2
+ +At this point, the realtime system receives some updated departure times for the first trip on TripPattern A. This will require a change to a primitive array of departure times for that particular trip. All objects leading from the root of the snapshot down to that leaf array are copied in a selective deep copy. Specifically, the Timetable for TripPattern A and the first TripTimes within that Timetable are copied. All other elements in collections continue to point at the previously existing object instances. This is indicated by the (1:N-1) cardinality of the references pointing from the blue copied boxes back to the pre-existing white boxes. + +From this step onward, the "requests" box on the right side of the diagram will demonstrate how the snapshots interact with incoming routing and API requests and garbage collection. One of the HTTP handler threads is actively performing routing, so has been given a request-scoped reference to the current live snapshot. + +Snapshot Copy-on-Write Diagram 3
+ +In the next diagram, the selective deep copy is complete. The departure times have been updated, and are reachable from the root of the buffer snapshot, without affecting any pre-existing snapshots. The changes to the buffer are not published immediately; a series of changes is usually batched for publication every few seconds. Another incoming HTTP request, this time for the GraphQL API, is given a request-scoped reference to the same immutable live snapshot. + +Snapshot Copy-on-Write Diagram 4
+ +Every few seconds, the buffer contents are frozen ("committed" in database terms) and made available to routing and API services as a new "live snapshot". Once the snapshot is made visible to other threads, its entire object tree is immutable by convention. This immutability will eventually be enforced in code. The process of applying single-threaded updates to the private buffer begins anew. The blue instances have been promoted to become the current live snapshot. Green boxes will now represent the minimal subtree of shallow protective copies in the private buffer. Note that the two HTTP requests are still in progress, and retain their references to the previous, unchanging snapshot S even though we have published a new snapshot T. In this way, each request maintains a consistent view of all the transit data until it completes. + +Snapshot Copy-on-Write Diagram 5
+ +Now the realtime system receives some updated arrival and departure times for the third trip in TripPattern B. It again makes a protective copy of all items from the root of the snapshot down to the leaf arrays it needs to update. This includes the Timetable for TripPattern B and the third TripTimes in that Timetable, as well as some primitive arrays. The copied collections in the buffer now contain a mixture of references to white, blue, and green objects from three generations of snapshots. Both live snapshots S and T remain unchanged by the writes to the copies in the buffer, even though the buffer reuses most of the instances in these previous snapshots. Note: In the original (white) snapshot, the TripTimes are only shown for the frontmost Timetable A in the pile. Timetables B and C are also implied to have TripTimes B1, B2, B3... and C1, C2, C3... and associated Trips and time arrays, which are not shown to avoid clutter. For this reason, the protective copies of Timetable B and TripTimes B3 (in green) are shown pointing in the general direction of this tree of pre-existing objects, not at specific individual objects. + +The routing request has now completed and released its reference to the original snapshot S (white). But another routing request has arrived on another HTTP handler thread, and is given a request-scoped reference to the current live snapshot T (blue). + +Snapshot Copy-on-Write Diagram 6
+ +Once the GraphQL request completes, no threads are using the old snapshot S (white) anymore. That snapshot will now be garbage collected, transitively releasing any objects that are no longer used because they've been superseded by protective copies. + +Snapshot Copy-on-Write Diagram 7
+ +Without any further active steps by OTP or the JVM, simply rearranging elements of the diagram after garbage collection occurs reveals that our routing thread is seeing a full coherent snapshot composed of objects created over the course of several subsequent snapshots. The buffer (green) will soon be promoted to the current live snapshot and the cycle continues. + +Snapshot Copy-on-Write Diagram 8
+ ## Design Considerations diff --git a/src/main/java/org/opentripplanner/updater/trip/MqttGtfsRealtimeUpdater.java b/src/main/java/org/opentripplanner/updater/trip/MqttGtfsRealtimeUpdater.java index 3ba7af0305b..e1c8bf4ea83 100644 --- a/src/main/java/org/opentripplanner/updater/trip/MqttGtfsRealtimeUpdater.java +++ b/src/main/java/org/opentripplanner/updater/trip/MqttGtfsRealtimeUpdater.java @@ -17,9 +17,6 @@ import org.eclipse.paho.client.mqttv3.MqttMessage; import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; import org.opentripplanner.framework.tostring.ToStringBuilder; -import org.opentripplanner.transit.service.DefaultTransitService; -import org.opentripplanner.transit.service.TransitModel; -import org.opentripplanner.updater.GtfsRealtimeFuzzyTripMatcher; import org.opentripplanner.updater.spi.GraphUpdater; import org.opentripplanner.updater.spi.UpdateResult; import org.opentripplanner.updater.spi.WriteToGraphCallback; @@ -60,13 +57,12 @@ public class MqttGtfsRealtimeUpdater implements GraphUpdater { private final Consumer recordMetrics; private WriteToGraphCallback saveResultOnGraph; - private GtfsRealtimeFuzzyTripMatcher fuzzyTripMatcher = null; + private final boolean fuzzyTripMatching; private MqttClient client; public MqttGtfsRealtimeUpdater( MqttGtfsRealtimeUpdaterParameters parameters, - TransitModel transitModel, TimetableSnapshotSource snapshotSource ) { this.configRef = parameters.configRef(); @@ -77,10 +73,7 @@ public MqttGtfsRealtimeUpdater( this.backwardsDelayPropagationType = parameters.getBackwardsDelayPropagationType(); this.snapshotSource = snapshotSource; // Set properties of realtime data snapshot source - if (parameters.getFuzzyTripMatching()) { - this.fuzzyTripMatcher = - new GtfsRealtimeFuzzyTripMatcher(new DefaultTransitService(transitModel)); - } + this.fuzzyTripMatching = parameters.getFuzzyTripMatching(); this.recordMetrics = TripUpdateMetrics.streaming(parameters); LOG.info("Creating streaming GTFS-RT TripUpdate updater subscribing to MQTT broker at {}", url); } @@ -178,7 +171,7 @@ public void messageArrived(String topic, MqttMessage message) { saveResultOnGraph.execute( new TripUpdateGraphWriterRunnable( snapshotSource, - fuzzyTripMatcher, + fuzzyTripMatching, backwardsDelayPropagationType, updateIncrementality, updates, diff --git a/src/main/java/org/opentripplanner/updater/trip/PollingTripUpdater.java b/src/main/java/org/opentripplanner/updater/trip/PollingTripUpdater.java index 8af27d9818c..71f93235e01 100644 --- a/src/main/java/org/opentripplanner/updater/trip/PollingTripUpdater.java +++ b/src/main/java/org/opentripplanner/updater/trip/PollingTripUpdater.java @@ -4,9 +4,6 @@ import java.util.List; import java.util.function.Consumer; import org.opentripplanner.framework.tostring.ToStringBuilder; -import org.opentripplanner.transit.service.DefaultTransitService; -import org.opentripplanner.transit.service.TransitModel; -import org.opentripplanner.updater.GtfsRealtimeFuzzyTripMatcher; import org.opentripplanner.updater.spi.PollingGraphUpdater; import org.opentripplanner.updater.spi.UpdateResult; import org.opentripplanner.updater.spi.WriteToGraphCallback; @@ -43,11 +40,10 @@ public class PollingTripUpdater extends PollingGraphUpdater { /** * Set only if we should attempt to match the trip_id from other data in TripDescriptor */ - private GtfsRealtimeFuzzyTripMatcher fuzzyTripMatcher; + private final boolean fuzzyTripMatching; public PollingTripUpdater( PollingTripUpdaterParameters parameters, - TransitModel transitModel, TimetableSnapshotSource snapshotSource ) { super(parameters); @@ -56,10 +52,7 @@ public PollingTripUpdater( this.updateSource = new GtfsRealtimeTripUpdateSource(parameters); this.backwardsDelayPropagationType = parameters.backwardsDelayPropagationType(); this.snapshotSource = snapshotSource; - if (parameters.fuzzyTripMatching()) { - this.fuzzyTripMatcher = - new GtfsRealtimeFuzzyTripMatcher(new DefaultTransitService(transitModel)); - } + this.fuzzyTripMatching = parameters.fuzzyTripMatching(); this.recordMetrics = BatchTripUpdateMetrics.batch(parameters); @@ -89,7 +82,7 @@ public void runPolling() { // Handle trip updates via graph writer runnable TripUpdateGraphWriterRunnable runnable = new TripUpdateGraphWriterRunnable( snapshotSource, - fuzzyTripMatcher, + fuzzyTripMatching, backwardsDelayPropagationType, incrementality, updates, @@ -106,7 +99,7 @@ public String toString() { .of(this.getClass()) .addObj("updateSource", updateSource) .addStr("feedId", feedId) - .addBoolIfTrue("fuzzyTripMatching", fuzzyTripMatcher != null) + .addBool("fuzzyTripMatching", fuzzyTripMatching) .toString(); } } diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java index b5683a9a62e..fc762347aec 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotManager.java @@ -4,6 +4,7 @@ import java.util.Objects; import java.util.function.Supplier; import javax.annotation.Nullable; +import org.opentripplanner.model.RealTimeTripUpdate; import org.opentripplanner.model.Timetable; import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.TransitLayerUpdater; @@ -11,7 +12,6 @@ import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.framework.Result; import org.opentripplanner.transit.model.network.TripPattern; -import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.updater.TimetableSnapshotSourceParameters; import org.opentripplanner.updater.spi.UpdateError; import org.opentripplanner.updater.spi.UpdateSuccess; @@ -183,19 +183,14 @@ public void clearBuffer(String feedId) { /** * Update the TripTimes of one Trip in a Timetable of a TripPattern. If the Trip of the TripTimes - * does not exist yet in the Timetable, add it. This method will make a protective copy - * of the Timetable if such a copy has not already been made while building up this snapshot, - * handling both cases where patterns were pre-existing in static data or created by realtime data. + * does not exist yet in the Timetable, add it. This method will make a protective copy of the + * Timetable if such a copy has not already been made while building up this snapshot, handling + * both cases where patterns were pre-existing in static data or created by realtime data. * - * @param serviceDate service day for which this update is valid * @return whether the update was actually applied */ - public Result updateBuffer( - TripPattern pattern, - TripTimes tripTimes, - LocalDate serviceDate - ) { - return buffer.update(pattern, tripTimes, serviceDate); + public Result updateBuffer(RealTimeTripUpdate realTimeTripUpdate) { + return buffer.update(realTimeTripUpdate); } /** diff --git a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java index 56e3c380b67..03ce9052331 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java @@ -41,6 +41,7 @@ import org.opentripplanner.framework.lang.StringUtils; import org.opentripplanner.framework.time.ServiceDateUtils; import org.opentripplanner.gtfs.mapping.TransitModeMapper; +import org.opentripplanner.model.RealTimeTripUpdate; import org.opentripplanner.model.StopTime; import org.opentripplanner.model.Timetable; import org.opentripplanner.model.TimetableSnapshot; @@ -333,6 +334,15 @@ public TimetableSnapshot getTimetableSnapshot() { return snapshotManager.getTimetableSnapshot(); } + /** + * @return the current timetable snapshot buffer that contains pending changes (not yet published + * in a snapshot). This should be used in the context of an updater to build a TransitEditorService + * that sees all the changes applied so far by real-time updates. + */ + public TimetableSnapshot getTimetableSnapshotBuffer() { + return snapshotManager.getTimetableSnapshotBuffer(); + } + private static void logUpdateResult( String feedId, Map failuresByRelationship, @@ -424,10 +434,14 @@ private Result handleScheduledTrip( ); cancelScheduledTrip(tripId, serviceDate, CancelationType.DELETE); - return snapshotManager.updateBuffer(newPattern, updatedTripTimes, serviceDate); + return snapshotManager.updateBuffer( + new RealTimeTripUpdate(newPattern, updatedTripTimes, serviceDate) + ); } else { // Set the updated trip times in the buffer - return snapshotManager.updateBuffer(pattern, updatedTripTimes, serviceDate); + return snapshotManager.updateBuffer( + new RealTimeTripUpdate(pattern, updatedTripTimes, serviceDate) + ); } } @@ -453,7 +467,7 @@ private Result validateAndHandleAddedTrip( // // Check whether trip id already exists in graph - final Trip trip = transitEditorService.getTripForId(tripId); + final Trip trip = transitEditorService.getScheduledTripForId(tripId); if (trip != null) { // TODO: should we support this and add a new instantiation of this trip (making it @@ -630,7 +644,16 @@ private Result handleAddedTrip( "number of stop should match the number of stop time updates" ); - Route route = getOrCreateRoute(tripDescriptor, tripId); + Route route; + boolean routeExists = routeExists(tripId.getFeedId(), tripDescriptor); + if (routeExists) { + route = + transitEditorService.getRouteForId( + new FeedScopedId(tripId.getFeedId(), tripDescriptor.getRouteId()) + ); + } else { + route = createRoute(tripDescriptor, tripId); + } // Create new Trip @@ -660,19 +683,14 @@ private Result handleAddedTrip( stopTimeUpdates, stops, serviceDate, - RealTimeState.ADDED + RealTimeState.ADDED, + !routeExists ); } - private Route getOrCreateRoute(TripDescriptor tripDescriptor, FeedScopedId tripId) { - if (routeExists(tripId.getFeedId(), tripDescriptor)) { - // Try to find route - return transitEditorService.getRouteForId( - new FeedScopedId(tripId.getFeedId(), tripDescriptor.getRouteId()) - ); - } + private Route createRoute(TripDescriptor tripDescriptor, FeedScopedId tripId) { // the route in this update doesn't already exist, but the update contains the information so it will be created - else if ( + if ( tripDescriptor.hasExtension(MfdzRealtimeExtensions.tripDescriptor) && !routeExists(tripId.getFeedId(), tripDescriptor) ) { @@ -696,10 +714,7 @@ else if ( var name = Objects.requireNonNullElse(addedRouteExtension.routeLongName(), tripId.toString()); builder.withLongName(new NonLocalizedString(name)); builder.withUrl(addedRouteExtension.routeUrl()); - - var route = builder.build(); - transitEditorService.addRoutes(route); - return route; + return builder.build(); } // no information about the rout is given, so we create a dummy one else { @@ -713,9 +728,7 @@ else if ( // Create route name I18NString longName = NonLocalizedString.ofNullable(tripDescriptor.getTripId()); builder.withLongName(longName); - var route = builder.build(); - transitEditorService.addRoutes(route); - return route; + return builder.build(); } } @@ -755,7 +768,8 @@ private Result addTripToGraphAndBuffer( final List stopTimeUpdates, final List stops, final LocalDate serviceDate, - final RealTimeState realTimeState + final RealTimeState realTimeState, + final boolean isAddedRoute ) { // Preconditions Objects.requireNonNull(stops); @@ -869,7 +883,17 @@ private Result addTripToGraphAndBuffer( pattern.lastStop().getName() ); // Add new trip times to the buffer - return snapshotManager.updateBuffer(pattern, newTripTimes, serviceDate); + // TODO add support for TripOnServiceDate for GTFS-RT + return snapshotManager.updateBuffer( + new RealTimeTripUpdate( + pattern, + newTripTimes, + serviceDate, + null, + realTimeState == RealTimeState.ADDED, + isAddedRoute + ) + ); } /** @@ -900,7 +924,7 @@ private boolean cancelScheduledTrip( case CANCEL -> newTripTimes.cancelTrip(); case DELETE -> newTripTimes.deleteTrip(); } - snapshotManager.updateBuffer(pattern, newTripTimes, serviceDate); + snapshotManager.updateBuffer(new RealTimeTripUpdate(pattern, newTripTimes, serviceDate)); success = true; } } @@ -939,7 +963,7 @@ private boolean cancelPreviouslyAddedTrip( case CANCEL -> newTripTimes.cancelTrip(); case DELETE -> newTripTimes.deleteTrip(); } - snapshotManager.updateBuffer(pattern, newTripTimes, serviceDate); + snapshotManager.updateBuffer(new RealTimeTripUpdate(pattern, newTripTimes, serviceDate)); cancelledAddedTrip = true; } } @@ -1045,7 +1069,8 @@ private Result handleModifiedTrip( tripUpdate.getStopTimeUpdateList(), stops, serviceDate, - RealTimeState.MODIFIED + RealTimeState.MODIFIED, + false ); } diff --git a/src/main/java/org/opentripplanner/updater/trip/TripUpdateGraphWriterRunnable.java b/src/main/java/org/opentripplanner/updater/trip/TripUpdateGraphWriterRunnable.java index 848e4ece9ef..35124de13e5 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TripUpdateGraphWriterRunnable.java +++ b/src/main/java/org/opentripplanner/updater/trip/TripUpdateGraphWriterRunnable.java @@ -4,10 +4,8 @@ import java.util.List; import java.util.Objects; import java.util.function.Consumer; -import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.updater.GraphWriterRunnable; -import org.opentripplanner.updater.GtfsRealtimeFuzzyTripMatcher; +import org.opentripplanner.updater.RealTimeUpdateContext; import org.opentripplanner.updater.spi.UpdateResult; class TripUpdateGraphWriterRunnable implements GraphWriterRunnable { @@ -19,7 +17,7 @@ class TripUpdateGraphWriterRunnable implements GraphWriterRunnable { */ private final List updates; - private final GtfsRealtimeFuzzyTripMatcher fuzzyTripMatcher; + private final boolean fuzzyTripMatching; private final BackwardsDelayPropagationType backwardsDelayPropagationType; @@ -29,7 +27,7 @@ class TripUpdateGraphWriterRunnable implements GraphWriterRunnable { TripUpdateGraphWriterRunnable( TimetableSnapshotSource snapshotSource, - GtfsRealtimeFuzzyTripMatcher fuzzyTripMatcher, + boolean fuzzyTripMatching, BackwardsDelayPropagationType backwardsDelayPropagationType, UpdateIncrementality updateIncrementality, List updates, @@ -37,7 +35,7 @@ class TripUpdateGraphWriterRunnable implements GraphWriterRunnable { Consumer sendMetrics ) { this.snapshotSource = snapshotSource; - this.fuzzyTripMatcher = fuzzyTripMatcher; + this.fuzzyTripMatching = fuzzyTripMatching; this.backwardsDelayPropagationType = backwardsDelayPropagationType; this.updateIncrementality = updateIncrementality; this.updates = Objects.requireNonNull(updates); @@ -46,9 +44,9 @@ class TripUpdateGraphWriterRunnable implements GraphWriterRunnable { } @Override - public void run(Graph graph, TransitModel transitModel) { + public void run(RealTimeUpdateContext context) { var result = snapshotSource.applyTripUpdates( - fuzzyTripMatcher, + fuzzyTripMatching ? context.gtfsRealtimeFuzzyTripMatcher() : null, backwardsDelayPropagationType, updateIncrementality, updates, diff --git a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdater.java b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdater.java index 31074aafe38..96544321735 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdater.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdater.java @@ -5,13 +5,12 @@ 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.vehicle_parking.VehicleParking; import org.opentripplanner.routing.vehicle_parking.VehicleParkingService; import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces; import org.opentripplanner.transit.model.framework.FeedScopedId; -import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.updater.GraphWriterRunnable; +import org.opentripplanner.updater.RealTimeUpdateContext; import org.opentripplanner.updater.spi.DataSource; import org.opentripplanner.updater.spi.PollingGraphUpdater; import org.opentripplanner.updater.spi.WriteToGraphCallback; @@ -73,7 +72,7 @@ private AvailabilityUpdater(List updates) { } @Override - public void run(Graph graph, TransitModel ignored) { + public void run(RealTimeUpdateContext context) { updates.forEach(this::handleUpdate); } 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 171bd53f77a..59aea16e928 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdater.java @@ -23,8 +23,8 @@ import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.TraverseModeSet; import org.opentripplanner.transit.model.framework.FeedScopedId; -import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.updater.GraphWriterRunnable; +import org.opentripplanner.updater.RealTimeUpdateContext; import org.opentripplanner.updater.spi.DataSource; import org.opentripplanner.updater.spi.PollingGraphUpdater; import org.opentripplanner.updater.spi.WriteToGraphCallback; @@ -98,14 +98,14 @@ private VehicleParkingGraphWriterRunnable(List updatedVehiclePar } @Override - public void run(Graph graph, TransitModel transitModel) { + public void run(RealTimeUpdateContext context) { // Apply stations to graph /* Add any new park and update space available for existing parks */ Set toAdd = new HashSet<>(); Set toLink = new HashSet<>(); Set toRemove = new HashSet<>(); - var vehicleParkingHelper = new VehicleParkingHelper(graph); + var vehicleParkingHelper = new VehicleParkingHelper(context.graph()); for (VehicleParking updatedVehicleParking : updatedVehicleParkings) { var operational = updatedVehicleParking.getState().equals(VehicleParkingState.OPERATIONAL); @@ -133,7 +133,7 @@ public void run(Graph graph, TransitModel transitModel) { tempEdgesByPark.get(oldVehicleParking).forEach(DisposableEdgeCollection::disposeEdges); verticesByPark .get(oldVehicleParking) - .forEach(v -> removeVehicleParkingEdgesFromGraph(v, graph)); + .forEach(v -> removeVehicleParkingEdgesFromGraph(v, context.graph())); verticesByPark.remove(oldVehicleParking); } diff --git a/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java b/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java index f1355ca0fa4..c2860d1d091 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_position/PollingVehiclePositionUpdater.java @@ -1,16 +1,12 @@ package org.opentripplanner.updater.vehicle_position; import com.google.transit.realtime.GtfsRealtime.VehiclePosition; -import java.time.LocalDate; import java.util.List; +import java.util.Set; import org.opentripplanner.framework.tostring.ToStringBuilder; import org.opentripplanner.service.realtimevehicles.RealtimeVehicleRepository; -import org.opentripplanner.transit.model.network.TripPattern; -import org.opentripplanner.transit.model.timetable.Trip; -import org.opentripplanner.transit.service.DefaultTransitService; -import org.opentripplanner.transit.service.TransitModel; -import org.opentripplanner.transit.service.TransitService; -import org.opentripplanner.updater.GtfsRealtimeFuzzyTripMatcher; +import org.opentripplanner.service.realtimevehicles.model.RealtimeVehicle; +import org.opentripplanner.standalone.config.routerconfig.updaters.VehiclePositionsUpdaterConfig; import org.opentripplanner.updater.spi.PollingGraphUpdater; import org.opentripplanner.updater.spi.WriteToGraphCallback; import org.slf4j.Logger; @@ -18,7 +14,7 @@ /** * Map vehicle positions to - * {@link org.opentripplanner.service.realtimevehicles.model.RealtimeVehicle} and add them to OTP + * {@link RealtimeVehicle} and add them to OTP * patterns via a GTFS-RT source. */ public class PollingVehiclePositionUpdater extends PollingGraphUpdater { @@ -29,39 +25,27 @@ public class PollingVehiclePositionUpdater extends PollingGraphUpdater { * Update streamer */ private final GtfsRealtimeHttpVehiclePositionSource vehiclePositionSource; - - private final RealtimeVehiclePatternMatcher realtimeVehiclePatternMatcher; + private final Set vehiclePositionFeatures; /** * Parent update manager. Is used to execute graph writer runnables. */ private WriteToGraphCallback saveResultOnGraph; + private final String feedId; + private final RealtimeVehicleRepository realtimeVehicleRepository; + private final boolean fuzzyTripMatching; public PollingVehiclePositionUpdater( VehiclePositionsUpdaterParameters params, - RealtimeVehicleRepository realtimeVehicleRepository, - TransitModel transitModel + RealtimeVehicleRepository realtimeVehicleRepository ) { super(params); this.vehiclePositionSource = new GtfsRealtimeHttpVehiclePositionSource(params.url(), params.headers()); - // TODO Inject TransitService, do not create it here. We currently do not - // support dagger injection in updaters, so this is ok for now. - TransitService transitService = new DefaultTransitService(transitModel); - var fuzzyTripMatcher = params.fuzzyTripMatching() - ? new GtfsRealtimeFuzzyTripMatcher(transitService) - : null; - this.realtimeVehiclePatternMatcher = - new RealtimeVehiclePatternMatcher( - params.feedId(), - transitService::getTripForId, - transitService::getPatternForTrip, - (trip, date) -> getPatternIncludingRealtime(transitModel, trip, date), - realtimeVehicleRepository, - transitService.getTimeZone(), - fuzzyTripMatcher, - params.vehiclePositionFeatures() - ); + this.realtimeVehicleRepository = realtimeVehicleRepository; + this.feedId = params.feedId(); + this.fuzzyTripMatching = params.fuzzyTripMatching(); + this.vehiclePositionFeatures = params.vehiclePositionFeatures(); LOG.info( "Creating vehicle position updater running every {}: {}", @@ -86,7 +70,13 @@ public void runPolling() { if (updates != null) { // Handle updating trip positions via graph writer runnable - var runnable = new VehiclePositionUpdaterRunnable(updates, realtimeVehiclePatternMatcher); + var runnable = new VehiclePositionUpdaterRunnable( + realtimeVehicleRepository, + vehiclePositionFeatures, + feedId, + fuzzyTripMatching, + updates + ); saveResultOnGraph.execute(runnable); } } @@ -95,14 +85,4 @@ public void runPolling() { public String toString() { return ToStringBuilder.of(this.getClass()).addObj("source", vehiclePositionSource).toString(); } - - private static TripPattern getPatternIncludingRealtime( - TransitModel transitModel, - Trip trip, - LocalDate sd - ) { - // a new instance of DefaultTransitService must be created to retrieve - // the current TimetableSnapshot - return (new DefaultTransitService(transitModel)).getPatternForTrip(trip, sd); - } } diff --git a/src/main/java/org/opentripplanner/updater/vehicle_position/VehiclePositionUpdaterRunnable.java b/src/main/java/org/opentripplanner/updater/vehicle_position/VehiclePositionUpdaterRunnable.java index d1f923696b5..3165221a5fc 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_position/VehiclePositionUpdaterRunnable.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_position/VehiclePositionUpdaterRunnable.java @@ -3,22 +3,46 @@ import com.google.transit.realtime.GtfsRealtime.VehiclePosition; import java.util.List; import java.util.Objects; -import org.opentripplanner.routing.graph.Graph; -import org.opentripplanner.transit.service.TransitModel; +import java.util.Set; +import org.opentripplanner.service.realtimevehicles.RealtimeVehicleRepository; +import org.opentripplanner.standalone.config.routerconfig.updaters.VehiclePositionsUpdaterConfig; import org.opentripplanner.updater.GraphWriterRunnable; +import org.opentripplanner.updater.RealTimeUpdateContext; -public record VehiclePositionUpdaterRunnable( - List updates, - RealtimeVehiclePatternMatcher matcher -) - implements GraphWriterRunnable { - public VehiclePositionUpdaterRunnable { - Objects.requireNonNull(updates); - Objects.requireNonNull(matcher); +public class VehiclePositionUpdaterRunnable implements GraphWriterRunnable { + + private final List updates; + private final RealtimeVehicleRepository realtimeVehicleRepository; + private final String feedId; + private final boolean fuzzyTripMatching; + private final Set vehiclePositionFeatures; + + public VehiclePositionUpdaterRunnable( + RealtimeVehicleRepository realtimeVehicleRepository, + Set vehiclePositionFeatures, + String feedId, + boolean fuzzyTripMatching, + List updates + ) { + this.updates = Objects.requireNonNull(updates); + this.feedId = feedId; + this.realtimeVehicleRepository = realtimeVehicleRepository; + this.fuzzyTripMatching = fuzzyTripMatching; + this.vehiclePositionFeatures = vehiclePositionFeatures; } @Override - public void run(Graph graph, TransitModel transitModel) { + public void run(RealTimeUpdateContext context) { + RealtimeVehiclePatternMatcher matcher = new RealtimeVehiclePatternMatcher( + feedId, + context.transitService()::getTripForId, + context.transitService()::getPatternForTrip, + context.transitService()::getPatternForTrip, + realtimeVehicleRepository, + context.transitService().getTimeZone(), + fuzzyTripMatching ? context.gtfsRealtimeFuzzyTripMatcher() : null, + vehiclePositionFeatures + ); // Apply new vehicle positions matcher.applyRealtimeVehicleUpdates(updates); } diff --git a/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java b/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java index 8dfa046b1f9..77c3c8d01c6 100644 --- a/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java +++ b/src/main/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdater.java @@ -15,7 +15,6 @@ import org.opentripplanner.framework.time.DurationUtils; import org.opentripplanner.framework.time.TimeUtils; import org.opentripplanner.framework.tostring.ToStringBuilder; -import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.linking.DisposableEdgeCollection; import org.opentripplanner.routing.linking.LinkingDirection; import org.opentripplanner.routing.linking.VertexLinker; @@ -32,8 +31,8 @@ import org.opentripplanner.street.search.TraverseMode; import org.opentripplanner.street.search.TraverseModeSet; import org.opentripplanner.transit.model.framework.FeedScopedId; -import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.updater.GraphWriterRunnable; +import org.opentripplanner.updater.RealTimeUpdateContext; import org.opentripplanner.updater.spi.PollingGraphUpdater; import org.opentripplanner.updater.spi.UpdaterConstructionException; import org.opentripplanner.updater.spi.WriteToGraphCallback; @@ -156,10 +155,10 @@ public VehicleRentalGraphWriterRunnable( } @Override - public void run(Graph graph, TransitModel transitModel) { + public void run(RealTimeUpdateContext context) { // Apply stations to graph Set stationSet = new HashSet<>(); - var vertexFactory = new VertexFactory(graph); + var vertexFactory = new VertexFactory(context.graph()); /* add any new stations and update vehicle counts for existing stations */ for (VehicleRentalPlace station : stations) { @@ -239,7 +238,9 @@ public void run(Graph graph, TransitModel transitModel) { latestModifiedEdges.forEach(StreetEdge::removeRentalExtension); - var updater = new GeofencingVertexUpdater(graph.getStreetIndex()::getEdgesForEnvelope); + var updater = new GeofencingVertexUpdater( + context.graph().getStreetIndex()::getEdgesForEnvelope + ); latestModifiedEdges = updater.applyGeofencingZones(geofencingZones); latestAppliedGeofencingZones = geofencingZones; diff --git a/src/test/java/org/opentripplanner/GtfsTest.java b/src/test/java/org/opentripplanner/GtfsTest.java index 074e31147a3..9b37c488b8e 100644 --- a/src/test/java/org/opentripplanner/GtfsTest.java +++ b/src/test/java/org/opentripplanner/GtfsTest.java @@ -191,7 +191,7 @@ protected void setUp() throws Exception { gtfsBundle.setFeedId(feedId); List gtfsBundleList = Collections.singletonList(gtfsBundle); - alertsUpdateHandler = new AlertsUpdateHandler(); + alertsUpdateHandler = new AlertsUpdateHandler(false); var deduplicator = new Deduplicator(); graph = new Graph(deduplicator); transitModel = new TransitModel(new StopModel(), deduplicator); @@ -234,7 +234,7 @@ protected void setUp() throws Exception { feedId.getId() ); timetableSnapshotSource.flushBuffer(); - alertsUpdateHandler.update(feedMessage); + alertsUpdateHandler.update(feedMessage, null); } catch (Exception exception) {} } } diff --git a/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java b/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java index 964c3d8155e..c438fb2fd06 100644 --- a/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java +++ b/src/test/java/org/opentripplanner/gtfs/mapping/TripMapperTest.java @@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.time.Duration; import java.util.Collection; import java.util.Collections; import org.junit.jupiter.api.Test; @@ -122,12 +123,12 @@ void flexTimePenalty() { var flexTrip = new Trip(); flexTrip.setId(new AgencyAndId("1", "1")); flexTrip.setSafeDurationFactor(1.5); - flexTrip.setSafeDurationOffset(600d); + flexTrip.setSafeDurationOffset(60d); flexTrip.setRoute(new GtfsTestData().route); var mapper = defaultTripMapper(); var mapped = mapper.map(flexTrip); var penalty = mapper.flexSafeTimePenalties().get(mapped); assertEquals(1.5f, penalty.coefficient()); - assertEquals(600, penalty.constant().toSeconds()); + assertEquals(Duration.ofHours(1), penalty.constant()); } } diff --git a/src/test/java/org/opentripplanner/model/TimetableSnapshotTest.java b/src/test/java/org/opentripplanner/model/TimetableSnapshotTest.java index 89c30da79e4..0a737630a5b 100644 --- a/src/test/java/org/opentripplanner/model/TimetableSnapshotTest.java +++ b/src/test/java/org/opentripplanner/model/TimetableSnapshotTest.java @@ -16,7 +16,6 @@ import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.Map; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.opentripplanner.ConstantsForTests; @@ -25,6 +24,7 @@ import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.framework.Result; import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.updater.spi.UpdateError; import org.opentripplanner.updater.trip.BackwardsDelayPropagationType; @@ -106,7 +106,7 @@ public void testResolve() { @Test public void testUpdate() { - Assertions.assertThrows( + assertThrows( ConcurrentModificationException.class, () -> { LocalDate today = LocalDate.now(timeZone); @@ -159,7 +159,7 @@ public void testUpdate() { @Test public void testCommit() { - Assertions.assertThrows( + assertThrows( ConcurrentModificationException.class, () -> { LocalDate today = LocalDate.now(timeZone); @@ -267,9 +267,11 @@ void testCannotUpdateReadOnlyTimetableSnapshot() { TimetableSnapshot committedSnapshot = createCommittedSnapshot(); LocalDate today = LocalDate.now(timeZone); TripPattern pattern = patternIndex.get(new FeedScopedId(feedId, "1.1")); + TripTimes tripTimes = pattern.getScheduledTimetable().getTripTimes().getFirst(); + RealTimeTripUpdate realTimeTripUpdate = new RealTimeTripUpdate(pattern, tripTimes, today); assertThrows( ConcurrentModificationException.class, - () -> committedSnapshot.update(pattern, null, today) + () -> committedSnapshot.update(realTimeTripUpdate) ); } @@ -323,7 +325,9 @@ private Result updateResolver( BackwardsDelayPropagationType.REQUIRED_NO_DATA ); if (result.isSuccess()) { - return resolver.update(pattern, result.successValue().getTripTimes(), serviceDate); + return resolver.update( + new RealTimeTripUpdate(pattern, result.successValue().getTripTimes(), serviceDate) + ); } throw new RuntimeException("createUpdatedTripTimes returned an error: " + result); } diff --git a/src/test/java/org/opentripplanner/netex/validation/JourneyPatternSJMismatchTest.java b/src/test/java/org/opentripplanner/netex/validation/JourneyPatternSJMismatchTest.java new file mode 100644 index 00000000000..f1c87342800 --- /dev/null +++ b/src/test/java/org/opentripplanner/netex/validation/JourneyPatternSJMismatchTest.java @@ -0,0 +1,182 @@ +package org.opentripplanner.netex.validation; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.netex.index.api.HMapValidationRule.Status.DISCARD; +import static org.opentripplanner.netex.index.api.HMapValidationRule.Status.OK; +import static org.rutebanken.netex.model.StopUseEnumeration.ACCESS; +import static org.rutebanken.netex.model.StopUseEnumeration.PASSTHROUGH; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.opentripplanner.netex.index.NetexEntityIndex; +import org.opentripplanner.netex.mapping.MappingSupport; +import org.rutebanken.netex.model.EntityStructure; +import org.rutebanken.netex.model.JourneyPatternRefStructure; +import org.rutebanken.netex.model.PointInJourneyPatternRefStructure; +import org.rutebanken.netex.model.PointInLinkSequence_VersionedChildStructure; +import org.rutebanken.netex.model.PointsInJourneyPattern_RelStructure; +import org.rutebanken.netex.model.ServiceJourney; +import org.rutebanken.netex.model.ServiceJourneyPattern; +import org.rutebanken.netex.model.StopPointInJourneyPattern; +import org.rutebanken.netex.model.StopUseEnumeration; +import org.rutebanken.netex.model.TimetabledPassingTime; +import org.rutebanken.netex.model.TimetabledPassingTimes_RelStructure; + +class JourneyPatternSJMismatchTest { + + private static final String PATTERN_ID = "pattern"; + private static final String JOURNEY_ID = "journey"; + + @Test + void patternAndJourneyMatch() { + var pattern = new ServiceJourneyPatternBuilder(PATTERN_ID) + .withPointsInSequence(1, 2, 3) + .build(); + + var index = new NetexEntityIndex(); + index.journeyPatternsById.add(pattern); + + var journey = new ServiceJourneyBuilder(JOURNEY_ID) + .withPatternId(PATTERN_ID) + .withPassingTimes(pattern.getPointsInSequence()) + .build(); + + var rule = new JourneyPatternSJMismatch(); + rule.setup(index.readOnlyView()); + + assertEquals(OK, rule.validate(journey)); + } + + @Test + void differentNumberOfPassingTimes() { + var pattern = new ServiceJourneyPatternBuilder(PATTERN_ID) + .withPointsInSequence(1, 2, 3) + .build(); + + var index = new NetexEntityIndex(); + index.journeyPatternsById.add(pattern); + + var journey = new ServiceJourneyBuilder(JOURNEY_ID) + .withPatternId(PATTERN_ID) + .withPassingTimes(List.of("P-1", "P-2")) + .build(); + + var rule = new JourneyPatternSJMismatch(); + rule.setup(index.readOnlyView()); + + assertEquals(DISCARD, rule.validate(journey)); + } + + @Test + void passThrough() { + var pattern = new ServiceJourneyPatternBuilder(PATTERN_ID) + .addStopPointInSequence(1, ACCESS) + .addStopPointInSequence(2, PASSTHROUGH) + .addStopPointInSequence(3, ACCESS) + .build(); + + var index = new NetexEntityIndex(); + index.journeyPatternsById.add(pattern); + + var journey = new ServiceJourneyBuilder(JOURNEY_ID) + .withPatternId(PATTERN_ID) + .withPassingTimes(List.of("P-1", "P-3")) + .build(); + + var rule = new JourneyPatternSJMismatch(); + rule.setup(index.readOnlyView()); + + assertEquals(OK, rule.validate(journey)); + } + + static class ServiceJourneyPatternBuilder { + + private final ServiceJourneyPattern pattern = new ServiceJourneyPattern(); + private final PointsInJourneyPattern_RelStructure points = new PointsInJourneyPattern_RelStructure(); + + ServiceJourneyPatternBuilder(String id) { + pattern.setId(id); + pattern.setPointsInSequence(points); + } + + ServiceJourneyPatternBuilder withPointsInSequence(int... orders) { + var items = Arrays.stream(orders).mapToObj(order -> pointInPattern(order, ACCESS)).toList(); + points.withPointInJourneyPatternOrStopPointInJourneyPatternOrTimingPointInJourneyPattern( + items + ); + return this; + } + + ServiceJourneyPatternBuilder addStopPointInSequence(int order, StopUseEnumeration stopUse) { + var point = pointInPattern(order, stopUse); + points + .getPointInJourneyPatternOrStopPointInJourneyPatternOrTimingPointInJourneyPattern() + .add(point); + return this; + } + + ServiceJourneyPattern build() { + return pattern; + } + + private static PointInLinkSequence_VersionedChildStructure pointInPattern( + int order, + StopUseEnumeration stopUse + ) { + var p = new StopPointInJourneyPattern(); + p.setId("P-%s".formatted(order)); + p.setOrder(BigInteger.valueOf(order)); + p.setStopUse(stopUse); + return p; + } + } + + static class ServiceJourneyBuilder { + + private final ServiceJourney journey = new ServiceJourney(); + + ServiceJourneyBuilder(String id) { + journey.setId(id); + } + + ServiceJourneyBuilder withPatternId(String id) { + var ref = MappingSupport.createWrappedRef(id, JourneyPatternRefStructure.class); + journey.withJourneyPatternRef(ref); + return this; + } + + ServiceJourneyBuilder withPassingTimes(PointsInJourneyPattern_RelStructure pointsInSequence) { + return withPassingTimes( + pointsInSequence + .getPointInJourneyPatternOrStopPointInJourneyPatternOrTimingPointInJourneyPattern() + .stream() + .map(EntityStructure::getId) + .toList() + ); + } + + ServiceJourneyBuilder withPassingTimes(Collection ids) { + var passingTimes = new TimetabledPassingTimes_RelStructure(); + passingTimes.withTimetabledPassingTime( + ids.stream().map(ServiceJourneyBuilder::timetabledPassingTime).toList() + ); + journey.withPassingTimes(passingTimes); + return this; + } + + ServiceJourney build() { + return journey; + } + + private static TimetabledPassingTime timetabledPassingTime(String pointInPatternRef) { + var passingTime = new TimetabledPassingTime(); + passingTime.withPointInJourneyPatternRef( + MappingSupport.createWrappedRef(pointInPatternRef, PointInJourneyPatternRefStructure.class) + ); + return passingTime; + } + } +} diff --git a/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java b/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java index 8f5d1a0208e..7b877abcf3a 100644 --- a/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java +++ b/src/test/java/org/opentripplanner/routing/algorithm/mapping/RaptorPathToItineraryMapperTest.java @@ -1,5 +1,6 @@ package org.opentripplanner.routing.algorithm.mapping; +import static com.google.common.truth.Truth.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -11,6 +12,7 @@ import java.time.Month; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -18,11 +20,15 @@ import org.opentripplanner._support.time.ZoneIds; import org.opentripplanner.ext.flex.FlexAccessEgress; import org.opentripplanner.ext.flex.FlexPathDurations; +import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.framework.model.Cost; import org.opentripplanner.framework.model.TimeAndCost; import org.opentripplanner.framework.time.TimeUtils; import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.StopTime; +import org.opentripplanner.model.plan.Leg; +import org.opentripplanner.model.plan.ScheduledTransitLeg; +import org.opentripplanner.model.plan.StreetLeg; import org.opentripplanner.raptor._data.api.TestPathBuilder; import org.opentripplanner.raptor._data.transit.TestAccessEgress; import org.opentripplanner.raptor._data.transit.TestRoute; @@ -51,12 +57,10 @@ import org.opentripplanner.street.search.state.State; import org.opentripplanner.street.search.state.TestStateBuilder; import org.opentripplanner.transit.model._data.TransitModelForTest; -import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.network.StopPattern; import org.opentripplanner.transit.model.network.TripPattern; -import org.opentripplanner.transit.model.organization.Agency; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.timetable.booking.RoutingBookingInfo; import org.opentripplanner.transit.service.DefaultTransitService; @@ -73,8 +77,7 @@ public class RaptorPathToItineraryMapperTest { private static final int TRANSIT_START = TimeUtils.time("10:00"); private static final int TRANSIT_END = TimeUtils.time("11:00"); - - private final TestTransitData data = new TestTransitData(); + private static final Route ROUTE = TransitModelForTest.route("route").build(); public static final RaptorCostCalculator COST_CALCULATOR = new DefaultCostCalculator<>( BOARD_COST_SEC, @@ -84,9 +87,9 @@ public class RaptorPathToItineraryMapperTest { STOP_COSTS ); - private static final RegularStop S1 = TEST_MODEL.stop("STOP1", 0.0, 0.0).build(); - - private static final RegularStop S2 = TEST_MODEL.stop("STOP2", 1.0, 1.0).build(); + private static final RegularStop S1 = TEST_MODEL.stop("STOP1").build(); + private static final RegularStop S2 = TEST_MODEL.stop("STOP2").build(); + private static final RegularStop S3 = TEST_MODEL.stop("STOP3").build(); @ParameterizedTest @ValueSource(ints = { 0, 3000, -3000 }) @@ -113,6 +116,35 @@ void createItineraryTestZeroDurationEgress(int lastLegCost) { ); } + @Test + void noExtraLegWhenTransferringAtSameStop() { + var mapper = getRaptorPathToItineraryMapper(); + + var path = transferAtSameStopPath(); + var itinerary = mapper.createItinerary(path); + assertThat(itinerary.getLegs().stream().map(Object::getClass)).doesNotContain(StreetLeg.class); + } + + @Test + void extraLegWhenTransferringAtSameStop() { + RaptorPathToItineraryMapper mapper = getRaptorPathToItineraryMapper(); + + var schedule = getTestTripSchedule2(); + var path = new TestPathBuilder(COST_CALCULATOR) + .access(TRANSIT_START - BOARD_SLACK, 1) + .bus(schedule, 2) + .bus(schedule, 1) + .egress(TestAccessEgress.free(1, RaptorCostConverter.toRaptorCost(100))); + + OTPFeature.ExtraTransferLegOnSameStop.testOn(() -> { + var itinerary = mapper.createItinerary(path); + assertEquals( + List.of(ScheduledTransitLeg.class, StreetLeg.class, ScheduledTransitLeg.class), + itinerary.getLegs().stream().map(Leg::getClass).toList() + ); + }); + } + @Test @Disabled("Need to write a general test framework to enable this.") void penalty() { @@ -184,6 +216,36 @@ void createItineraryWithOnBoardFlexAccess() { assertEquals(3, itinerary.getLegs().size(), "The wrong number of legs was returned"); } + private RaptorPath transferAtSameStopPath() { + var schedule = transferAtSameStopSchedule(); + return new TestPathBuilder(COST_CALCULATOR) + .access(TRANSIT_START, 1) + .bus(schedule, 2) + .bus(schedule, 1) + .egress(TestAccessEgress.free(1, RaptorCostConverter.toRaptorCost(100))); + } + + private TestTripSchedule transferAtSameStopSchedule() { + TestTransitData data = new TestTransitData(); + var pattern = TestTripPattern.pattern("TestPattern", 1, 2, 3, 2, 1).withRoute(ROUTE); + + var timetable = new TestTripSchedule.Builder() + .times( + TimeUtils.time("10:00"), + TimeUtils.time("10:05"), + TimeUtils.time("10:10"), + TimeUtils.time("10:15"), + TimeUtils.time("10:20") + ) + .pattern(pattern) + .originalPattern(getOriginalPattern(pattern)) + .build(); + + data.withRoutes(TestRoute.route("TransferAtSameStop", 1, 2, 3, 2, 1).withTimetable(timetable)); + + return data.getRoute(0).getTripSchedule(0); + } + private TripPattern getOriginalPattern(TestTripPattern pattern) { var stopModelBuilder = TEST_MODEL.stopModelBuilder(); ArrayList stopTimes = new ArrayList<>(); @@ -233,7 +295,12 @@ private static TransitLayer getTransitLayer() { new HashMap<>(), null, null, - TEST_MODEL.stopModelBuilder().withRegularStop(S1).withRegularStop(S2).build(), + TEST_MODEL + .stopModelBuilder() + .withRegularStop(S1) + .withRegularStop(S2) + .withRegularStop(S3) + .build(), null, null, null, @@ -241,21 +308,30 @@ private static TransitLayer getTransitLayer() { ); } - private TestTripSchedule getTestTripSchedule() { - var agency = Agency - .of(new FeedScopedId("TestFeed", "Auth_1")) - .withName("Test_Agency") - .withTimezone("Europe/Stockholm") - .build(); + private TestTripSchedule getTestTripSchedule2() { + TestTransitData data = new TestTransitData(); + var pattern = TestTripPattern.pattern("TestPattern", 1, 2, 3, 2, 1).withRoute(ROUTE); - var route = Route - .of(new FeedScopedId("TestFeed", "Line_1")) - .withAgency(agency) - .withMode(TransitMode.BUS) - .withShortName("Test_Bus") + var timetable = new TestTripSchedule.Builder() + .times( + TimeUtils.time("10:00"), + TimeUtils.time("10:05"), + TimeUtils.time("10:10"), + TimeUtils.time("10:15"), + TimeUtils.time("10:20") + ) + .pattern(pattern) + .originalPattern(getOriginalPattern(pattern)) .build(); - var pattern = TestTripPattern.pattern("TestPattern", 1, 2).withRoute(route); + data.withRoutes(TestRoute.route("TestRoute_1", 1, 2, 3, 2, 1).withTimetable(timetable)); + + return data.getRoute(0).getTripSchedule(0); + } + + private TestTripSchedule getTestTripSchedule() { + TestTransitData data = new TestTransitData(); + var pattern = TestTripPattern.pattern("TestPattern", 1, 2).withRoute(ROUTE); var timetable = new TestTripSchedule.Builder() .times(TRANSIT_START, TRANSIT_END) diff --git a/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java b/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java index 54733f084d6..b04a1258984 100644 --- a/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java +++ b/src/test/java/org/opentripplanner/transit/service/DefaultTransitServiceTest.java @@ -12,6 +12,7 @@ import java.util.Set; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.opentripplanner.model.RealTimeTripUpdate; import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.framework.Deduplicator; @@ -68,7 +69,9 @@ static void setup() { .withDepartureTimes(new int[] { 0, 1 }) .build() ); - timetableSnapshot.update(REAL_TIME_PATTERN, tripTimes, LocalDate.now()); + timetableSnapshot.update( + new RealTimeTripUpdate(REAL_TIME_PATTERN, tripTimes, LocalDate.now()) + ); return timetableSnapshot.commit(); }); diff --git a/src/test/java/org/opentripplanner/updater/alert/AlertsUpdateHandlerTest.java b/src/test/java/org/opentripplanner/updater/alert/AlertsUpdateHandlerTest.java index 0474c328162..6e590ce4957 100644 --- a/src/test/java/org/opentripplanner/updater/alert/AlertsUpdateHandlerTest.java +++ b/src/test/java/org/opentripplanner/updater/alert/AlertsUpdateHandlerTest.java @@ -35,7 +35,7 @@ public class AlertsUpdateHandlerTest { @BeforeEach public void setUp() { - handler = new AlertsUpdateHandler(); + handler = new AlertsUpdateHandler(false); handler.setFeedId("1"); handler.setEarlyStart(5); handler.setTransitAlertService(service); @@ -531,7 +531,7 @@ private TransitAlert processOneAlert(GtfsRealtime.Alert alert) { .setHeader(GtfsRealtime.FeedHeader.newBuilder().setGtfsRealtimeVersion("2.0")) .addEntity(GtfsRealtime.FeedEntity.newBuilder().setAlert(alert).setId("1")) .build(); - handler.update(message); + handler.update(message, null); Collection alerts = service.getAllAlerts(); assertEquals(1, alerts.size()); return alerts.iterator().next(); diff --git a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java index bf6f743eac7..a40bd8bd797 100644 --- a/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java +++ b/src/test/java/org/opentripplanner/updater/trip/RealtimeTestEnvironment.java @@ -12,7 +12,6 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import org.opentripplanner.DateTimeHelper; -import org.opentripplanner.ext.siri.SiriFuzzyTripMatcher; import org.opentripplanner.ext.siri.SiriTimetableSnapshotSource; import org.opentripplanner.ext.siri.updater.EstimatedTimetableHandler; import org.opentripplanner.framework.i18n.I18NString; @@ -20,11 +19,13 @@ import org.opentripplanner.model.StopTime; import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.model.calendar.CalendarServiceData; +import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.framework.Deduplicator; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.model.network.Route; import org.opentripplanner.transit.model.network.TripPattern; +import org.opentripplanner.transit.model.organization.Operator; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.Station; import org.opentripplanner.transit.model.site.StopLocation; @@ -37,6 +38,7 @@ import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; import org.opentripplanner.transit.service.TransitService; +import org.opentripplanner.updater.DefaultRealTimeUpdateContext; import org.opentripplanner.updater.TimetableSnapshotSourceParameters; import org.opentripplanner.updater.spi.UpdateResult; import uk.org.siri.siri20.EstimatedTimetableDeliveryStructure; @@ -82,6 +84,7 @@ public final class RealtimeTestEnvironment { public final FeedScopedId route1Id = TransitModelForTest.id("TestRoute1"); public final Trip trip1; public final Trip trip2; + public final Operator operator1; public final TransitModel transitModel; private final SiriTimetableSnapshotSource siriSource; private final TimetableSnapshotSource gtfsSource; @@ -111,7 +114,10 @@ private RealtimeTestEnvironment(SourceType sourceType) { transitModel.initTimeZone(timeZone); transitModel.addAgency(TransitModelForTest.AGENCY); - Route route1 = TransitModelForTest.route(route1Id).build(); + operator1 = Operator.of(operator1Id).withName("Operator 1").build(); + transitModel.getOperators().add(operator1); + + Route route1 = TransitModelForTest.route(route1Id).withOperator(operator1).build(); trip1 = createTrip( @@ -180,12 +186,7 @@ public String getFeedId() { } private EstimatedTimetableHandler getEstimatedTimetableHandler(boolean fuzzyMatching) { - return new EstimatedTimetableHandler( - siriSource, - fuzzyMatching ? new SiriFuzzyTripMatcher(getTransitService()) : null, - getTransitService(), - getFeedId() - ); + return new EstimatedTimetableHandler(siriSource, fuzzyMatching, getFeedId()); } public TripPattern getPatternForTrip(FeedScopedId tripId) { @@ -303,7 +304,15 @@ private UpdateResult applyEstimatedTimetable( ) { Objects.requireNonNull(siriSource, "Test environment is configured for GTFS-RT only"); UpdateResult updateResult = getEstimatedTimetableHandler(fuzzyMatching) - .applyUpdate(updates, DIFFERENTIAL); + .applyUpdate( + updates, + DIFFERENTIAL, + new DefaultRealTimeUpdateContext( + new Graph(), + transitModel, + siriSource.getTimetableSnapshotBuffer() + ) + ); commitTimetableSnapshot(); return updateResult; } diff --git a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotManagerTest.java b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotManagerTest.java index 384e8a41fd1..1f3f71a2588 100644 --- a/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotManagerTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/TimetableSnapshotManagerTest.java @@ -6,7 +6,6 @@ import static org.opentripplanner.updater.trip.TimetableSnapshotManagerTest.SameAssert.NotSame; import static org.opentripplanner.updater.trip.TimetableSnapshotManagerTest.SameAssert.Same; -import java.time.Duration; import java.time.LocalDate; import java.time.Month; import java.util.concurrent.atomic.AtomicReference; @@ -14,6 +13,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.opentripplanner.model.RealTimeTripUpdate; import org.opentripplanner.model.TimetableSnapshot; import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.network.TripPattern; @@ -86,7 +86,7 @@ public void testPurgeExpiredData( clock::get ); - var res1 = snapshotManager.updateBuffer(PATTERN, TRIP_TIMES, YESTERDAY); + var res1 = snapshotManager.updateBuffer(new RealTimeTripUpdate(PATTERN, TRIP_TIMES, YESTERDAY)); assertTrue(res1.isSuccess()); snapshotManager.commitTimetableSnapshot(true); @@ -95,7 +95,7 @@ public void testPurgeExpiredData( // Turn the clock to tomorrow clock.set(TOMORROW); - var res2 = snapshotManager.updateBuffer(PATTERN, TRIP_TIMES, TODAY); + var res2 = snapshotManager.updateBuffer(new RealTimeTripUpdate(PATTERN, TRIP_TIMES, TODAY)); assertTrue(res2.isSuccess()); snapshotManager.purgeAndCommit(); diff --git a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java index 8371c5dda3a..2e0b9d3d88e 100644 --- a/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java +++ b/src/test/java/org/opentripplanner/updater/trip/moduletests/addition/AddedTest.java @@ -16,9 +16,12 @@ import java.util.List; import org.junit.jupiter.api.Test; import org.opentripplanner.model.PickDrop; +import org.opentripplanner.transit.model._data.TransitModelForTest; import org.opentripplanner.transit.model.basic.TransitMode; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.timetable.RealTimeState; +import org.opentripplanner.transit.model.timetable.Trip; +import org.opentripplanner.transit.service.TransitService; import org.opentripplanner.updater.spi.UpdateSuccess; import org.opentripplanner.updater.trip.RealtimeTestEnvironment; import org.opentripplanner.updater.trip.TripUpdateBuilder; @@ -62,8 +65,12 @@ void addedTripWithNewRoute() { assertEquals(TripUpdateBuilder.ROUTE_NAME, route.getName()); assertEquals(TransitMode.RAIL, route.getMode()); - var fromTransitModel = env.getTransitService().getRouteForId(route.getId()); + TransitService transitService = env.getTransitService(); + var fromTransitModel = transitService.getRouteForId(route.getId()); assertEquals(fromTransitModel, route); + var patternsForRoute = transitService.getPatternsForRoute(route); + assertEquals(1, patternsForRoute.size()); + assertEquals(pattern, patternsForRoute.stream().findFirst().orElseThrow()); assertEquals(PickDrop.CALL_AGENCY, pattern.getBoardType(0)); assertEquals(PickDrop.CALL_AGENCY, pattern.getAlightType(0)); @@ -122,6 +129,12 @@ void repeatedlyAddedTripWithNewRoute() { private TripPattern assertAddedTrip(String tripId, RealtimeTestEnvironment env) { var snapshot = env.getTimetableSnapshot(); + + TransitService transitService = env.getTransitService(); + Trip trip = transitService.getTripForId(TransitModelForTest.id(ADDED_TRIP_ID)); + assertNotNull(trip); + assertNotNull(transitService.getPatternForTrip(trip)); + var stopA = env.transitModel.getStopModel().getRegularStop(env.stopA1.getId()); // Get the trip pattern of the added trip which goes through stopA var patternsAtA = env.getTimetableSnapshot().getPatternsForStop(stopA); diff --git a/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdaterTest.java b/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdaterTest.java index 87222eeba8f..33455cf9b9a 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdaterTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingAvailabilityUpdaterTest.java @@ -18,6 +18,7 @@ import org.opentripplanner.standalone.config.routerconfig.updaters.VehicleParkingUpdaterConfig; import org.opentripplanner.transit.model.framework.FeedScopedId; import org.opentripplanner.transit.service.TransitModel; +import org.opentripplanner.updater.DefaultRealTimeUpdateContext; import org.opentripplanner.updater.GraphUpdaterManager; import org.opentripplanner.updater.GraphWriterRunnable; import org.opentripplanner.updater.spi.DataSource; @@ -118,14 +119,18 @@ class GraphUpdaterMock extends GraphUpdaterManager { private static final Graph GRAPH = new Graph(); private static final TransitModel TRANSIT_MODEL = new TransitModel(); + public static final DefaultRealTimeUpdateContext REAL_TIME_UPDATE_CONTEXT = new DefaultRealTimeUpdateContext( + GRAPH, + TRANSIT_MODEL + ); public GraphUpdaterMock(List updaters) { - super(GRAPH, TRANSIT_MODEL, updaters); + super(REAL_TIME_UPDATE_CONTEXT, updaters); } @Override public Future execute(GraphWriterRunnable runnable) { - runnable.run(GRAPH, TRANSIT_MODEL); + runnable.run(REAL_TIME_UPDATE_CONTEXT); return Futures.immediateVoidFuture(); } } 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 f49299eb4ea..e504e42eb6c 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_parking/VehicleParkingUpdaterTest.java @@ -21,6 +21,7 @@ import org.opentripplanner.street.model.edge.VehicleParkingEdge; import org.opentripplanner.street.model.vertex.VehicleParkingEntranceVertex; import org.opentripplanner.transit.service.TransitModel; +import org.opentripplanner.updater.DefaultRealTimeUpdateContext; import org.opentripplanner.updater.GraphUpdaterManager; import org.opentripplanner.updater.GraphWriterRunnable; import org.opentripplanner.updater.spi.DataSource; @@ -30,8 +31,9 @@ class VehicleParkingUpdaterTest { private DataSource dataSource; private Graph graph; - private TransitModel transitModel; + private DefaultRealTimeUpdateContext realTimeUpdateContext; + private VehicleParkingUpdater vehicleParkingUpdater; @BeforeEach @@ -41,6 +43,7 @@ public void setup() { graphData.initGraph(); graph = graphData.getGraph(); transitModel = graphData.getTransitModel(); + realTimeUpdateContext = new DefaultRealTimeUpdateContext(graph, transitModel); dataSource = (DataSource) Mockito.mock(DataSource.class); when(dataSource.update()).thenReturn(true); @@ -268,12 +271,12 @@ private void runUpdaterOnce() { class GraphUpdaterMock extends GraphUpdaterManager { public GraphUpdaterMock(Graph graph, TransitModel transitModel, List updaters) { - super(graph, transitModel, updaters); + super(realTimeUpdateContext, updaters); } @Override public Future execute(GraphWriterRunnable runnable) { - runnable.run(graph, transitModel); + runnable.run(realTimeUpdateContext); return Futures.immediateVoidFuture(); } } diff --git a/src/test/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdaterTest.java b/src/test/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdaterTest.java index 60f0389abe7..0016bf4c00c 100644 --- a/src/test/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdaterTest.java +++ b/src/test/java/org/opentripplanner/updater/vehicle_rental/VehicleRentalUpdaterTest.java @@ -16,6 +16,7 @@ import org.opentripplanner.service.vehiclerental.internal.DefaultVehicleRentalService; import org.opentripplanner.service.vehiclerental.model.VehicleRentalPlace; import org.opentripplanner.transit.service.TransitModel; +import org.opentripplanner.updater.DefaultRealTimeUpdateContext; import org.opentripplanner.updater.GraphUpdaterManager; import org.opentripplanner.updater.GraphWriterRunnable; import org.opentripplanner.updater.spi.HttpHeaders; @@ -45,7 +46,7 @@ void failingDatasourceCountsAsPrimed() { static class MockManager extends GraphUpdaterManager { public MockManager(VehicleRentalUpdater updater) { - super(new Graph(), new TransitModel(), List.of(updater)); + super(new DefaultRealTimeUpdateContext(new Graph(), new TransitModel()), List.of(updater)); } @Override