diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index da79a466e0b..a67cae22195 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -47,7 +47,7 @@ Write a few words on how the new code is tested. - Was the code designed so it is unit testable? - Were any tests applied to the smallest appropriate unit? - Do all tests - pass [the continuous integration service](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/docs/Developers-Guide.md#continuous-integration) + pass [the continuous integration service](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/doc/user/Developers-Guide.md#continuous-integration) ? ### Documentation @@ -59,7 +59,7 @@ Write a few words on how the new code is tested. ### Changelog -The [changelog file](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/docs/Changelog.md) +The [changelog file](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/doc/user/Changelog.md) is generated from the pull-request title, make sure the title describe the feature or issue fixed. To exclude the PR from the changelog add the label `skip changelog` to the PR. diff --git a/.github/workflows/cibuild.yml b/.github/workflows/cibuild.yml index 4b814d03e35..89bc77b2b97 100644 --- a/.github/workflows/cibuild.yml +++ b/.github/workflows/cibuild.yml @@ -108,7 +108,7 @@ jobs: if: github.event_name == 'pull_request' - name: Install Python dependencies - run: pip install -r docs/requirements.txt + run: pip install -r doc/user/requirements.txt - name: Build main documentation diff --git a/.github/workflows/post-merge.yml b/.github/workflows/post-merge.yml index 86f3741aada..b26198e99a6 100644 --- a/.github/workflows/post-merge.yml +++ b/.github/workflows/post-merge.yml @@ -29,8 +29,8 @@ jobs: run: | # add a line above the one which contains AUTOMATIC_CHANGELOG_PLACEHOLDER ITEM="${TITLE} [#${NUMBER}](${URL})" - TEMP_FILE=docs/Changelog.generated.md - FILE=docs/Changelog.md + TEMP_FILE=doc/user/Changelog.generated.md + FILE=doc/user/Changelog.md awk "/CHANGELOG_PLACEHOLDER/{print \"- $ITEM\"}1" $FILE > $TEMP_FILE mv $TEMP_FILE $FILE git add $FILE diff --git a/.gitignore b/.gitignore index a90f06cdcb0..6fac28d2178 100644 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,7 @@ o_o_standalone_config_IncludeFileDirectiveTest_part.json _site/ build/ dist/ -docs/_build/ +doc/user/_build/ gen-java/ gen-javabean/ gen-py/ diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index acd90b787e6..4bd313c7a06 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,24 +1,28 @@ # OTP Architecture -OTP is developed over more than 10 years, and most of the design documentation is in the code as +OTP has been developed for more than 15 years, and most of the design documentation is in the code as comments and JavaDoc. Over the years the complexity have increased, and the natural developer turnover creates a demand for more architecture and design documentation. The new OTP2 documentation is put together with the source; hopefully making it easier to maintain. Instead of documenting modules in old style _package-info.java_ files we use _package.md_ files. This document should serve as an index to all existing top-level documented components. -This document is far from complete - hopefully it can evolve over time and become a good +This document is far from complete. Hopefully it can evolve over time and become a good introduction to OTP architecture. The OTP project GitHub issues are a good place to look for detailed discussions on many design decisions. -Be sure to also read the [developer documentation](docs/Developers-Guide.md). +We document [Decision Records](DEVELOPMENT_DECISION_RECORDS.md) in a log. Make sure you as a developer are familiar +with the decisions and follow them. Reviewers should use them actively when reviewing code and may +use them to ask for changes. + +Be sure to also read the [developer documentation](doc/user/Developers-Guide.md). ## Modules/Components The diagram shows a simplified/generic version on how we want to model the OTP components with 2 examples. The Transit model is more complex than the VehiclePosition model. -![MainModelOverview](docs/images/ServiceModelOverview.png) +![MainModelOverview](doc/dev/images/ServiceModelOverview.png) - `Use Case Service` A service which combine the functionality in many `Domain Services` to fulfill a use-case or set of features. It may have an api with request/response classes. These are @@ -26,7 +30,7 @@ examples. The Transit model is more complex than the VehiclePosition model. class has the same name as the interface with prefix `Default`. - `Domain Model` A model which encapsulate a business area. In the drawing two examples are shown, the `transit` and `vhicleposition` domain model. The transit model is more complex so the - implementation have a separate `Service` and `Repository`. Almost all http endpoints are , + implementation has a separate `Service` and `Repository`. Almost all http endpoints are, read-only so the `Service` can focus on serving the http API endpoints, while the repository is used to maintain the model by the updaters. @@ -41,10 +45,6 @@ but this is a start and we would like to expand this list in the future. The Configuration module is responsible for loading and parsing OTP configuration files and map them into Plan Old Java Objects (POJOs). These POJOs are injected into the other components. -### [REST API](src/main/java/org/opentripplanner/api/package.md) - -Short introduction to the REST API. - ### [GTFS import module](src/main/java/org/opentripplanner/gtfs/package.md) Used to import GTFS transit data files. diff --git a/DEVELOPMENT_DECISION_RECORDS.md b/DEVELOPMENT_DECISION_RECORDS.md new file mode 100644 index 00000000000..10b9e005809 --- /dev/null +++ b/DEVELOPMENT_DECISION_RECORDS.md @@ -0,0 +1,106 @@ +# Development Decision Records + + +## Use-Decision-Records + +We will [use decision-records](doc/dev/decisionrecords/UseDecisionRecords.md) to document OTP +development relevant decision. Use the [template](doc/dev/decisionrecords/_TEMPLATE.md) to describe +new decision records. + + +## Scout-Rule + +Leave things BETTER than you found them, clean up code you visit or/and add unit +tests. Expect to include some code cleanup as part of all PRs. + +## Follow-Naming-Conventions + +Use established terminology from GTFS, NeTEx or the existing OTP code. Make sure the code is easy +to read and understand. [Follow naming conventions](CODE_CONVENTIONS.md#naming-conventions) . + + +## Write-Code-Documentation - Use JavaDoc + +Document the business intention and decisions in the relevant code. Do not repeat the logic +expressed in the code. Use JavaDoc, you may have to refactor part of your code to encapsulate the +business logic into a method or class to do this. + +Document all `public` types, methods and fields with JavaDoc. It is ok to document implementation +notes on `private` members and as inline comments. + +> If you decide to NOT follow these decision records, then you must document why. + +**See also** + - [Developers-Guide > Code comments](doc/user/Developers-Guide.md#code-comments). + - [Codestyle > Javadoc Guidlines](doc/dev/decisionrecords/Codestyle.md#javadoc-guidlines) - JavaDoc checklist + + +## Document-Config-and-APIs + +Document API and configuration parameters. + + +## Respect-Codestyle + +OTP uses prettier to format code. For more information on code style see the +[Codestyle](doc/dev/decisionrecords/Codestyle.md) document. + + +## Use-Object-Oriented-Principals + +Respect Object-Oriented principals + - Honor encapsulation & Single-responsibility principle + - Abstraction - Use interfaces when a module needs "someone" to play a role + - Inheritance - Inheritances expresses “is-a” and/or “has-a” relationship, do not use it "just" + to share data/functionality. + - Use polymorphism and not `instanceof`. + + +## Use-Dependency-Injection + +Use dependency injection to wire components. You can use manual DI or Dagger. Put the +wiring code in `/configure/Module.java`. + +OTP will use a dependency injection library or framework to handle object lifecycles (particularly +request-scoped vs. singleton scoped) and ensure selective availability of components, services, +context, and configuration at their site of use. Systems that operate via imperative Java code +(whether hand-written or generated) will be preferred over those operating through reflection or +external markup files. See [additional background](https://github.com/opentripplanner/OpenTripPlanner/pull/5360#issuecomment-1910134299). + +## Use-Module-Encapsulation + +Keep modules clean. Consider adding an `api`, `spi` and mapping code to +isolate the module from the rest of the code. Avoid circular dependencies between modules. + + +## DRY - Do not repeat yourself + +Keep the code [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) - Do not +repeat yourself. Avoid implementing the same business rule in two places -> refactor. + + +## Avoid-Feature-Envy + +[Feature envy](https://refactoring.guru/smells/feature-envy) + + +## Test-Coverage + +All _business_ logic should have unit tests. Keep integration/system tests to a minimum. Add test at +the lowest level practical to test the business feature. Prefer unit tests on a method over a class, +over a module, over the system. On all non-trivial code, full _branch_ test coverage is preferable. +Tests should be designed to genuinely demonstrate correctness or adherence to specifications, not +simply inflate line coverage numbers. + + +## Use-Immutable-Types + +Prefer immutable types over mutable. Use builders where appropriate. See +[Records, POJOs and Builders](doc/dev/decisionrecords/RecordsPOJOsBuilders.md#records-pojos-and-builders) + + +## Be-Careful-With-Records + +[Avoid using records if you cannot encapsulate it properly](doc/dev/decisionrecords/RecordsPOJOsBuilders.md#records) + + diff --git a/client/package-lock.json b/client/package-lock.json index 87139a1a909..4632899b38f 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.5.1", + "maplibre-gl": "4.6.0", "react": "18.3.1", "react-bootstrap": "2.10.4", "react-dom": "18.3.1", @@ -23,8 +23,8 @@ "@graphql-codegen/client-preset": "4.3.3", "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.4.1", - "@testing-library/react": "16.0.0", - "@types/react": "18.3.3", + "@testing-library/react": "16.0.1", + "@types/react": "18.3.5", "@types/react-dom": "18.3.0", "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", @@ -32,15 +32,15 @@ "@vitest/coverage-v8": "2.0.5", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", - "eslint-plugin-import": "2.29.1", + "eslint-plugin-import": "2.30.0", "eslint-plugin-jsx-a11y": "6.9.0", - "eslint-plugin-react": "7.35.0", + "eslint-plugin-react": "7.35.1", "eslint-plugin-react-hooks": "4.6.2", - "eslint-plugin-react-refresh": "0.4.9", - "jsdom": "24.1.1", + "eslint-plugin-react-refresh": "0.4.11", + "jsdom": "25.0.0", "prettier": "3.3.3", "typescript": "5.5.4", - "vite": "5.4.0", + "vite": "5.4.2", "vitest": "2.0.5" } }, @@ -3010,9 +3010,9 @@ } }, "node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.3.0.tgz", - "integrity": "sha512-eSiQ3E5LUSxAOY9ABXGyfNhout2iEa6mUxKeaQ9nJ8NL1NuaQYU7zKqzx/LEYcXe1neT4uYAgM1wYZj3fTSXtA==", + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.3.1.tgz", + "integrity": "sha512-5ueL4UDitzVtceQ8J4kY+Px3WK+eZTsmGwha3MBKHKqiHvKrjWWwBCIl1K8BuJSc5OFh83uI8IFNoFvQxX2uUw==", "license": "ISC", "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", @@ -3022,7 +3022,7 @@ "quickselect": "^2.0.0", "rw": "^1.3.3", "sort-object": "^3.0.3", - "tinyqueue": "^2.0.3" + "tinyqueue": "^3.0.0" }, "bin": { "gl-style-format": "dist/gl-style-format.mjs", @@ -3537,174 +3537,236 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", - "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.2.tgz", + "integrity": "sha512-fSuPrt0ZO8uXeS+xP3b+yYTCBUd05MoSp2N/MFOgjhhUhMmchXlpTQrTpI8T+YAwAQuK7MafsCOxW7VrPMrJcg==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", - "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.2.tgz", + "integrity": "sha512-xGU5ZQmPlsjQS6tzTTGwMsnKUtu0WVbl0hYpTPauvbRAnmIvpInhJtgjj3mcuJpEiuUw4v1s4BimkdfDWlh7gA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", - "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.2.tgz", + "integrity": "sha512-99AhQ3/ZMxU7jw34Sq8brzXqWH/bMnf7ZVhvLk9QU2cOepbQSVTns6qoErJmSiAvU3InRqC2RRZ5ovh1KN0d0Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", - "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.2.tgz", + "integrity": "sha512-ZbRaUvw2iN/y37x6dY50D8m2BnDbBjlnMPotDi/qITMJ4sIxNY33HArjikDyakhSv0+ybdUxhWxE6kTI4oX26w==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", - "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.2.tgz", + "integrity": "sha512-ztRJJMiE8nnU1YFcdbd9BcH6bGWG1z+jP+IPW2oDUAPxPjo9dverIOyXz76m6IPA6udEL12reYeLojzW2cYL7w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.2.tgz", + "integrity": "sha512-flOcGHDZajGKYpLV0JNc0VFH361M7rnV1ee+NTeC/BQQ1/0pllYcFmxpagltANYt8FYf9+kL6RSk80Ziwyhr7w==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", - "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.2.tgz", + "integrity": "sha512-69CF19Kp3TdMopyteO/LJbWufOzqqXzkrv4L2sP8kfMaAQ6iwky7NoXTp7bD6/irKgknDKM0P9E/1l5XxVQAhw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", - "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.2.tgz", + "integrity": "sha512-48pD/fJkTiHAZTnZwR0VzHrao70/4MlzJrq0ZsILjLW/Ab/1XlVUStYyGt7tdyIiVSlGZbnliqmult/QGA2O2w==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.2.tgz", + "integrity": "sha512-cZdyuInj0ofc7mAQpKcPR2a2iu4YM4FQfuUzCVA2u4HI95lCwzjoPtdWjdpDKyHxI0UO82bLDoOaLfpZ/wviyQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", - "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.2.tgz", + "integrity": "sha512-RL56JMT6NwQ0lXIQmMIWr1SW28z4E4pOhRRNqwWZeXpRlykRIlEpSWdsgNWJbYBEWD84eocjSGDu/XxbYeCmwg==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.2.tgz", + "integrity": "sha512-PMxkrWS9z38bCr3rWvDFVGD6sFeZJw4iQlhrup7ReGmfn7Oukrr/zweLhYX6v2/8J6Cep9IEA/SmjXjCmSbrMQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", - "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.2.tgz", + "integrity": "sha512-B90tYAUoLhU22olrafY3JQCFLnT3NglazdwkHyxNDYF/zAxJt5fJUB/yBoWFoIQ7SQj+KLe3iL4BhOMa9fzgpw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", - "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.2.tgz", + "integrity": "sha512-7twFizNXudESmC9oneLGIUmoHiiLppz/Xs5uJQ4ShvE6234K0VB1/aJYU3f/4g7PhssLGKBVCC37uRkkOi8wjg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", - "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.2.tgz", + "integrity": "sha512-9rRero0E7qTeYf6+rFh3AErTNU1VCQg2mn7CQcI44vNUWM9Ze7MSRS/9RFuSsox+vstRt97+x3sOhEey024FRQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", - "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.2.tgz", + "integrity": "sha512-5rA4vjlqgrpbFVVHX3qkrCo/fZTj1q0Xxpg+Z7yIo3J2AilW7t2+n6Q8Jrx+4MrYpAnjttTYF8rr7bP46BPzRw==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", - "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.2.tgz", + "integrity": "sha512-6UUxd0+SKomjdzuAcp+HAmxw1FlGBnl1v2yEPSabtx4lBfdXHDVsW7+lQkgz9cNFJGY3AWR7+V8P5BqkD9L9nA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, "node_modules/@swc/helpers": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.7.tgz", @@ -3734,9 +3796,9 @@ } }, "node_modules/@testing-library/react": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.0.tgz", - "integrity": "sha512-guuxUKRWQ+FgNX0h0NS0FIq3Q3uLtWVpBzcLOggmfMoUpgBnzBzvLLd4fbm6yS8ydJd94cIfY4yP9qUQjM2KwQ==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz", + "integrity": "sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==", "dev": true, "license": "MIT", "dependencies": { @@ -3889,9 +3951,9 @@ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/react": { - "version": "18.3.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", - "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", + "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==", "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -6070,10 +6132,11 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", - "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.9.0.tgz", + "integrity": "sha512-McVbYmwA3NEKwRQY5g4aWMdcZE5xZxV8i8l7CqJSrameuGSQJtSWaL/LxTEzSKKaCcOhlpDR8XEfYXWPrdo/ZQ==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^3.2.7" }, @@ -6096,26 +6159,28 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz", + "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==", "dev": true, + "license": "MIT", "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", "array.prototype.flat": "^1.3.2", "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", + "eslint-module-utils": "^2.9.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", "semver": "^6.3.1", "tsconfig-paths": "^3.15.0" }, @@ -6131,6 +6196,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" @@ -6141,6 +6207,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.1" } @@ -6150,6 +6217,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -6162,6 +6230,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" }, @@ -6245,9 +6314,9 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.35.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz", - "integrity": "sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==", + "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==", "dev": true, "license": "MIT", "dependencies": { @@ -6290,9 +6359,9 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.9.tgz", - "integrity": "sha512-QK49YrBAo5CLNLseZ7sZgvgTy21E6NEw22eZqc4teZfH8pxV3yXc9XXOYfUI6JNpw7mfHNkAeWtBxrTyykB6HA==", + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.11.tgz", + "integrity": "sha512-wrAKxMbVr8qhXTtIKfXqAn5SAtRZt0aXxe5P23Fh4pUAdC6XEsybGLB8P0PI4j1yYqOgUEUlzKAGDfo7rJOjcw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -7062,27 +7131,41 @@ } }, "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-4.0.0.tgz", + "integrity": "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==", + "license": "MIT", "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" + "ini": "^4.1.3", + "kind-of": "^6.0.3", + "which": "^4.0.0" }, "engines": { - "node": ">=6" + "node": ">=16" + } + }, + "node_modules/global-prefix/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "license": "ISC", + "engines": { + "node": ">=16" } }, "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "license": "ISC", "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { - "which": "bin/which" + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" } }, "node_modules/globals": { @@ -7533,9 +7616,13 @@ "dev": true }, "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/inquirer": { "version": "8.2.6", @@ -7693,12 +7780,16 @@ } }, "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8094,7 +8185,8 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/isobject": { "version": "3.0.1", @@ -8229,9 +8321,9 @@ } }, "node_modules/jsdom": { - "version": "24.1.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.1.1.tgz", - "integrity": "sha512-5O1wWV99Jhq4DV7rCLIoZ/UIhyQeDR7wHVyZAHAshbrvZsLs+Xzz7gtwnlJTJDjleiTKh54F4dXrX70vJQTyJQ==", + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.0.tgz", + "integrity": "sha512-OhoFVT59T7aEq75TVw9xxEfkXgacpqAhQaYgP9y/fDqWQCMB/b1H66RfmPm/MaeaAIU9nDwMOVTlPN51+ao6CQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8727,9 +8819,9 @@ } }, "node_modules/maplibre-gl": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.5.1.tgz", - "integrity": "sha512-pKFDK8ZU2atwZWC8gdPVhN7Bf5HIPgtA+IG/iQ7J6WgmqSwCSmylc5q3stahWqXfx9PYUwVNJITrp1Hw96SUiA==", + "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==", "license": "BSD-3-Clause", "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", @@ -8739,7 +8831,7 @@ "@mapbox/unitbezier": "^0.0.1", "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", - "@maplibre/maplibre-gl-style-spec": "^20.3.0", + "@maplibre/maplibre-gl-style-spec": "^20.3.1", "@types/geojson": "^7946.0.14", "@types/geojson-vt": "3.2.5", "@types/mapbox__point-geometry": "^0.1.4", @@ -8749,7 +8841,7 @@ "earcut": "^3.0.0", "geojson-vt": "^4.0.2", "gl-matrix": "^3.4.3", - "global-prefix": "^3.0.0", + "global-prefix": "^4.0.0", "kdbush": "^4.0.2", "murmurhash-js": "^1.0.0", "pbf": "^3.3.0", @@ -8773,12 +8865,6 @@ "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", "license": "ISC" }, - "node_modules/maplibre-gl/node_modules/tinyqueue": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", - "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", - "license": "ISC" - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -9543,9 +9629,9 @@ } }, "node_modules/postcss": { - "version": "8.4.40", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz", - "integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==", + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", "dev": true, "funding": [ { @@ -10077,10 +10163,11 @@ } }, "node_modules/rollup": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", - "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.2.tgz", + "integrity": "sha512-e3TapAgYf9xjdLvKQCkQTnbTKd4a6jwlpQSJJFokHGaX2IVjoEqkIIhiQfqsi0cdwlOD+tQGuOd5AJkc5RngBw==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "1.0.5" }, @@ -10092,19 +10179,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.0", - "@rollup/rollup-android-arm64": "4.13.0", - "@rollup/rollup-darwin-arm64": "4.13.0", - "@rollup/rollup-darwin-x64": "4.13.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", - "@rollup/rollup-linux-arm64-gnu": "4.13.0", - "@rollup/rollup-linux-arm64-musl": "4.13.0", - "@rollup/rollup-linux-riscv64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-musl": "4.13.0", - "@rollup/rollup-win32-arm64-msvc": "4.13.0", - "@rollup/rollup-win32-ia32-msvc": "4.13.0", - "@rollup/rollup-win32-x64-msvc": "4.13.0", + "@rollup/rollup-android-arm-eabi": "4.21.2", + "@rollup/rollup-android-arm64": "4.21.2", + "@rollup/rollup-darwin-arm64": "4.21.2", + "@rollup/rollup-darwin-x64": "4.21.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.21.2", + "@rollup/rollup-linux-arm-musleabihf": "4.21.2", + "@rollup/rollup-linux-arm64-gnu": "4.21.2", + "@rollup/rollup-linux-arm64-musl": "4.21.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.21.2", + "@rollup/rollup-linux-riscv64-gnu": "4.21.2", + "@rollup/rollup-linux-s390x-gnu": "4.21.2", + "@rollup/rollup-linux-x64-gnu": "4.21.2", + "@rollup/rollup-linux-x64-musl": "4.21.2", + "@rollup/rollup-win32-arm64-msvc": "4.21.2", + "@rollup/rollup-win32-ia32-msvc": "4.21.2", + "@rollup/rollup-win32-x64-msvc": "4.21.2", "fsevents": "~2.3.2" } }, @@ -10911,9 +11001,10 @@ } }, "node_modules/tinyqueue": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", - "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", + "license": "ISC" }, "node_modules/tinyrainbow": { "version": "1.2.0", @@ -11400,15 +11491,15 @@ } }, "node_modules/vite": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.0.tgz", - "integrity": "sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz", + "integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.40", - "rollup": "^4.13.0" + "postcss": "^8.4.41", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" diff --git a/client/package.json b/client/package.json index 74b4ae8f0a2..5b26215fe5f 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.5.1", + "maplibre-gl": "4.6.0", "react": "18.3.1", "react-bootstrap": "2.10.4", "react-dom": "18.3.1", @@ -32,8 +32,8 @@ "@graphql-codegen/client-preset": "4.3.3", "@graphql-codegen/introspection": "4.0.3", "@parcel/watcher": "2.4.1", - "@testing-library/react": "16.0.0", - "@types/react": "18.3.3", + "@testing-library/react": "16.0.1", + "@types/react": "18.3.5", "@types/react-dom": "18.3.0", "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", @@ -41,15 +41,15 @@ "@vitest/coverage-v8": "2.0.5", "eslint": "8.57.0", "eslint-config-prettier": "9.1.0", - "eslint-plugin-import": "2.29.1", + "eslint-plugin-import": "2.30.0", "eslint-plugin-jsx-a11y": "6.9.0", - "eslint-plugin-react": "7.35.0", + "eslint-plugin-react": "7.35.1", "eslint-plugin-react-hooks": "4.6.2", - "eslint-plugin-react-refresh": "0.4.9", - "jsdom": "24.1.1", + "eslint-plugin-react-refresh": "0.4.11", + "jsdom": "25.0.0", "prettier": "3.3.3", "typescript": "5.5.4", - "vite": "5.4.0", + "vite": "5.4.2", "vitest": "2.0.5" } } diff --git a/client/src/components/ItineraryList/ItineraryLegDetails.tsx b/client/src/components/ItineraryList/ItineraryLegDetails.tsx index 56fdf430388..e75813a4a45 100644 --- a/client/src/components/ItineraryList/ItineraryLegDetails.tsx +++ b/client/src/components/ItineraryList/ItineraryLegDetails.tsx @@ -10,14 +10,9 @@ export function ItineraryLegDetails({ leg, isLast }: { leg: Leg; isLast: boolean
{formatDistance(leg.distance)}, {formatDuration(leg.duration)}
-
- - - -
+ + -{' '} +
{leg.mode}{' '} {leg.line && ( @@ -28,9 +23,9 @@ export function ItineraryLegDetails({ leg, isLast }: { leg: Leg; isLast: boolean , {leg.authority?.name} )}{' '} -
{leg.mode !== Mode.Foot && ( <> +
{leg.fromPlace.name} →{' '} )}{' '} diff --git a/client/src/style.css b/client/src/style.css index 7dd2565c449..1a24ac2c072 100644 --- a/client/src/style.css +++ b/client/src/style.css @@ -86,7 +86,7 @@ } .itinerary-leg-details .mode { - margin-top: 10px; + margin-top: 2px; } .itinerary-header-itinerary-number { diff --git a/docs/Codestyle.md b/doc/dev/decisionrecords/Codestyle.md similarity index 99% rename from docs/Codestyle.md rename to doc/dev/decisionrecords/Codestyle.md index d468937adfa..f9ffc1a9056 100644 --- a/docs/Codestyle.md +++ b/doc/dev/decisionrecords/Codestyle.md @@ -58,7 +58,7 @@ Editor > Code Style_. Then select **Project** in the \_Scheme drop down. You can run the Prettier Maven plugin as an external tool in IntelliJ. Set it up as an `External tool` and assign a key-shortcut to the tool execution. -![External Tool Dialog](images/ExternalToolDialog.png) +![External Tool Dialog](../images/ExternalToolDialog.png) ``` Name: Prettier Format Current File diff --git a/CODE_CONVENTIONS.md b/doc/dev/decisionrecords/NamingConventions.md similarity index 56% rename from CODE_CONVENTIONS.md rename to doc/dev/decisionrecords/NamingConventions.md index 49b92fbe2f8..ed6175f4f4c 100644 --- a/CODE_CONVENTIONS.md +++ b/doc/dev/decisionrecords/NamingConventions.md @@ -1,34 +1,4 @@ -# Code Conventions - -We try to follow these conventions or best practices. The goal is to get cleaner code and make the -review process easier: - -- the developer knows what to expect -- the reviewer knows what to look for -- discussions and personal preferences can be avoided saving time -- new topics should be documented here - -These conventions are not "hard" rules, and often there might be other forces which pull a -decision in another direction, in that case documenting your choice is often enough to pass the -review. - -## Best practices - in focus - -- [ ] Document `public` interfaces, classes and methods - especially those part of a module api. -- [ ] Leave Things BETTER than you found them - clean up code you visit or/and add unit tests. -- [ ] [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) - Do not repeat yourself. Avoid implementing the same business rule in two places -> refactor. -- [ ] [Feature envy](https://refactoring.guru/smells/feature-envy) -- [ ] Make types immutable if possible. References to other Entities might need to be mutable, if - so try to init them once, and throw an exception if set again. - Example: - -```java -Builder initStop(Stop stop) { - this.stop = requireNotInitialized(this.stop, stop); -} -``` - -## Naming Conventions +# Naming Conventions In general, we use American English. We use the GTFS terminology inside OTP as the transit domain specific language. In cases where GTFS does not provide an alternative we use NeTEx. The naming @@ -36,7 +6,7 @@ should follow the Java standard naming conventions. For example a "real-time upd is named `RealTimeUpdater`. If in doubt check the Oxford Dictionary(American). -### Packages +## Packages Try to arrange code by domain functionality, not technology. The main structure of a package should be `org.opentripplanner...`. @@ -55,14 +25,14 @@ be `org.opentripplanner...`. | `util` | General "util" functionality, often characterized by `static` methods. Dependencies to other OTP packages is NOT allowed, only 3rd party utils libs. | | `o.o.apis` | OTP external endpoints. Note! Many apis are in the Sandbox where they are in the `o.o.ext` package. | -> **Note!** The above is the goal, the current package structure needs cleanup. +> **Note!** The above is the goal, the current package structure needs cleanup. > **Note!** Util methods depending on an OTP type/component should go into that type/component, not in the -utils class. E.g. static factory methods. Warning the "pure" utilities right now are placed into +utils class. E.g. static factory methods. Warning the "pure" utilities right now are placed into sub-packages of `o.o.util`, the root package needs cleanup. -### Methods +## Methods Here are a list of common prefixes used, and what to expect. @@ -88,15 +58,15 @@ These prefixes are also "allowed", but not preferred - they have some kind of ne | `setStop(Stop stop)` | Set a mutable stop reference. Avoid if not part of natural lifecycle. Use `initStop(...)` if possible | | `getStop() : Stop` | Old style accessor, use the shorter form `stop() : Stop` | -### Service, Model and Repository +## Service, Model and Repository -![MainModelOverview](docs/images/ServiceModelOverview.png) +![MainModelOverview](../images/ServiceModelOverview.png) Naming convention for builders with and without a context. -##### Graph Build and tests run without a context +#### Graph Build and tests run without a context ```Java // Create a new Stop @@ -105,112 +75,3 @@ trip = Trip.of(id).withName("The Express").build(); // Modify and existing stop stop = stop.copyOf().withPrivateCode("TEX").build(); ``` - - -## Records, POJOs and Builders - -We prefer immutable typesafe types over flexibility and "short" class definitions. This make -the code more robust and less error-prune. - -### Records - -You may use records, but avoid using records if you can not encapsulate it properly. Be especially -aware of arrays fields (can not be protected) and collections (remember to make a defensive copy). -If you need to override `equals` and `hashCode`, then it is probably not worth it. -Be aware that `equals` compare references, not the value of a field. Consider overriding `toString`. - -### Builders - -OTP used a simple builder pattern in many places, especially when creating immutable types. - -#### Builder conventions -- Use factory methods to create builder, either `of()` or `copyOf()`. The _copyOf_ uses an existing - instance as its base. The `of()` creates a builder with all default values set. All constructors - should be private (or package local) to enforce the use of the factory methods. -- If the class have more than 5 fields avoid using an inner class builder, instead create a builder - in the same package. -- Make all fields in the main class final to enforce immutability. -- Consider using utility methods for parameter checking, like `Objects#requireNonNull` and - `ObjectUtils.ifNotNull`. -- Validate all fields in the main type constructor(i.e. not in the builder), especially null checks. - Prefer default values over null-checks. All business logic using the type can rely on its validity. -- You may keep the original instance in the builder to avoid creating a new object if nothing - changed. This prevents polluting the heap for long-lived objects and make comparison very fast. -- There is no need to provide all get accessors in the Builder if not needed. -- Unit-test builders and verify all fields are copied over. -- For nested builders see the field `nested` in the example. - -
- Builder example - -```Java -/** - * THIS CLASS IS IMMUTABLE AND THREAD-SAFE - */ -public class A { - public static final A DEFAULT = new A(); - private final List names; - private final int age; - private final B nested; - - private A() { - this.names = List.of("default"); - this.age = 7; - this.nested = B.of(); - } - - private A(Builder builder) { - this.names = List.copyOf(builder.names); - this.age = builder.age; - this.nested = builder.nested(); - - if(age < 0 || age > 150) { - throw new IllegalArgumentException("Age is out of range[0..150]: " + age); - } - } - - public static A.Builder of() { return DEFAULT.copyOf(); } - public A.Builder copyOf() { return new Builder(this); } - - public List listNames() { return names; } - public int age() { return age; } - - public boolean equals(Object other) { ... } - public int hashCode() { ... } - public String toString() { return ToStringBuilder.of(A.class)...; } - - public static class Builder { - private final A original; - private final List names; - private int age; - private B.Builder nested = null; - - public Builder(A original) { - this.original = original; - this.names = new ArrayList<>(original.names); - this.age = original.age; - } - - public Builder withName(String name) { this.names.add(name); return this; } - - public int age() { return age; } - public Builder withAge(int age) { this.age = age; return this; } - - private B nested() { return nested==null ? original.nested() : nested.build(); } - public Builder withB(Consumer body) { - if(nested == null) { nested = original.nested.copyOf(); } - body.accept(nested); - return this; - } - public A build() { - A value = new A(this); - return original.equals(value) ? original : value; - } - } -} -``` - -
- - - diff --git a/doc/dev/decisionrecords/RecordsPOJOsBuilders.md b/doc/dev/decisionrecords/RecordsPOJOsBuilders.md new file mode 100644 index 00000000000..e0752fc70b0 --- /dev/null +++ b/doc/dev/decisionrecords/RecordsPOJOsBuilders.md @@ -0,0 +1,110 @@ +## Records, POJOs and Builders + +We prefer immutable typesafe types over flexibility and "short" class definitions. This makes +the code more robust and less error-prone. References to other entities might need to be mutable, +if so try to init them once, and throw an exception if set again. Example: + +```java +Builder initStop(Stop stop) { + this.stop = requireNotInitialized(this.stop, stop); +} +``` + + +### Records + +You may use records, but avoid using records if you cannot encapsulate them properly. Generally, records are considered appropriate and useful for throw-away compound types private to an implementation, such as hash table keys or compound values in a temporary list or set. On the other hand, records are generally not appropriate in the domain model where we insist on full encapsulation, which records cannot readily provide. Be especially +aware of array fields (which can not be protected) and collections (remember to make a defensive copy). Consider overriding `toString`. But if you need to override `equals` and `hashCode`, then it is probably not worth using a record. The implicit `equals()` and `hashCode()` implementations for records behave as if they call `equals` and `hashCode` on each field of the record, so their behavior will depend heavily on the types of these fields. + +### Builders + +OTP used a simple builder pattern in many places, especially when creating immutable types. + +#### Builder conventions +- Use factory methods to create builder, either `of()` or `copyOf()`. The _copyOf_ uses an existing + instance as its base. The `of()` creates a builder with all default values set. All constructors + should be private (or package local) to enforce the use of the factory methods. +- If the class has more than 5 fields, then avoid using an inner class builder, instead create a + builder in the same package. +- Make all fields in the main class final to enforce immutability. +- Consider using utility methods for parameter checking, like `Objects#requireNonNull` and + `ObjectUtils.ifNotNull`. +- Validate all fields in the main type constructor(i.e. not in the builder), especially null checks. + Prefer default values over null-checks. All business logic using the type can rely on its validity. +- You may keep the original instance in the builder to avoid creating a new object if nothing + changed. This prevents polluting the heap for long-lived objects and make comparison very fast. +- There is no need to provide all get accessors in the Builder if not needed. +- Unit-test builders and verify all fields are copied over. +- For nested builders see the field `nested` in the example. + +
+ Builder example + +```Java +/** + * THIS CLASS IS IMMUTABLE AND THREAD-SAFE + */ +public class A { + public static final A DEFAULT = new A(); + private final List names; + private final int age; + private final B nested; + + private A() { + this.names = List.of("default"); + this.age = 7; + this.nested = B.of(); + } + + private A(Builder builder) { + this.names = List.copyOf(builder.names); + this.age = builder.age; + this.nested = builder.nested(); + + if(age < 0 || age > 150) { + throw new IllegalArgumentException("Age is out of range[0..150]: " + age); + } + } + + public static A.Builder of() { return DEFAULT.copyOf(); } + public A.Builder copyOf() { return new Builder(this); } + + public List listNames() { return names; } + public int age() { return age; } + + public boolean equals(Object other) { ... } + public int hashCode() { ... } + public String toString() { return ToStringBuilder.of(A.class)...; } + + public static class Builder { + private final A original; + private final List names; + private int age; + private B.Builder nested = null; + + public Builder(A original) { + this.original = original; + this.names = new ArrayList<>(original.names); + this.age = original.age; + } + + public Builder withName(String name) { this.names.add(name); return this; } + + public int age() { return age; } + public Builder withAge(int age) { this.age = age; return this; } + + private B nested() { return nested==null ? original.nested() : nested.build(); } + public Builder withB(Consumer body) { + if(nested == null) { nested = original.nested.copyOf(); } + body.accept(nested); + return this; + } + public A build() { + A value = new A(this); + return original.equals(value) ? original : value; + } + } +} +``` + +
diff --git a/doc/dev/decisionrecords/UseDecisionRecords.md b/doc/dev/decisionrecords/UseDecisionRecords.md new file mode 100644 index 00000000000..a3520ea5133 --- /dev/null +++ b/doc/dev/decisionrecords/UseDecisionRecords.md @@ -0,0 +1,37 @@ +# Decision Records + +An OTP Decision Record is a justified software design choice that addresses a significant +functional or non-functional requirement. [Architectural Decision Records](https://adr.github.io/) is a similar +concept, but we have widened the scope to include any relevant decision about OTP development. + + +## Process + +Decisions we make in the developer meetings are recorded in the [Developer Decision Records](/DEVELOPMENT_DECISION_RECORDS.md) +list. If the decision is small and uncontroversial, but yet important and can be expressed in +maximum 2 sentences, we will list it here without any more documentation. If the decision requires +a bit more discussion and explanations, then we will create a PR with a document for it. + +Use the **[template](/doc/dev/decisionrecords/_TEMPLATE.md) as a starting point for documenting +the decision. + + +### How to discuss and document a Decision Record + +- Create a new pull-request and describe the decision record by adding a document to the + `/doc/dev/decisionrecords` folder. Use the [template](/doc/dev/decisionrecords/_TEMPLATE.md). + template. +- Present the decision record in a developer meeting. Make sure to update the main description + based on the feedback/discussion and decisions in the developer meeting. +- The final approval is done in the developer meeting, at least 3 developers representing 3 + different organisations should approve it. No vote against the proposal. If the developers + are not able to agree, the PLC can decide. +- References to Development Decision Records in reviews can be done by linking or just typing. + For example `Use-Dependency-Injection` or [Use-Dependency-Injection](../../../DEVELOPMENT_DECISION_RECORDS.md#use-dependency-injection) + +### Checklist +- [ ] Give it a meaningful title that quickly lets the reader understand what it is all about. +- [ ] Get it approved in a developer meeting with 3 votes in favor (3 organisations). +- [ ] Add the name and description to the list in the [Development Decision Records](../../../DEVELOPMENT_DECISION_RECORDS.md) list. + Maximum two sentences should be used. Try to keep it as short as possible. +- [ ] Remember to link to the PR. diff --git a/doc/dev/decisionrecords/_TEMPLATE.md b/doc/dev/decisionrecords/_TEMPLATE.md new file mode 100644 index 00000000000..45bb3b11d34 --- /dev/null +++ b/doc/dev/decisionrecords/_TEMPLATE.md @@ -0,0 +1,33 @@ +# {TITLE} + +{DESCRIPTION} + + +Original pull-request: {#NNNN} + + +### Context and Problem Statement + + + +### Other options + + - + +### Decision & Consequences + + + +#### Positive Consequences + + - + +#### Negative Consequences + + - diff --git a/docs/images/ExternalToolDialog.png b/doc/dev/images/ExternalToolDialog.png similarity index 100% rename from docs/images/ExternalToolDialog.png rename to doc/dev/images/ExternalToolDialog.png diff --git a/docs/images/ServiceModelOverview.png b/doc/dev/images/ServiceModelOverview.png similarity index 100% rename from docs/images/ServiceModelOverview.png rename to doc/dev/images/ServiceModelOverview.png diff --git a/doc-templates/BuildConfiguration.md b/doc/templates/BuildConfiguration.md similarity index 99% rename from doc-templates/BuildConfiguration.md rename to doc/templates/BuildConfiguration.md index 43a29e1fb79..7e768e30eb1 100644 --- a/doc-templates/BuildConfiguration.md +++ b/doc/templates/BuildConfiguration.md @@ -1,7 +1,7 @@ diff --git a/doc-templates/Configuration.md b/doc/templates/Configuration.md similarity index 99% rename from doc-templates/Configuration.md rename to doc/templates/Configuration.md index e06adc46dc5..0ef96b8d4fa 100644 --- a/doc-templates/Configuration.md +++ b/doc/templates/Configuration.md @@ -1,7 +1,7 @@ diff --git a/doc-templates/Emissions.md b/doc/templates/Emissions.md similarity index 100% rename from doc-templates/Emissions.md rename to doc/templates/Emissions.md diff --git a/doc-templates/Flex.md b/doc/templates/Flex.md similarity index 100% rename from doc-templates/Flex.md rename to doc/templates/Flex.md diff --git a/doc-templates/GraphQL-Tutorial.md b/doc/templates/GraphQL-Tutorial.md similarity index 97% rename from doc-templates/GraphQL-Tutorial.md rename to doc/templates/GraphQL-Tutorial.md index 51f0ef7880a..11a2e304119 100644 --- a/doc-templates/GraphQL-Tutorial.md +++ b/doc/templates/GraphQL-Tutorial.md @@ -1,7 +1,7 @@ diff --git a/doc-templates/OsmMapper.md b/doc/templates/OsmMapper.md similarity index 100% rename from doc-templates/OsmMapper.md rename to doc/templates/OsmMapper.md diff --git a/doc-templates/RideHailing.md b/doc/templates/RideHailing.md similarity index 100% rename from doc-templates/RideHailing.md rename to doc/templates/RideHailing.md diff --git a/doc-templates/RouteRequest.md b/doc/templates/RouteRequest.md similarity index 92% rename from doc-templates/RouteRequest.md rename to doc/templates/RouteRequest.md index 4abfdec71e2..a452e1d1480 100644 --- a/doc-templates/RouteRequest.md +++ b/doc/templates/RouteRequest.md @@ -1,7 +1,7 @@ diff --git a/doc-templates/RouterConfiguration.md b/doc/templates/RouterConfiguration.md similarity index 96% rename from doc-templates/RouterConfiguration.md rename to doc/templates/RouterConfiguration.md index f181bf5db93..b6c6ccf9c4b 100644 --- a/doc-templates/RouterConfiguration.md +++ b/doc/templates/RouterConfiguration.md @@ -1,7 +1,7 @@ diff --git a/doc-templates/RoutingModes.md b/doc/templates/RoutingModes.md similarity index 100% rename from doc-templates/RoutingModes.md rename to doc/templates/RoutingModes.md diff --git a/doc-templates/StopConsolidation.md b/doc/templates/StopConsolidation.md similarity index 97% rename from doc-templates/StopConsolidation.md rename to doc/templates/StopConsolidation.md index c92cab6afe1..6817ee47d4c 100644 --- a/doc-templates/StopConsolidation.md +++ b/doc/templates/StopConsolidation.md @@ -1,7 +1,7 @@ # Stop consolidation diff --git a/doc-templates/UpdaterConfig.md b/doc/templates/UpdaterConfig.md similarity index 98% rename from doc-templates/UpdaterConfig.md rename to doc/templates/UpdaterConfig.md index f16d28f570c..440cd96f733 100644 --- a/doc-templates/UpdaterConfig.md +++ b/doc/templates/UpdaterConfig.md @@ -1,7 +1,7 @@ diff --git a/doc-templates/VehicleParking.md b/doc/templates/VehicleParking.md similarity index 100% rename from doc-templates/VehicleParking.md rename to doc/templates/VehicleParking.md diff --git a/doc-templates/sandbox/MapboxVectorTilesApi.md b/doc/templates/sandbox/MapboxVectorTilesApi.md similarity index 100% rename from doc-templates/sandbox/MapboxVectorTilesApi.md rename to doc/templates/sandbox/MapboxVectorTilesApi.md diff --git a/doc-templates/sandbox/VehicleRentalServiceDirectory.md b/doc/templates/sandbox/VehicleRentalServiceDirectory.md similarity index 100% rename from doc-templates/sandbox/VehicleRentalServiceDirectory.md rename to doc/templates/sandbox/VehicleRentalServiceDirectory.md diff --git a/doc-templates/sandbox/siri/SiriAzureUpdater.md b/doc/templates/sandbox/siri/SiriAzureUpdater.md similarity index 100% rename from doc-templates/sandbox/siri/SiriAzureUpdater.md rename to doc/templates/sandbox/siri/SiriAzureUpdater.md diff --git a/doc-templates/sandbox/siri/SiriGooglePubSubUpdater.md b/doc/templates/sandbox/siri/SiriGooglePubSubUpdater.md similarity index 100% rename from doc-templates/sandbox/siri/SiriGooglePubSubUpdater.md rename to doc/templates/sandbox/siri/SiriGooglePubSubUpdater.md diff --git a/doc-templates/sandbox/siri/SiriUpdater.md b/doc/templates/sandbox/siri/SiriUpdater.md similarity index 100% rename from doc-templates/sandbox/siri/SiriUpdater.md rename to doc/templates/sandbox/siri/SiriUpdater.md diff --git a/docs/Accessibility.md b/doc/user/Accessibility.md similarity index 99% rename from docs/Accessibility.md rename to doc/user/Accessibility.md index 49373c522c2..27cb47ce92e 100644 --- a/docs/Accessibility.md +++ b/doc/user/Accessibility.md @@ -109,4 +109,4 @@ inaccessible to wheelchair users. ## Example A full configuration example is available -at [`/docs/examples`](https://github.com/opentripplanner/OpenTripPlanner/tree/dev-2.x/docs/examples/ibi) \ No newline at end of file +at [`/docs/examples`](https://github.com/opentripplanner/OpenTripPlanner/tree/dev-2.x/doc/user/examples/ibi) \ No newline at end of file diff --git a/docs/Analysis.md b/doc/user/Analysis.md similarity index 100% rename from docs/Analysis.md rename to doc/user/Analysis.md diff --git a/docs/Basic-Tutorial.md b/doc/user/Basic-Tutorial.md similarity index 98% rename from docs/Basic-Tutorial.md rename to doc/user/Basic-Tutorial.md index a7a69b8a3bb..d6e46f3f1f5 100644 --- a/docs/Basic-Tutorial.md +++ b/doc/user/Basic-Tutorial.md @@ -1,207 +1,207 @@ -# OpenTripPlanner Basic Tutorial - -This page should allow you to set up and test your own OTP2 server. If all goes well it should only -take a few minutes! - -## Get Java - -As a Java program, OTP must be run within a Java virtual machine (JVM), which is provided as part of -the Java runtime (JRE) or Java development kit (JDK). OTP2 is compatible with Java 21 or later. We -recommend running on Java 21 rather than a later version, as it is a long-term support release. -Run `java -version` to check that you have version 21 or newer of the JVM installed. If you do not, -you will need to install a recent OpenJDK or Oracle Java package for your operating system. - -## Get OTP - -OpenTripPlanner is written in Java and distributed as a single runnable JAR file. This is a "shaded" -JAR containing all other libraries needed for OTP to work, and is available from the Maven Central -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/), -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) -. - -You may also want to get your own copy of the OTP source code -and [build a bleeding edge development JAR from scratch](Getting-OTP.md), especially if you plan to -do some development yourself. In that case, check out the branch `dev-2.x`. - -## Get some data - -### GTFS for Transit Schedules and Stops - -First you'll need GTFS data to build a transit network. There's an excellent description of the GTFS -format [here](http://gtfs.org/). Transport agencies throughout the world provide GTFS schedules to -the public. Transitland has a -[registry of feeds](https://transit.land/feed-registry) and [TransitFeeds](http://transitfeeds.com/) -also provides an extensive catalog. The best option is often to simply fetch the data directly from -a transit operator or agency. If you know of a feed you want to work with, download it and put it in -an empty directory you have created for your OTP instance such as `/home/username/otp` on -Linux, `/Users/username/otp` on MacOS, or `C:\Users\username\otp` on Windows. For OTP2 to detect a -GTFS file, **its name must end in `.zip` and must contain the letters 'gtfs'**. We often use the -convention of saving GTFS files with names ending in `.gtfs.zip` which meets both these criteria, -reflecting the fact that a GTFS feed is just a ZIP file containing a specific set of files. If you -don't have a particular feed in mind, the one for Portland, Oregon's TriMet agency is a good option. -It is available at [this URL](http://developer.trimet.org/schedule/gtfs.zip). This is a -moderate-sized input of good quality (TriMet initiated OTP development and helped develop the GTFS -format). On Linux, this could be done on the command line as follows: - - $ cd /home/username - $ mkdir otp - $ cd otp - $ wget "http://developer.trimet.org/schedule/gtfs.zip" -O trimet.gtfs.zip - -### OSM for Streets - -You'll also need OpenStreetMap data to build a road network for walking, cycling, and -driving. [OpenStreetMap](https://www.openstreetmap.org/) is a global collaborative map database that -rivals or surpasses the quality of commercial maps in many locations. Several services extract -smaller geographic regions from this database. Interline Technologies maintains a collection -of [extracts updated daily for urban areas around the world](https://www.interline.io/osm/extracts/) -. [Geofabrik](http://download.geofabrik.de/) provides extracts for larger areas like countries or -states, from which you can prepare your own smaller bounding-box extracts -using [Osmosis](http://wiki.openstreetmap.org/wiki/Osmosis#Extracting_bounding_boxes) -, [osmconvert](http://wiki.openstreetmap.org/wiki/Osmconvert#Applying_Geographical_Borders), or (our -favorite) [Osmium-Tool](https://osmcode.org/osmium-tool/manual.html#creating-geographic-extracts). -There is also [Protomaps](https://app.protomaps.com/) which can create custom extracts -for any region of the world with an easy to use drag and drop interface. -OSM data can be delivered as XML or in the more compact binary PBF format. OpenTripPlanner consumes -only PBF because it's smaller and more efficient. - -Download OSM PBF data for the same geographic region as your GTFS feed, and place this PBF file in -the same directory you created for the OSM data. If you are using the TriMet GTFS feed, you could -download -the [Geofabrik extract for the US state of Oregon](http://download.geofabrik.de/north-america/us/oregon.html) -, then further trim that to just -the [TriMet service area](https://trimet.org/pdfs/taxinfo/trimetdistrictboundary.pdf) using the -bounding box switch of one of the above tools. On Linux or MacOS you could do that as follows: - - $ cd /home/username - $ wget http://download.geofabrik.de/north-america/us/oregon-latest.osm.pbf - $ osmconvert oregon-latest.osm.pbf -b=-123.043,45.246,-122.276,45.652 --complete-ways -o=portland.pbf - $ mv portland.pbf otp - -We find [this tool](https://boundingbox.klokantech.com/) useful for determining the geographic -coordinates of bounding boxes. The CSV option in that tool produces exactly the format expected by -the `osmconvert -b` switch. The `--complete-ways` switch is important to handle roads that cross -outside your bounding box. - -If you have extracted a smaller PBF file from a larger region, be sure to put only your extract (not -the original larger file) in the directory with your GTFS data. Otherwise OTP will try to load both -the original file and the extract in a later step. See -the [page on preparing OSM data](Preparing-OSM.md) for additional information and example commands -for cropping and filtering OSM data. - -## Starting OTP - -A typical command to start OTP looks like `java -Xmx2G -jar otp.shaded.jar `. The -`-Xmx` parameter sets the limit on how much memory OTP is allowed to consume. GTFS and OSM data sets -are often very large, and OTP is relatively memory-hungry. You will need at least 1GB of memory when -working with the Portland TriMet data set, and several gigabytes for larger inputs. -[Here is more information about the system requirements](System-Requirements.md). If you have -sufficient memory in your computer, set this to a couple of gigabytes (e.g. `-Xmx2G`). Java uses -a [garbage collection](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)) approach -to memory management, which requires some "breathing room" to efficiently operate. Without -sufficient free memory OTP can grind to a halt. [VisualVM](https://visualvm.github.io) is a good way -to inspect Java memory usage, especially with -the [VisualGC plugin](https://visualvm.github.io/plugins.html). - -## Building Graphs - -There are two main phases to preparing and deploying an OTP server. The first is to analyze the -GTFS, OSM and any other inputs (such as elevation data) and build a representation of the -transportation network. Following mathematical terminology we call this -a ['graph'](http://en.wikipedia.org/wiki/Graph_%28mathematics%29), and refer to this phase as "graph -building". The second phase is to start a server that provides trip planning and other API services -for this graph. - -It is possible to save the graph to a file on disk after the first phase, then load the graph from -the file in the second phase. This allows restarting the server or starting multiple instances of -the server without repeating the often time-consuming process of building the graph. It is also -possible to split the graph building process into separate OSM and GTFS stages for similar reasons: -to allow reusing results from slow processes, such as applying elevation data to streets. These -different options are controlled with command line switches, and will be described in more detail -below and in other tutorials. - -## Simple One-step Server - -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 - -where `/home/username/otp` should be the directory where you put your configuration and input files. - -If you're using the Portland input data, the graph build operation should take about one minute to -complete, and then you'll see a `Grizzly server running` message. At this point you have an -OpenTripPlanner server running locally and can open [http://localhost:8080/](http://localhost:8080/) -in a web browser. You should be presented with a Javascript client application that will interact -with your local OpenTripPlanner instance. - -This map-based user interface is in fact sending HTTP GET requests to the OTP server running on your -local machine. It can be informative to watch the HTTP requests and responses being generated using -the developer tools in your web browser. OTP's built-in web server will run by default on port 8080. -If by any chance some other software is already using that port number, you can specify a different -port number with a switch -`--port 8801`. - - -## Saving a Graph - -If you want speed up the process of repeatedly starting up a server with the same graph, you can -build a graph from street and transit data then save it to a file using the `--build` and `--save` -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 . - -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 . - -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 -server. Accordingly, you may want to perform the build on one machine (e.g. a throw-away cloud -instance with more memory or compute capacity), then copy the resulting graph file to one or more -smaller machines to serve the API. - -## Layering GTFS onto OSM - -Building the street graph (especially with elevation data) can take a long time. It is common for -transit data to change more frequently than street data, so it can be convenient to build the street -graph once, and then layer transit data on top of the streets to make the final graph. - -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 . - -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 . - -Finally, the server can be started using the `--load` parameter: - - $ java -Xmx2G -jar otp-2.5.0-shaded.jar --load . - -## Command Line Switches - -The flow diagram below summarizes all the command line switches used in the above examples, and how -they control which actions are taken when OTP starts up. - -![Command-Line-Parameter-Flow](images/cli-flow.svg) - -You must use at least one of the required parameters: `--load`, `--loadStreet`, `--build` -, `--buildStreet`. A _required_ parameter may imply other parameters when the flow allows for no -other choice. For example, `--load` implies `--serve`, so `--serve` is not necessary and has no -additional effect when used together with `--load`. - -You can run the OTP .jar file with the `--help` option for a full list of command line parameters. - -## Exploring the API - -If you want to learn how to use OTP's API's, check out the [GraphQL tutorial](apis/GraphQL-Tutorial.md). +# OpenTripPlanner Basic Tutorial + +This page should allow you to set up and test your own OTP2 server. If all goes well it should only +take a few minutes! + +## Get Java + +As a Java program, OTP must be run within a Java virtual machine (JVM), which is provided as part of +the Java runtime (JRE) or Java development kit (JDK). OTP2 is compatible with Java 21 or later. We +recommend running on Java 21 rather than a later version, as it is a long-term support release. +Run `java -version` to check that you have version 21 or newer of the JVM installed. If you do not, +you will need to install a recent OpenJDK or Oracle Java package for your operating system. + +## Get OTP + +OpenTripPlanner is written in Java and distributed as a single runnable JAR file. This is a "shaded" +JAR containing all other libraries needed for OTP to work, and is available from the Maven Central +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/), +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) +. + +You may also want to get your own copy of the OTP source code +and [build a bleeding edge development JAR from scratch](Getting-OTP.md), especially if you plan to +do some development yourself. In that case, check out the branch `dev-2.x`. + +## Get some data + +### GTFS for Transit Schedules and Stops + +First you'll need GTFS data to build a transit network. There's an excellent description of the GTFS +format [here](http://gtfs.org/). Transport agencies throughout the world provide GTFS schedules to +the public. Transitland has a +[registry of feeds](https://transit.land/feed-registry) and [TransitFeeds](http://transitfeeds.com/) +also provides an extensive catalog. The best option is often to simply fetch the data directly from +a transit operator or agency. If you know of a feed you want to work with, download it and put it in +an empty directory you have created for your OTP instance such as `/home/username/otp` on +Linux, `/Users/username/otp` on MacOS, or `C:\Users\username\otp` on Windows. For OTP2 to detect a +GTFS file, **its name must end in `.zip` and must contain the letters 'gtfs'**. We often use the +convention of saving GTFS files with names ending in `.gtfs.zip` which meets both these criteria, +reflecting the fact that a GTFS feed is just a ZIP file containing a specific set of files. If you +don't have a particular feed in mind, the one for Portland, Oregon's TriMet agency is a good option. +It is available at [this URL](http://developer.trimet.org/schedule/gtfs.zip). This is a +moderate-sized input of good quality (TriMet initiated OTP development and helped develop the GTFS +format). On Linux, this could be done on the command line as follows: + + $ cd /home/username + $ mkdir otp + $ cd otp + $ wget "http://developer.trimet.org/schedule/gtfs.zip" -O trimet.gtfs.zip + +### OSM for Streets + +You'll also need OpenStreetMap data to build a road network for walking, cycling, and +driving. [OpenStreetMap](https://www.openstreetmap.org/) is a global collaborative map database that +rivals or surpasses the quality of commercial maps in many locations. Several services extract +smaller geographic regions from this database. Interline Technologies maintains a collection +of [extracts updated daily for urban areas around the world](https://www.interline.io/osm/extracts/) +. [Geofabrik](http://download.geofabrik.de/) provides extracts for larger areas like countries or +states, from which you can prepare your own smaller bounding-box extracts +using [Osmosis](http://wiki.openstreetmap.org/wiki/Osmosis#Extracting_bounding_boxes) +, [osmconvert](http://wiki.openstreetmap.org/wiki/Osmconvert#Applying_Geographical_Borders), or (our +favorite) [Osmium-Tool](https://osmcode.org/osmium-tool/manual.html#creating-geographic-extracts). +There is also [Protomaps](https://app.protomaps.com/) which can create custom extracts +for any region of the world with an easy to use drag and drop interface. +OSM data can be delivered as XML or in the more compact binary PBF format. OpenTripPlanner consumes +only PBF because it's smaller and more efficient. + +Download OSM PBF data for the same geographic region as your GTFS feed, and place this PBF file in +the same directory you created for the OSM data. If you are using the TriMet GTFS feed, you could +download +the [Geofabrik extract for the US state of Oregon](http://download.geofabrik.de/north-america/us/oregon.html) +, then further trim that to just +the [TriMet service area](https://trimet.org/pdfs/taxinfo/trimetdistrictboundary.pdf) using the +bounding box switch of one of the above tools. On Linux or MacOS you could do that as follows: + + $ cd /home/username + $ wget http://download.geofabrik.de/north-america/us/oregon-latest.osm.pbf + $ osmconvert oregon-latest.osm.pbf -b=-123.043,45.246,-122.276,45.652 --complete-ways -o=portland.pbf + $ mv portland.pbf otp + +We find [this tool](https://boundingbox.klokantech.com/) useful for determining the geographic +coordinates of bounding boxes. The CSV option in that tool produces exactly the format expected by +the `osmconvert -b` switch. The `--complete-ways` switch is important to handle roads that cross +outside your bounding box. + +If you have extracted a smaller PBF file from a larger region, be sure to put only your extract (not +the original larger file) in the directory with your GTFS data. Otherwise OTP will try to load both +the original file and the extract in a later step. See +the [page on preparing OSM data](Preparing-OSM.md) for additional information and example commands +for cropping and filtering OSM data. + +## Starting OTP + +A typical command to start OTP looks like `java -Xmx2G -jar otp.shaded.jar `. The +`-Xmx` parameter sets the limit on how much memory OTP is allowed to consume. GTFS and OSM data sets +are often very large, and OTP is relatively memory-hungry. You will need at least 1GB of memory when +working with the Portland TriMet data set, and several gigabytes for larger inputs. +[Here is more information about the system requirements](System-Requirements.md). If you have +sufficient memory in your computer, set this to a couple of gigabytes (e.g. `-Xmx2G`). Java uses +a [garbage collection](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)) approach +to memory management, which requires some "breathing room" to efficiently operate. Without +sufficient free memory OTP can grind to a halt. [VisualVM](https://visualvm.github.io) is a good way +to inspect Java memory usage, especially with +the [VisualGC plugin](https://visualvm.github.io/plugins.html). + +## Building Graphs + +There are two main phases to preparing and deploying an OTP server. The first is to analyze the +GTFS, OSM and any other inputs (such as elevation data) and build a representation of the +transportation network. Following mathematical terminology we call this +a ['graph'](http://en.wikipedia.org/wiki/Graph_%28mathematics%29), and refer to this phase as "graph +building". The second phase is to start a server that provides trip planning and other API services +for this graph. + +It is possible to save the graph to a file on disk after the first phase, then load the graph from +the file in the second phase. This allows restarting the server or starting multiple instances of +the server without repeating the often time-consuming process of building the graph. It is also +possible to split the graph building process into separate OSM and GTFS stages for similar reasons: +to allow reusing results from slow processes, such as applying elevation data to streets. These +different options are controlled with command line switches, and will be described in more detail +below and in other tutorials. + +## Simple One-step Server + +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 + +where `/home/username/otp` should be the directory where you put your configuration and input files. + +If you're using the Portland input data, the graph build operation should take about one minute to +complete, and then you'll see a `Grizzly server running` message. At this point you have an +OpenTripPlanner server running locally and can open [http://localhost:8080/](http://localhost:8080/) +in a web browser. You should be presented with a Javascript client application that will interact +with your local OpenTripPlanner instance. + +This map-based user interface is in fact sending HTTP GET requests to the OTP server running on your +local machine. It can be informative to watch the HTTP requests and responses being generated using +the developer tools in your web browser. OTP's built-in web server will run by default on port 8080. +If by any chance some other software is already using that port number, you can specify a different +port number with a switch +`--port 8801`. + + +## Saving a Graph + +If you want speed up the process of repeatedly starting up a server with the same graph, you can +build a graph from street and transit data then save it to a file using the `--build` and `--save` +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 . + +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 . + +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 +server. Accordingly, you may want to perform the build on one machine (e.g. a throw-away cloud +instance with more memory or compute capacity), then copy the resulting graph file to one or more +smaller machines to serve the API. + +## Layering GTFS onto OSM + +Building the street graph (especially with elevation data) can take a long time. It is common for +transit data to change more frequently than street data, so it can be convenient to build the street +graph once, and then layer transit data on top of the streets to make the final graph. + +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 . + +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 . + +Finally, the server can be started using the `--load` parameter: + + $ java -Xmx2G -jar otp-2.5.0-shaded.jar --load . + +## Command Line Switches + +The flow diagram below summarizes all the command line switches used in the above examples, and how +they control which actions are taken when OTP starts up. + +![Command-Line-Parameter-Flow](images/cli-flow.svg) + +You must use at least one of the required parameters: `--load`, `--loadStreet`, `--build` +, `--buildStreet`. A _required_ parameter may imply other parameters when the flow allows for no +other choice. For example, `--load` implies `--serve`, so `--serve` is not necessary and has no +additional effect when used together with `--load`. + +You can run the OTP .jar file with the `--help` option for a full list of command line parameters. + +## Exploring the API + +If you want to learn how to use OTP's API's, check out the [GraphQL tutorial](apis/GraphQL-Tutorial.md). diff --git a/docs/Bibliography.md b/doc/user/Bibliography.md similarity index 100% rename from docs/Bibliography.md rename to doc/user/Bibliography.md diff --git a/docs/BoardingLocations.md b/doc/user/BoardingLocations.md similarity index 88% rename from docs/BoardingLocations.md rename to doc/user/BoardingLocations.md index 7151b40e176..bf92033624b 100644 --- a/docs/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/docs/BuildConfiguration.md b/doc/user/BuildConfiguration.md similarity index 99% rename from docs/BuildConfiguration.md rename to doc/user/BuildConfiguration.md index 6a6b42cd664..b311991120e 100644 --- a/docs/BuildConfiguration.md +++ b/doc/user/BuildConfiguration.md @@ -1,7 +1,7 @@ diff --git a/docs/Changelog.md b/doc/user/Changelog.md similarity index 99% rename from docs/Changelog.md rename to doc/user/Changelog.md index c8f6a9f1800..697cc79a14b 100644 --- a/docs/Changelog.md +++ b/doc/user/Changelog.md @@ -54,6 +54,13 @@ based on merged pull requests. Search GitHub issues and pull requests for smalle - 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) +- Allow NeTEx ServiceJourneyPatterns with stopUse=passthrough [#6037](https://github.com/opentripplanner/OpenTripPlanner/pull/6037) +- Additional SVG diagrams in updaters package.md [#5936](https://github.com/opentripplanner/OpenTripPlanner/pull/5936) +- Upgrade OBA, remove camsys-apps.com from Maven repos [#6041](https://github.com/opentripplanner/OpenTripPlanner/pull/6041) +- OSM data links added to the graph build report about ambiguous levels and layers [#6049](https://github.com/opentripplanner/OpenTripPlanner/pull/6049) [](AUTOMATIC_CHANGELOG_PLACEHOLDER_DO_NOT_REMOVE) ## 2.5.0 (2024-03-13) diff --git a/docs/Configuration.md b/doc/user/Configuration.md similarity index 99% rename from docs/Configuration.md rename to doc/user/Configuration.md index 7e48dab5344..8e366e476a9 100644 --- a/docs/Configuration.md +++ b/doc/user/Configuration.md @@ -1,7 +1,7 @@ diff --git a/docs/Container-Image.md b/doc/user/Container-Image.md similarity index 100% rename from docs/Container-Image.md rename to doc/user/Container-Image.md diff --git a/docs/Data-Sources.md b/doc/user/Data-Sources.md similarity index 86% rename from docs/Data-Sources.md rename to doc/user/Data-Sources.md index bf426186f57..5ee5fea29d1 100644 --- a/docs/Data-Sources.md +++ b/doc/user/Data-Sources.md @@ -4,9 +4,7 @@ At the core of OpenTripPlanner is a library of Java code that finds efficient paths through multi-modal transportation networks built -from [OpenStreetMap](http://wiki.openstreetmap.org/wiki/Main_Page) -and [GTFS](https://developers.google.com/transit/gtfs/) data. It can also receive GTFS-RT (real-time) -data. +from [OpenStreetMap](http://wiki.openstreetmap.org/wiki/Main_Page), [GTFS/GTFS RT](https://gtfs.org/documentation/overview/) and [GBFS](https://gbfs.org/). In addition to GTFS, OTP can also load data in the Nordic Profile of Netex, the EU-standard transit data interchange format. The upcoming EU-wide profile was heavily influenced by the Nordic Profile diff --git a/docs/Deployments.md b/doc/user/Deployments.md similarity index 98% rename from docs/Deployments.md rename to doc/user/Deployments.md index d1df3984b05..56ff23c113c 100644 --- a/docs/Deployments.md +++ b/doc/user/Deployments.md @@ -1,96 +1,96 @@ -# OpenTripPlanner Deployments Worldwide - -## Official Production - -The following are known deployments of OTP in a government- or agency-sponsored production capacity: - -* **Norway (nationwide)** Since November 2017, the national integrated ticketing agency Entur has - prodvided a [national journey planner](https://en-tur.no/) which consumes schedule data in the EU - standard NeTEx format with SIRI real-time updates. Entur has contributed greatly to the OTP2 effort - and primarily uses OTP2 in production, handling peak loads in excess of 20 requests per second. - Most regional agencies in Norway, like **Ruter, Oslo area** uses OTP as a service provided by Entur. -* **Finland (nationwide)** The [Helsinki Regional Transport Authority](https://www.reittiopas.fi/), - the [Finnish Transport Agency](https://opas.matka.fi/), and - other [Finnish cities](https://waltti.fi/?lang=en) have collaborated to - create [Digitransit](https://digitransit.fi/en/), providing OTP-based trip planners, APIs, open - data, Docker containers and open source code. Each member organisation runs its own instance of a - shared codebase and deployment environment. Their source code is - available [on Github](https://github.com/HSLdevcom/), including - a [new custom UI](https://github.com/HSLdevcom/digitransit-ui). This system also has a strong - real-time component. -* **Finland Intercity** The Finnish intercity coach - service [Matkahuolto](https://en.wikipedia.org/wiki/Matkahuolto) - has [developed a trip planner in partnership with Kyyti](https://www.kyyti.com/matkahuoltos-new-app-brings-real-travel-chains-within-the-reach-of-citizens-in-addition-to-coach-travel-hsl-tickets-are-also-available/). -* **Skåne, Sweden**, the JourneyPlanner and mobile app for the regional transit agency [Skånetrafiken](https://www.skanetrafiken.se/) - uses OTP2 with the nordic profile of NeTEx and SIRI for real-time updates. -* [**Northern Colorado**](https://discover.rideno.co/) -* [**Philadelphia and surrounding areas**](https://plan.septa.org) -* **Portland, Oregon** TriMet is the agency that originally started the OpenTripPlanner project. - Their [Regional Trip Planner](http://ride.trimet.org) is based on OTP and provides about 40,000 - trip plans on a typical weekday. -* [**New York City**](https://new.mta.info/) -* **New York State** The State Department of - Transportation's [transit trip planner](https://511ny.org/#TransitRegion-1) provides itineraries - for public transit systems throughout the state in a single unified OTP instance. -* **Los Angeles, California** The new [metro.net trip planner](https://www.metro.net/). -* **Atlanta, Georgia** The Metropolitan Atlanta Rapid Transit Authority's ( - MARTA) [trip planner](http://itsmarta.com/planatrip.aspx) and the Atlanta region's transit - information hub [https://atlrides.com/](https://atlrides.com/) both use OTP to power their website trip - planners. -* **Boston, Massachusetts** - The [Massachusetts Bay Transportation Authority trip planner](https://www.mbta.com/trip-planner). -* **Seattle, Washington** The [Sound Transit Trip Planner](https://www.soundtransit.org/tripplanner) - is based on OTP. OTP also powers the trip planning feature of - the [OneBusAway native apps](http://onebusaway.org/) in the Puget Sound region. Technical details - are [here](https://github.com/OneBusAway/onebusaway-android/blob/master/SYSTEM_ARCHITECTURE.md#add-trip-planning-andor-bike-share-optional) - . -* [**Ride Metro Houston**](https://planyourtrip.ridemetro.org/) -* **Tampa, Florida** Hillsoborough Area Regional Transit uses an OpenTripPlanner server to power the - trip planning feature of the [OneBusAway native apps](http://onebusaway.org/) in their region. - Technical details - are [here](https://github.com/OneBusAway/onebusaway-android/blob/master/SYSTEM_ARCHITECTURE.md#add-trip-planning-andor-bike-share-optional) - . -* [**Piemonte Region, Italy**](https://map.muoversinpiemonte.it/#planner) and the [**City of - Torino**](https://www.muoversiatorino.it/) built on OpenTripPlanner - by [5T](http://www.5t.torino.it/). -* [**Valencia, Spain**](http://www.emtvalencia.es/geoportal/?lang=en_otp) from the Municipal - Transport Company of Valencia S.A.U. -* [**Grenoble, France**](http://www.metromobilite.fr/) from SMTC, Grenoble Alpes métropole, l'État - Français, the Rhône-alpes region, the Isère council and the City of Grenoble. -* **Rennes, France** where the STAR network provides an OTP client - for [iOS](https://itunes.apple.com/us/app/starbusmetro/id899970416?mt=8) - , [Android](https://play.google.com/store/apps/details?id=com.bookbeo.starbusmetro), Windows Phone - et Web. -* **Alençon, France** integrated urban and school bus - network [planner from Réunir Alençon](https://altobus.com/mon-itineraire/). -* [**Poznań, Poland**](http://ztm.poznan.pl/#planner) from Urban Transport Authority of Poznań (ZTM - Poznan). -* **Trento Province, Italy** - - [ViaggiaTrento](https://play.google.com/store/apps/details?id=eu.trentorise.smartcampus.viaggiatrento) - and [ViaggiaRovereto](https://play.google.com/store/apps/details?id=eu.trentorise.smartcampus.viaggiarovereto) - were implemented as part of the [SmartCampus Project](http://www.smartcampuslab.it), a research - project founded by [TrentoRise](http://trentorise.eu), [UNITN](http://www.unitn.it), - and [FBK](http://www.fbk.eu). -* **University of South Florida** (Tampa, Florida). The [USF Maps App](https://maps.usf.edu/) is a - responsive web application for that helps university students, staff, and visitors find their way - around the campus using multiple modes of transportation, including the USF Bull Runner campus - shuttle, Share-A-Bull bike share, and pedestrian pathways. - Open-sourced [on Github](https://github.com/CUTR-at-USF/usf-mobullity). -* **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. - -## Independent Production - -The following OTP-based services are presented as production-quality deployments, but are not backed -by an official transportation authority or government. OTP is also known to be used on the back end -of several popular multi-city mobile trip planning applications. - -* **The Netherlands (nationwide)** [Plannerstack Foundation](http://www.plannerstack.org/) provides - national scale trip planning APIs using OTP and other open source trip planners, based - on [OpenOV's extremely detailed open data](http://gtfs.openov.nl/) including minutely real-time - updates for every vehicle in the country. -* [OTP Android](https://play.google.com/store/apps/details?id=edu.usf.cutr.opentripplanner.android) - by CUTR-USF and Vreixo González can find itineraries on many different OTP servers via a service - discovery mechanism. -* [**ViviBus Bologna**](http://www.vivibus.it/) Bologna, Italy. +# OpenTripPlanner Deployments Worldwide + +## Official Production + +The following are known deployments of OTP in a government- or agency-sponsored production capacity: + +* **Norway (nationwide)** Since November 2017, the national integrated ticketing agency Entur has + prodvided a [national journey planner](https://en-tur.no/) which consumes schedule data in the EU + standard NeTEx format with SIRI real-time updates. Entur has contributed greatly to the OTP2 effort + and primarily uses OTP2 in production, handling peak loads in excess of 20 requests per second. + Most regional agencies in Norway, like **Ruter, Oslo area** uses OTP as a service provided by Entur. +* **Finland (nationwide)** The [Helsinki Regional Transport Authority](https://www.reittiopas.fi/), + the [Finnish Transport Agency](https://opas.matka.fi/), and + other [Finnish cities](https://waltti.fi/?lang=en) have collaborated to + create [Digitransit](https://digitransit.fi/en/), providing OTP-based trip planners, APIs, open + data, Docker containers and open source code. Each member organisation runs its own instance of a + shared codebase and deployment environment. Their source code is + available [on Github](https://github.com/HSLdevcom/), including + a [new custom UI](https://github.com/HSLdevcom/digitransit-ui). This system also has a strong + real-time component. +* **Finland Intercity** The Finnish intercity coach + service [Matkahuolto](https://en.wikipedia.org/wiki/Matkahuolto) + has [developed a trip planner in partnership with Kyyti](https://www.kyyti.com/matkahuoltos-new-app-brings-real-travel-chains-within-the-reach-of-citizens-in-addition-to-coach-travel-hsl-tickets-are-also-available/). +* **Skåne, Sweden**, the JourneyPlanner and mobile app for the regional transit agency [Skånetrafiken](https://www.skanetrafiken.se/) + uses OTP2 with the nordic profile of NeTEx and SIRI for real-time updates. +* [**Northern Colorado**](https://discover.rideno.co/) +* [**Philadelphia and surrounding areas**](https://plan.septa.org) +* **Portland, Oregon** TriMet is the agency that originally started the OpenTripPlanner project. + Their [Regional Trip Planner](http://ride.trimet.org) is based on OTP and provides about 40,000 + trip plans on a typical weekday. +* [**New York City**](https://new.mta.info/) +* **New York State** The State Department of + Transportation's [transit trip planner](https://511ny.org/#TransitRegion-1) provides itineraries + for public transit systems throughout the state in a single unified OTP instance. +* **Los Angeles, California** The new [metro.net trip planner](https://www.metro.net/). +* **Atlanta, Georgia** The Metropolitan Atlanta Rapid Transit Authority's ( + MARTA) [trip planner](http://itsmarta.com/planatrip.aspx) and the Atlanta region's transit + information hub [https://atlrides.com/](https://atlrides.com/) both use OTP to power their website trip + planners. +* **Boston, Massachusetts** + The [Massachusetts Bay Transportation Authority trip planner](https://www.mbta.com/trip-planner). +* **Seattle, Washington** The [Sound Transit Trip Planner](https://www.soundtransit.org/tripplanner) + is based on OTP. OTP also powers the trip planning feature of + the [OneBusAway native apps](http://onebusaway.org/) in the Puget Sound region. Technical details + are [here](https://github.com/OneBusAway/onebusaway-android/blob/master/SYSTEM_ARCHITECTURE.md#add-trip-planning-andor-bike-share-optional) + . +* [**Ride Metro Houston**](https://planyourtrip.ridemetro.org/) +* **Tampa, Florida** Hillsoborough Area Regional Transit uses an OpenTripPlanner server to power the + trip planning feature of the [OneBusAway native apps](http://onebusaway.org/) in their region. + Technical details + are [here](https://github.com/OneBusAway/onebusaway-android/blob/master/SYSTEM_ARCHITECTURE.md#add-trip-planning-andor-bike-share-optional) + . +* [**Piemonte Region, Italy**](https://map.muoversinpiemonte.it/#planner) and the [**City of + Torino**](https://www.muoversiatorino.it/) built on OpenTripPlanner + by [5T](http://www.5t.torino.it/). +* [**Valencia, Spain**](http://www.emtvalencia.es/geoportal/?lang=en_otp) from the Municipal + Transport Company of Valencia S.A.U. +* [**Grenoble, France**](http://www.metromobilite.fr/) from SMTC, Grenoble Alpes métropole, l'État + Français, the Rhône-alpes region, the Isère council and the City of Grenoble. +* **Rennes, France** where the STAR network provides an OTP client + for [iOS](https://itunes.apple.com/us/app/starbusmetro/id899970416?mt=8) + , [Android](https://play.google.com/store/apps/details?id=com.bookbeo.starbusmetro), Windows Phone + et Web. +* **Alençon, France** integrated urban and school bus + network [planner from Réunir Alençon](https://altobus.com/mon-itineraire/). +* [**Poznań, Poland**](http://ztm.poznan.pl/#planner) from Urban Transport Authority of Poznań (ZTM + Poznan). +* **Trento Province, Italy** + - [ViaggiaTrento](https://play.google.com/store/apps/details?id=eu.trentorise.smartcampus.viaggiatrento) + and [ViaggiaRovereto](https://play.google.com/store/apps/details?id=eu.trentorise.smartcampus.viaggiarovereto) + were implemented as part of the [SmartCampus Project](http://www.smartcampuslab.it), a research + project founded by [TrentoRise](http://trentorise.eu), [UNITN](http://www.unitn.it), + and [FBK](http://www.fbk.eu). +* **University of South Florida** (Tampa, Florida). The [USF Maps App](https://maps.usf.edu/) is a + responsive web application for that helps university students, staff, and visitors find their way + around the campus using multiple modes of transportation, including the USF Bull Runner campus + shuttle, Share-A-Bull bike share, and pedestrian pathways. + Open-sourced [on Github](https://github.com/CUTR-at-USF/usf-mobullity). +* **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. + +## Independent Production + +The following OTP-based services are presented as production-quality deployments, but are not backed +by an official transportation authority or government. OTP is also known to be used on the back end +of several popular multi-city mobile trip planning applications. + +* **The Netherlands (nationwide)** [Plannerstack Foundation](http://www.plannerstack.org/) provides + national scale trip planning APIs using OTP and other open source trip planners, based + on [OpenOV's extremely detailed open data](http://gtfs.openov.nl/) including minutely real-time + updates for every vehicle in the country. +* [OTP Android](https://play.google.com/store/apps/details?id=edu.usf.cutr.opentripplanner.android) + by CUTR-USF and Vreixo González can find itineraries on many different OTP servers via a service + discovery mechanism. +* [**ViviBus Bologna**](http://www.vivibus.it/) Bologna, Italy. diff --git a/docs/Developers-Guide.md b/doc/user/Developers-Guide.md similarity index 94% rename from docs/Developers-Guide.md rename to doc/user/Developers-Guide.md index 8a68f83f301..368a12edb62 100644 --- a/docs/Developers-Guide.md +++ b/doc/user/Developers-Guide.md @@ -197,17 +197,16 @@ Please use only ISO 8601 date format (YYYY-MM-DD) in documentation, comments, an project. This avoids the ambiguity that can result from differing local interpretations of date formats like 02/01/12. -## Code style - -The OTP code style is described on a separate [style guide page](Codestyle.md). - ## Code conventions and architecture -The [architecture](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/ARCHITECTURE.md) -and [code conventions](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/CODE_CONVENTIONS.md) -are only available on GitHub, not in the project documentation. These documents contain relative -links to code so, they are a bit easier to maintain that way. The target audience is also active -OTP developers that have the code checked out locally. +The development and architecture documentation are only available on GitHub, not in the user project +documentation (https://www.opentripplanner.org/). These documents contain relative links to code, +so they are a bit easier to maintain that way. The primary audience is also active OTP developers +that have the code checked out locally. + + - [Architecture](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/ARCHITECTURE.md) + - [Code Conventions](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/CODE_CONVENTIONS.md) + - [Development Decision Records](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/DEVELOPMENT_DECISION_RECORDS.md) ## Continuous Integration @@ -220,7 +219,7 @@ compile and test the new code, providing feedback on the stability of the build. ### Changelog workflow -The [changelog file](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/docs/Changelog.md) +The [changelog file](https://github.com/opentripplanner/OpenTripPlanner/blob/dev-2.x/doc/user/Changelog.md) is generated from the pull-request(PR) _title_ using the [changelog workflow](https://github.com/opentripplanner/OpenTripPlanner/actions/workflows/automatic-changelog.yml) . The workflow runs after the PR is merged, and it changes, commits and pushes the _Changelog.md_. A diff --git a/docs/Frontends.md b/doc/user/Frontends.md similarity index 100% rename from docs/Frontends.md rename to doc/user/Frontends.md diff --git a/docs/Getting-OTP.md b/doc/user/Getting-OTP.md similarity index 100% rename from docs/Getting-OTP.md rename to doc/user/Getting-OTP.md diff --git a/docs/Governance.md b/doc/user/Governance.md similarity index 100% rename from docs/Governance.md rename to doc/user/Governance.md diff --git a/docs/History.md b/doc/user/History.md similarity index 100% rename from docs/History.md rename to doc/user/History.md diff --git a/docs/In-Station-Navigation.md b/doc/user/In-Station-Navigation.md similarity index 100% rename from docs/In-Station-Navigation.md rename to doc/user/In-Station-Navigation.md diff --git a/docs/IslandPruning.md b/doc/user/IslandPruning.md similarity index 100% rename from docs/IslandPruning.md rename to doc/user/IslandPruning.md diff --git a/docs/Localization.md b/doc/user/Localization.md similarity index 100% rename from docs/Localization.md rename to doc/user/Localization.md diff --git a/docs/Logging.md b/doc/user/Logging.md similarity index 100% rename from docs/Logging.md rename to doc/user/Logging.md diff --git a/docs/Migrating-Configuration.md b/doc/user/Migrating-Configuration.md similarity index 100% rename from docs/Migrating-Configuration.md rename to doc/user/Migrating-Configuration.md diff --git a/docs/Netex-Norway.md b/doc/user/Netex-Norway.md similarity index 100% rename from docs/Netex-Norway.md rename to doc/user/Netex-Norway.md diff --git a/docs/Preparing-OSM.md b/doc/user/Preparing-OSM.md similarity index 100% rename from docs/Preparing-OSM.md rename to doc/user/Preparing-OSM.md diff --git a/docs/Presentations.md b/doc/user/Presentations.md similarity index 100% rename from docs/Presentations.md rename to doc/user/Presentations.md diff --git a/docs/Product-Overview.md b/doc/user/Product-Overview.md similarity index 100% rename from docs/Product-Overview.md rename to doc/user/Product-Overview.md diff --git a/docs/README-DOCS.txt b/doc/user/README-DOCS.txt similarity index 100% rename from docs/README-DOCS.txt rename to doc/user/README-DOCS.txt diff --git a/docs/ReleaseChecklist.md b/doc/user/ReleaseChecklist.md similarity index 96% rename from docs/ReleaseChecklist.md rename to doc/user/ReleaseChecklist.md index 0f7c0667762..c67849bc538 100644 --- a/docs/ReleaseChecklist.md +++ b/doc/user/ReleaseChecklist.md @@ -14,8 +14,8 @@ manually is more tedious, but keeps eyes on each step and is less prone to failu * Check all links and references to the release and update to the target release version. Search all files for with a regular expression: `2\.[012]\.0` and replace if appropriate with the new version. - * In `docs/index.md` replace what is the latest version and add a new line for the previous one -* Update `docs/Changelog.md` + * In `doc/user/index.md` replace what is the latest version and add a new line for the previous one +* Update `doc/user/Changelog.md` * Lines should have been added or updated as each pull request was merged * If you suspect any changes are not reflected in the Changelog, review the commit log and add any missing items @@ -78,9 +78,9 @@ manually is more tedious, but keeps eyes on each step and is less prone to failu * `git merge master` * `git push` * Set up next development iteration - * Add a new section header to `docs/Changelog.md` like `x.y+1.0-SNAPSHOT (in progress)` + * Add a new section header to `doc/user/Changelog.md` like `x.y+1.0-SNAPSHOT (in progress)` * Edit minor version in `pom.xml` to `x.y+1.0-SNAPSHOT` - * `git add pom.xml docs/Changelog.md` + * `git add pom.xml doc/user/Changelog.md` * `git commit -m "Prepare next development iteration x.y+1.0-SNAPSHOT"` * `git push` * Send a message in Gitter and email the OTP users mailing lists diff --git a/docs/Roadmap.md b/doc/user/Roadmap.md similarity index 100% rename from docs/Roadmap.md rename to doc/user/Roadmap.md diff --git a/docs/RouteRequest.md b/doc/user/RouteRequest.md similarity index 99% rename from docs/RouteRequest.md rename to doc/user/RouteRequest.md index a8baaf1603b..674ab238888 100644 --- a/docs/RouteRequest.md +++ b/doc/user/RouteRequest.md @@ -1,7 +1,7 @@ diff --git a/docs/RouterConfiguration.md b/doc/user/RouterConfiguration.md similarity index 99% rename from docs/RouterConfiguration.md rename to doc/user/RouterConfiguration.md index 0f91875e542..4e565cfe17d 100644 --- a/docs/RouterConfiguration.md +++ b/doc/user/RouterConfiguration.md @@ -1,7 +1,7 @@ diff --git a/docs/RoutingModes.md b/doc/user/RoutingModes.md similarity index 100% rename from docs/RoutingModes.md rename to doc/user/RoutingModes.md diff --git a/docs/SandboxExtension.md b/doc/user/SandboxExtension.md similarity index 97% rename from docs/SandboxExtension.md rename to doc/user/SandboxExtension.md index 0d05518fd56..70dec6f678d 100644 --- a/docs/SandboxExtension.md +++ b/doc/user/SandboxExtension.md @@ -30,7 +30,7 @@ added in the test directory: `src/ext-test` - To integrate the new feature into OTP you may have to create new extension points in the main/core code. Changes to the core OTP are subject to normal a review process. -- Create a readme file (`docs/sandbox/.md` package including: +- Create a readme file (`doc/user/sandbox/.md` package including: - Extension Name - Contact info - Change log diff --git a/docs/StopAreas.md b/doc/user/StopAreas.md similarity index 100% rename from docs/StopAreas.md rename to doc/user/StopAreas.md diff --git a/docs/System-Requirements.md b/doc/user/System-Requirements.md similarity index 100% rename from docs/System-Requirements.md rename to doc/user/System-Requirements.md diff --git a/docs/Troubleshooting-Routing.md b/doc/user/Troubleshooting-Routing.md similarity index 100% rename from docs/Troubleshooting-Routing.md rename to doc/user/Troubleshooting-Routing.md diff --git a/docs/UpdaterConfig.md b/doc/user/UpdaterConfig.md similarity index 99% rename from docs/UpdaterConfig.md rename to doc/user/UpdaterConfig.md index ebe0acf94b4..f3a0d982e68 100644 --- a/docs/UpdaterConfig.md +++ b/doc/user/UpdaterConfig.md @@ -1,7 +1,7 @@ diff --git a/docs/Version-Comparison.md b/doc/user/Version-Comparison.md similarity index 100% rename from docs/Version-Comparison.md rename to doc/user/Version-Comparison.md diff --git a/docs/Visual-Identity.md b/doc/user/Visual-Identity.md similarity index 100% rename from docs/Visual-Identity.md rename to doc/user/Visual-Identity.md diff --git a/docs/apis/Apis.md b/doc/user/apis/Apis.md similarity index 100% rename from docs/apis/Apis.md rename to doc/user/apis/Apis.md diff --git a/docs/apis/GTFS-GraphQL-API.md b/doc/user/apis/GTFS-GraphQL-API.md similarity index 100% rename from docs/apis/GTFS-GraphQL-API.md rename to doc/user/apis/GTFS-GraphQL-API.md diff --git a/docs/apis/GraphQL-Tutorial.md b/doc/user/apis/GraphQL-Tutorial.md similarity index 98% rename from docs/apis/GraphQL-Tutorial.md rename to doc/user/apis/GraphQL-Tutorial.md index bfc87813f1d..d65fbc144ba 100644 --- a/docs/apis/GraphQL-Tutorial.md +++ b/doc/user/apis/GraphQL-Tutorial.md @@ -1,7 +1,7 @@ diff --git a/docs/apis/TransmodelApi.md b/doc/user/apis/TransmodelApi.md similarity index 100% rename from docs/apis/TransmodelApi.md rename to doc/user/apis/TransmodelApi.md diff --git a/docs/examples/Readme.md b/doc/user/examples/Readme.md similarity index 100% rename from docs/examples/Readme.md rename to doc/user/examples/Readme.md diff --git a/docs/examples/entur/Readme.md b/doc/user/examples/entur/Readme.md similarity index 100% rename from docs/examples/entur/Readme.md rename to doc/user/examples/entur/Readme.md diff --git a/docs/examples/entur/build-config.json b/doc/user/examples/entur/build-config.json similarity index 100% rename from docs/examples/entur/build-config.json rename to doc/user/examples/entur/build-config.json diff --git a/docs/examples/entur/otp-config.json b/doc/user/examples/entur/otp-config.json similarity index 100% rename from docs/examples/entur/otp-config.json rename to doc/user/examples/entur/otp-config.json diff --git a/docs/examples/entur/router-config.json b/doc/user/examples/entur/router-config.json similarity index 100% rename from docs/examples/entur/router-config.json rename to doc/user/examples/entur/router-config.json diff --git a/docs/examples/ibi/atlanta/build-config.json b/doc/user/examples/ibi/atlanta/build-config.json similarity index 100% rename from docs/examples/ibi/atlanta/build-config.json rename to doc/user/examples/ibi/atlanta/build-config.json diff --git a/docs/examples/ibi/atlanta/otp-config.json b/doc/user/examples/ibi/atlanta/otp-config.json similarity index 100% rename from docs/examples/ibi/atlanta/otp-config.json rename to doc/user/examples/ibi/atlanta/otp-config.json diff --git a/docs/examples/ibi/atlanta/router-config.json b/doc/user/examples/ibi/atlanta/router-config.json similarity index 100% rename from docs/examples/ibi/atlanta/router-config.json rename to doc/user/examples/ibi/atlanta/router-config.json diff --git a/docs/examples/ibi/houston/build-config.json b/doc/user/examples/ibi/houston/build-config.json similarity index 100% rename from docs/examples/ibi/houston/build-config.json rename to doc/user/examples/ibi/houston/build-config.json diff --git a/docs/examples/ibi/portland/build-config.json b/doc/user/examples/ibi/portland/build-config.json similarity index 100% rename from docs/examples/ibi/portland/build-config.json rename to doc/user/examples/ibi/portland/build-config.json diff --git a/docs/examples/ibi/portland/otp-config.json b/doc/user/examples/ibi/portland/otp-config.json similarity index 100% rename from docs/examples/ibi/portland/otp-config.json rename to doc/user/examples/ibi/portland/otp-config.json diff --git a/docs/examples/ibi/portland/router-config.json b/doc/user/examples/ibi/portland/router-config.json similarity index 100% rename from docs/examples/ibi/portland/router-config.json rename to doc/user/examples/ibi/portland/router-config.json diff --git a/docs/examples/ibi/seattle/build-config.json b/doc/user/examples/ibi/seattle/build-config.json similarity index 100% rename from docs/examples/ibi/seattle/build-config.json rename to doc/user/examples/ibi/seattle/build-config.json diff --git a/docs/examples/ibi/seattle/router-config.json b/doc/user/examples/ibi/seattle/router-config.json similarity index 100% rename from docs/examples/ibi/seattle/router-config.json rename to doc/user/examples/ibi/seattle/router-config.json diff --git a/docs/examples/ibi/septa/router-config.json b/doc/user/examples/ibi/septa/router-config.json similarity index 100% rename from docs/examples/ibi/septa/router-config.json rename to doc/user/examples/ibi/septa/router-config.json diff --git a/docs/examples/skanetrafiken/Readme.md b/doc/user/examples/skanetrafiken/Readme.md similarity index 100% rename from docs/examples/skanetrafiken/Readme.md rename to doc/user/examples/skanetrafiken/Readme.md diff --git a/docs/examples/skanetrafiken/build-config.json b/doc/user/examples/skanetrafiken/build-config.json similarity index 100% rename from docs/examples/skanetrafiken/build-config.json rename to doc/user/examples/skanetrafiken/build-config.json diff --git a/docs/examples/skanetrafiken/oresund.json b/doc/user/examples/skanetrafiken/oresund.json similarity index 100% rename from docs/examples/skanetrafiken/oresund.json rename to doc/user/examples/skanetrafiken/oresund.json diff --git a/docs/examples/skanetrafiken/router-config.json b/doc/user/examples/skanetrafiken/router-config.json similarity index 100% rename from docs/examples/skanetrafiken/router-config.json rename to doc/user/examples/skanetrafiken/router-config.json diff --git a/docs/github_issue_linker.py b/doc/user/github_issue_linker.py similarity index 100% rename from docs/github_issue_linker.py rename to doc/user/github_issue_linker.py diff --git a/docs/images/badprojection.png b/doc/user/images/badprojection.png similarity index 100% rename from docs/images/badprojection.png rename to doc/user/images/badprojection.png diff --git a/docs/images/bicycle-safety-report.png b/doc/user/images/bicycle-safety-report.png similarity index 100% rename from docs/images/bicycle-safety-report.png rename to doc/user/images/bicycle-safety-report.png diff --git a/docs/images/boarding-schwabstrasse.png b/doc/user/images/boarding-schwabstrasse.png similarity index 100% rename from docs/images/boarding-schwabstrasse.png rename to doc/user/images/boarding-schwabstrasse.png diff --git a/docs/images/buckhead-station.png b/doc/user/images/buckhead-station.png similarity index 100% rename from docs/images/buckhead-station.png rename to doc/user/images/buckhead-station.png diff --git a/docs/images/cli-flow.svg b/doc/user/images/cli-flow.svg similarity index 100% rename from docs/images/cli-flow.svg rename to doc/user/images/cli-flow.svg diff --git a/docs/images/example-isochrone.png b/doc/user/images/example-isochrone.png similarity index 100% rename from docs/images/example-isochrone.png rename to doc/user/images/example-isochrone.png diff --git a/docs/images/exiting-oesterfeld.png b/doc/user/images/exiting-oesterfeld.png similarity index 100% rename from docs/images/exiting-oesterfeld.png rename to doc/user/images/exiting-oesterfeld.png diff --git a/docs/images/graphiql-autocomplete.png b/doc/user/images/graphiql-autocomplete.png similarity index 100% rename from docs/images/graphiql-autocomplete.png rename to doc/user/images/graphiql-autocomplete.png diff --git a/docs/images/graphiql-documentation.png b/doc/user/images/graphiql-documentation.png similarity index 100% rename from docs/images/graphiql-documentation.png rename to doc/user/images/graphiql-documentation.png diff --git a/docs/images/graphiql.png b/doc/user/images/graphiql.png similarity index 100% rename from docs/images/graphiql.png rename to doc/user/images/graphiql.png diff --git a/docs/images/nothruisland.png b/doc/user/images/nothruisland.png similarity index 100% rename from docs/images/nothruisland.png rename to doc/user/images/nothruisland.png diff --git a/docs/images/osmislands.png b/doc/user/images/osmislands.png similarity index 100% rename from docs/images/osmislands.png rename to doc/user/images/osmislands.png diff --git a/docs/images/otp-logo.svg b/doc/user/images/otp-logo.svg similarity index 87% rename from docs/images/otp-logo.svg rename to doc/user/images/otp-logo.svg index 1ed23d0be8e..6e32af842a6 100644 --- a/docs/images/otp-logo.svg +++ b/doc/user/images/otp-logo.svg @@ -1,7 +1,8 @@ - + - - Produced by OmniGraffle 6.6.2 2021-02-08 15:47:55 +0000Canvas 1Layer 1Bord-TimeLine 11Line 31Line 21Stop ABoard SlackBoard SlackAlight SlackAlight SlackAlight SlackTransit SlackTransit Slack«Extra Slack»«Extra Slack»Board SlackEarliest-Board-Time(Earliest-)Board-TimeStop BStop CStop DStop EStop-ArrivalStop-ArrivalStop-ArrivalStop-ArrivalStop-ArrivalOriginDestinationWait Time (Duration)Transit DurationWait Time (Duration)Transit DurationAlight-TimeAlight-Time diff --git a/mkdocs.yml b/mkdocs.yml index dbb9f86c877..8b3748be2b0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -6,7 +6,7 @@ site_name: OpenTripPlanner 2 site_url: https://docs.opentripplanner.org repo_url: https://github.com/opentripplanner/OpenTripPlanner -docs_dir: docs +docs_dir: doc/user site_dir: target/mkdocs strict: true @@ -96,7 +96,6 @@ nav: - "Developers' Guide": 'Developers-Guide.md' - Localization: 'Localization.md' - Bibliography: 'Bibliography.md' - - Codestyle: 'Codestyle.md' - Sandbox Development: 'SandboxExtension.md' - Release Checklist: 'ReleaseChecklist.md' - Sandbox: diff --git a/pom.xml b/pom.xml index 788bb0eeaea..33b74ac5551 100644 --- a/pom.xml +++ b/pom.xml @@ -59,15 +59,15 @@ 156 31.3 - 2.51.1 + 2.52 2.17.2 3.1.8 - 5.10.3 + 5.11.0 1.13.2 5.6.0 - 1.5.6 + 1.5.7 9.11.1 - 2.0.14 + 2.0.16 2.0.15 1.27 4.0.5 @@ -171,12 +171,12 @@ - + package-javadoc package jar - ${basedir}/docs/javadoc + ${basedir}/doc/javadoc javadoc @@ -533,11 +533,6 @@ Open Source Geospatial Foundation Repository https://repo.osgeo.org/repository/release/ - - onebusaway-releases - Onebusaway Releases Repo - https://repo.camsys-apps.com/releases/ - @@ -694,7 +689,7 @@ com.google.truth truth - 1.4.2 + 1.4.4 test @@ -824,13 +819,7 @@ org.onebusaway onebusaway-gtfs - 1.4.17 - - - org.slf4j - slf4j-simple - - + 3.2.3 @@ -859,7 +848,7 @@ com.graphql-java graphql-java - 22.1 + 22.3 com.graphql-java @@ -923,7 +912,7 @@ org.apache.commons commons-compress - 1.26.2 + 1.27.0 test @@ -1000,7 +989,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.4 + 3.2.5 sign-artifacts diff --git a/renovate.json5 b/renovate.json5 index 5a56c64cccb..b672797d48f 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -34,7 +34,7 @@ "matchFiles": ["client/package.json"], "matchUpdateTypes": ["patch", "minor"], "groupName": "Debug UI dependencies (non-major)", - "schedule": ["on the 3rd and 17th day of the month"], + "schedule": ["after 6pm on the 3rd and 17th day of the month"], "reviewers": ["testower"] }, { @@ -136,7 +136,7 @@ }, { "description": "Automerge logging dependencies in a single PR", - "groupName": "logging", + "groupName": "logging dependencies", "matchPackagePrefixes": [ "org.slf4j:", "ch.qos.logback:" diff --git a/src/client/index.html b/src/client/index.html index 2d82a433279..aa609bb0696 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/accessibilityscore/DecorateWithAccessibilityScoreTest.java b/src/ext-test/java/org/opentripplanner/ext/accessibilityscore/DecorateWithAccessibilityScoreTest.java index d70b9f32f62..27c10c08129 100644 --- a/src/ext-test/java/org/opentripplanner/ext/accessibilityscore/DecorateWithAccessibilityScoreTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/accessibilityscore/DecorateWithAccessibilityScoreTest.java @@ -2,20 +2,26 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.opentripplanner.model.plan.TestItineraryBuilder.newItinerary; import java.util.List; +import java.util.function.Function; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.model.plan.Itinerary; import org.opentripplanner.model.plan.Place; import org.opentripplanner.model.plan.PlanTestConstants; +import org.opentripplanner.model.plan.TestItineraryBuilder; import org.opentripplanner.routing.api.request.preference.WheelchairPreferences; -public class DecorateWithAccessibilityScoreTest implements PlanTestConstants { +class DecorateWithAccessibilityScoreTest implements PlanTestConstants { private static final int ID = 1; + private static final DecorateWithAccessibilityScore DECORATOR = new DecorateWithAccessibilityScore( + WheelchairPreferences.DEFAULT.maxSlope() + ); static List accessibilityScoreTestCase() { return List.of( @@ -48,17 +54,28 @@ static List accessibilityScoreTestCase() { @ParameterizedTest @MethodSource("accessibilityScoreTestCase") - public void accessibilityScoreTest(Itinerary itinerary, float expectedAccessibilityScore) { - var filter = new DecorateWithAccessibilityScore(WheelchairPreferences.DEFAULT.maxSlope()); - - filter.decorate(itinerary); + void accessibilityScoreTest(Itinerary itinerary, float expectedAccessibilityScore) { + DECORATOR.decorate(itinerary); assertEquals(expectedAccessibilityScore, itinerary.getAccessibilityScore()); - itinerary - .getLegs() - .forEach(l -> { - assertNotNull(l.accessibilityScore()); - }); + itinerary.getLegs().forEach(l -> assertNotNull(l.accessibilityScore())); + } + + private static List> nonWalkingCases() { + return List.of( + b -> b.bicycle(10, 20, B), + b -> b.drive(10, 20, B), + b -> b.rentedBicycle(10, 20, B) + ); + } + + @MethodSource("nonWalkingCases") + @ParameterizedTest + void noScoreForNonWalking(Function modifier) { + var itinerary = modifier.apply(newItinerary(A, 0)).build(); + DECORATOR.decorate(itinerary); + assertNull(itinerary.getAccessibilityScore()); + itinerary.getLegs().forEach(l -> assertNull(l.accessibilityScore())); } } 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/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 647b772aa69..cf29eec8f49 100644 --- a/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java @@ -16,6 +16,7 @@ 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 { diff --git a/src/ext-test/java/org/opentripplanner/ext/vectortiles/VectorTilesConfigDocTest.java b/src/ext-test/java/org/opentripplanner/ext/vectortiles/VectorTilesConfigDocTest.java index 233e3fa3737..51e1738ff6c 100644 --- a/src/ext-test/java/org/opentripplanner/ext/vectortiles/VectorTilesConfigDocTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/vectortiles/VectorTilesConfigDocTest.java @@ -4,8 +4,8 @@ import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; import static org.opentripplanner.framework.text.MarkdownFormatter.HEADER_4; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceSection; import static org.opentripplanner.standalone.config.framework.json.JsonSupport.jsonNodeFromPath; @@ -24,8 +24,8 @@ public class VectorTilesConfigDocTest { private static final String DOCUMENT = "sandbox/MapboxVectorTilesApi.md"; - private static final File TEMPLATE = new File(TEMPLATE_ROOT, DOCUMENT); - private static final File OUT_FILE = new File(DOCS_ROOT, DOCUMENT); + private static final File TEMPLATE = new File(TEMPLATE_PATH, DOCUMENT); + private static final File OUT_FILE = new File(USER_DOC_PATH, DOCUMENT); private static final SkipNodes SKIP_NODES = SkipNodes.of().build(); @Test 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-test/java/org/opentripplanner/ext/vectortiles/layers/vehiclerental/mapper/VehicleRentalLayerTest.java b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehiclerental/mapper/VehicleRentalLayerTest.java index 34a8f28f9a4..984d4927041 100644 --- a/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehiclerental/mapper/VehicleRentalLayerTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/vectortiles/layers/vehiclerental/mapper/VehicleRentalLayerTest.java @@ -38,6 +38,7 @@ public void floatingVehicle() { assertEquals("A:B", map.get("id")); assertEquals("BICYCLE", map.get("formFactor")); assertEquals("A", map.get("network")); + assertEquals(true, map.get("pickupAllowed")); assertNull(map.get("name")); } diff --git a/src/ext-test/java/org/opentripplanner/ext/vehiclerentalservicedirectory/generatedoc/VehicleRentalServiceDirectoryConfigDocTest.java b/src/ext-test/java/org/opentripplanner/ext/vehiclerentalservicedirectory/generatedoc/VehicleRentalServiceDirectoryConfigDocTest.java index 4936bb4dd44..2afcd95949c 100644 --- a/src/ext-test/java/org/opentripplanner/ext/vehiclerentalservicedirectory/generatedoc/VehicleRentalServiceDirectoryConfigDocTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/vehiclerentalservicedirectory/generatedoc/VehicleRentalServiceDirectoryConfigDocTest.java @@ -5,8 +5,8 @@ import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; import static org.opentripplanner.framework.text.MarkdownFormatter.HEADER_4; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceJsonExample; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceParametersDetails; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceParametersTable; @@ -26,8 +26,8 @@ public class VehicleRentalServiceDirectoryConfigDocTest { private static final String DOCUMENT = "sandbox/VehicleRentalServiceDirectory.md"; - private static final File TEMPLATE = new File(TEMPLATE_ROOT, DOCUMENT); - private static final File OUT_FILE = new File(DOCS_ROOT, DOCUMENT); + private static final File TEMPLATE = new File(TEMPLATE_PATH, DOCUMENT); + private static final File OUT_FILE = new File(USER_DOC_PATH, DOCUMENT); private static final String CONFIG_PATH = "org/opentripplanner/ext/vehiclerentalservicedirectory/generatedoc/" + ROUTER_CONFIG_FILENAME; private static final String CONFIG_TAG = "vehicleRentalServiceDirectory"; diff --git a/src/ext/java/org/opentripplanner/ext/accessibilityscore/DecorateWithAccessibilityScore.java b/src/ext/java/org/opentripplanner/ext/accessibilityscore/DecorateWithAccessibilityScore.java index 51a138b9f9c..d95f2c4c0cd 100644 --- a/src/ext/java/org/opentripplanner/ext/accessibilityscore/DecorateWithAccessibilityScore.java +++ b/src/ext/java/org/opentripplanner/ext/accessibilityscore/DecorateWithAccessibilityScore.java @@ -116,7 +116,7 @@ private Itinerary addAccessibilityScore(Itinerary i) { .map(leg -> { if (leg instanceof ScheduledTransitLeg transitLeg) { return transitLeg.withAccessibilityScore(compute(transitLeg)); - } else if (leg instanceof StreetLeg streetLeg) { + } else if (leg instanceof StreetLeg streetLeg && leg.isWalkingLeg()) { return streetLeg.withAccessibilityScore(compute(streetLeg)); } else { return leg; 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 1c9adeb1087..7746df7d02f 100644 --- a/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java +++ b/src/ext/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSource.java @@ -134,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(); } 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/ext/java/org/opentripplanner/ext/vectortiles/layers/vehiclerental/mapper/DigitransitRentalVehiclePropertyMapper.java b/src/ext/java/org/opentripplanner/ext/vectortiles/layers/vehiclerental/mapper/DigitransitRentalVehiclePropertyMapper.java index 33e661866bc..00ae4e5ed88 100644 --- a/src/ext/java/org/opentripplanner/ext/vectortiles/layers/vehiclerental/mapper/DigitransitRentalVehiclePropertyMapper.java +++ b/src/ext/java/org/opentripplanner/ext/vectortiles/layers/vehiclerental/mapper/DigitransitRentalVehiclePropertyMapper.java @@ -15,6 +15,7 @@ protected Collection map(VehicleRentalVehicle place) { var items = new ArrayList(); items.addAll(getFeedScopedIdAndNetwork(place)); items.add(new KeyValue("formFactor", place.vehicleType.formFactor.toString())); + items.add(new KeyValue("pickupAllowed", place.isAllowPickup())); return items; } } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/package.json b/src/main/java/org/opentripplanner/apis/gtfs/generated/package.json index 6dd08bb8041..d6cbf02d5e7 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/package.json +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/package.json @@ -14,6 +14,6 @@ "@graphql-codegen/cli": "5.0.2", "@graphql-codegen/java": "4.0.1", "@graphql-codegen/java-resolvers": "3.0.0", - "graphql": "16.8.1" + "graphql": "16.9.0" } } diff --git a/src/main/java/org/opentripplanner/apis/gtfs/generated/yarn.lock b/src/main/java/org/opentripplanner/apis/gtfs/generated/yarn.lock index 41bc851882b..25686abd94a 100644 --- a/src/main/java/org/opentripplanner/apis/gtfs/generated/yarn.lock +++ b/src/main/java/org/opentripplanner/apis/gtfs/generated/yarn.lock @@ -2181,10 +2181,10 @@ graphql-ws@^5.14.0: resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.14.0.tgz#766f249f3974fc2c48fae0d1fb20c2c4c79cd591" integrity sha512-itrUTQZP/TgswR4GSSYuwWUzrE/w5GhbwM2GX3ic2U7aw33jgEsayfIlvaj7/GcIvZgNMzsPTrE5hqPuFUiE5g== -graphql@16.8.1: - version "16.8.1" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07" - integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw== +graphql@16.9.0: + version "16.9.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.9.0.tgz#1c310e63f16a49ce1fbb230bd0a000e99f6f115f" + integrity sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw== has-flag@^3.0.0: version "3.0.0" 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/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/service/realtimevehicles/internal/DefaultRealtimeVehicleService.java b/src/main/java/org/opentripplanner/service/realtimevehicles/internal/DefaultRealtimeVehicleService.java index 07fa48fa84f..0058cdd9e15 100644 --- a/src/main/java/org/opentripplanner/service/realtimevehicles/internal/DefaultRealtimeVehicleService.java +++ b/src/main/java/org/opentripplanner/service/realtimevehicles/internal/DefaultRealtimeVehicleService.java @@ -8,7 +8,6 @@ import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import javax.annotation.Nonnull; import org.opentripplanner.service.realtimevehicles.RealtimeVehicleRepository; @@ -33,8 +32,16 @@ public DefaultRealtimeVehicleService(TransitService transitService) { this.transitService = transitService; } + /** + * Stores the relationship between a list of realtime vehicles with a pattern. If the pattern is + * a realtime-added one, then the original (scheduled) one is used as the key for the map storing + * the information. + */ @Override public void setRealtimeVehicles(TripPattern pattern, List updates) { + if (pattern.getOriginalTripPattern() != null) { + pattern = pattern.getOriginalTripPattern(); + } vehicles.put(pattern, List.copyOf(updates)); } @@ -43,8 +50,18 @@ public void clearRealtimeVehicles(TripPattern pattern) { vehicles.remove(pattern); } + /** + * Gets the realtime vehicles for a given pattern. If the pattern is a realtime-added one + * then the original (scheduled) one is used for the lookup instead, so you receive the correct + * result no matter if you use the realtime or static information. + * + * @see DefaultRealtimeVehicleService#setRealtimeVehicles(TripPattern, List) + */ @Override public List getRealtimeVehicles(@Nonnull TripPattern pattern) { + if (pattern.getOriginalTripPattern() != null) { + pattern = pattern.getOriginalTripPattern(); + } // the list is made immutable during insertion, so we can safely return them return vehicles.getOrDefault(pattern, List.of()); } 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/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/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/TimetableSnapshotSource.java b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java index 4c92553b26e..03ce9052331 100644 --- a/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java +++ b/src/main/java/org/opentripplanner/updater/trip/TimetableSnapshotSource.java @@ -334,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, 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/generate/doc/BuildConfigurationDocTest.java b/src/test/java/org/opentripplanner/generate/doc/BuildConfigurationDocTest.java index 4009a455abe..8189f18f20e 100644 --- a/src/test/java/org/opentripplanner/generate/doc/BuildConfigurationDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/BuildConfigurationDocTest.java @@ -4,8 +4,8 @@ import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; import static org.opentripplanner.framework.text.MarkdownFormatter.HEADER_3; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceJsonExample; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceParametersDetails; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceParametersTable; @@ -25,8 +25,8 @@ public class BuildConfigurationDocTest { private static final String CONFIG_JSON = OtpFileNames.BUILD_CONFIG_FILENAME; - private static final File TEMPLATE = new File(TEMPLATE_ROOT, "BuildConfiguration.md"); - private static final File OUT_FILE = new File(DOCS_ROOT, "BuildConfiguration.md"); + private static final File TEMPLATE = new File(TEMPLATE_PATH, "BuildConfiguration.md"); + private static final File OUT_FILE = new File(USER_DOC_PATH, "BuildConfiguration.md"); private static final String CONFIG_PATH = "standalone/config/" + CONFIG_JSON; private static final SkipNodes SKIP_NODES = SkipNodes @@ -38,7 +38,7 @@ public class BuildConfigurationDocTest { .build(); /** - * NOTE! This test updates the {@code docs/Configuration.md} document based on the latest + * NOTE! This test updates the {@code doc/user/Configuration.md} document based on the latest * version of the code. The following is auto generated: *

    *
  • The configuration type table
  • diff --git a/src/test/java/org/opentripplanner/generate/doc/ConfigurationDocTest.java b/src/test/java/org/opentripplanner/generate/doc/ConfigurationDocTest.java index 19a562d8b3f..fed5ddb325d 100644 --- a/src/test/java/org/opentripplanner/generate/doc/ConfigurationDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/ConfigurationDocTest.java @@ -3,8 +3,8 @@ import static org.opentripplanner.framework.io.FileUtils.assertFileEquals; import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceSection; import static org.opentripplanner.generate.doc.support.ConfigTypeTable.configTypeTable; import static org.opentripplanner.generate.doc.support.OTPFeatureTable.otpFeaturesTable; @@ -16,22 +16,22 @@ @GeneratesDocumentation public class ConfigurationDocTest { - private static final File TEMPLATE = new File(TEMPLATE_ROOT, "Configuration.md"); + private static final File TEMPLATE = new File(TEMPLATE_PATH, "Configuration.md"); - private static final File OUT_FILE = new File(DOCS_ROOT, "Configuration.md"); + private static final File OUT_FILE = new File(USER_DOC_PATH, "Configuration.md"); private static final String CONFIG_TYPE_PLACEHOLDER = "CONFIGURATION-TYPES-TABLE"; private static final String OTP_FEATURE_PLACEHOLDER = "OTP-FEATURE-TABLE"; /** - * NOTE! This test updates the {@code docs/Configuration.md} document based on the latest + * NOTE! This test updates the {@code doc/user/Configuration.md} document based on the latest * version of the code. The following is auto generated: *
      *
    • The configuration type table
    • *
    • The list of OTP features
    • *
    - * This test fails if the document have changed. This make sure that this test fails in the - * CI pipeline if config file changes is not committed. Manually inspect the changes in the + * This test fails if the document has changed. This makes sure that this test fails in the + * CI pipeline if config file changes are not committed. Manually inspect the changes in the * configuration, commit the configuration document, and run test again to pass. */ @Test diff --git a/src/test/java/org/opentripplanner/generate/doc/EmissionsConfigurationDocTest.java b/src/test/java/org/opentripplanner/generate/doc/EmissionsConfigurationDocTest.java index 68caa043e26..5010c78d527 100644 --- a/src/test/java/org/opentripplanner/generate/doc/EmissionsConfigurationDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/EmissionsConfigurationDocTest.java @@ -4,8 +4,8 @@ import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; import static org.opentripplanner.framework.text.MarkdownFormatter.HEADER_4; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceSection; import static org.opentripplanner.standalone.config.framework.json.JsonSupport.jsonNodeFromResource; @@ -24,8 +24,8 @@ @GeneratesDocumentation public class EmissionsConfigurationDocTest { - private static final File TEMPLATE = new File(TEMPLATE_ROOT, "Emissions.md"); - private static final File OUT_FILE = new File(DOCS_ROOT + "/sandbox", "Emissions.md"); + private static final File TEMPLATE = new File(TEMPLATE_PATH, "Emissions.md"); + private static final File OUT_FILE = new File(USER_DOC_PATH + "/sandbox", "Emissions.md"); private static final String CONFIG_JSON = OtpFileNames.BUILD_CONFIG_FILENAME; private static final String CONFIG_PATH = "standalone/config/" + CONFIG_JSON; private static final SkipNodes SKIP_NODES = SkipNodes.of().build(); diff --git a/src/test/java/org/opentripplanner/generate/doc/FlexConfigurationDocTest.java b/src/test/java/org/opentripplanner/generate/doc/FlexConfigurationDocTest.java index fd2d7092dc5..e18090466b9 100644 --- a/src/test/java/org/opentripplanner/generate/doc/FlexConfigurationDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/FlexConfigurationDocTest.java @@ -4,8 +4,8 @@ import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; import static org.opentripplanner.framework.text.MarkdownFormatter.HEADER_4; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceSection; import static org.opentripplanner.standalone.config.framework.json.JsonSupport.jsonNodeFromResource; @@ -23,8 +23,8 @@ @GeneratesDocumentation public class FlexConfigurationDocTest { - private static final File TEMPLATE = new File(TEMPLATE_ROOT, "Flex.md"); - private static final File OUT_FILE = new File(DOCS_ROOT + "/sandbox", "Flex.md"); + private static final File TEMPLATE = new File(TEMPLATE_PATH, "Flex.md"); + private static final File OUT_FILE = new File(USER_DOC_PATH + "/sandbox", "Flex.md"); private static final String ROUTER_CONFIG_FILENAME = "standalone/config/router-config.json"; private static final SkipNodes SKIP_NODES = SkipNodes.of().build(); diff --git a/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java b/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java index 3597844a027..7eb6a685cca 100644 --- a/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/GraphQLTutorialDocTest.java @@ -4,8 +4,8 @@ import static org.opentripplanner.framework.io.FileUtils.assertFileEquals; import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceSection; import com.google.common.io.Resources; @@ -20,12 +20,12 @@ @GeneratesDocumentation public class GraphQLTutorialDocTest { - private static final File TEMPLATE = new File(TEMPLATE_ROOT, "GraphQL-Tutorial.md"); + private static final File TEMPLATE = new File(TEMPLATE_PATH, "GraphQL-Tutorial.md"); - private static final File OUT_FILE = new File(DOCS_ROOT + "/apis", "GraphQL-Tutorial.md"); + private static final File OUT_FILE = new File(USER_DOC_PATH + "/apis", "GraphQL-Tutorial.md"); /** - * NOTE! This test updates the {@code docs/GraphQlTutorial.md} document based on the latest + * NOTE! This test updates the {@code doc/user/GraphQlTutorial.md} document based on the latest * version of the code. * This test fails if the document have changed. This make sure that this test fails in the * CI pipeline if config file changes is not committed. Manually inspect the changes in the diff --git a/src/test/java/org/opentripplanner/generate/doc/OsmMapperDocTest.java b/src/test/java/org/opentripplanner/generate/doc/OsmMapperDocTest.java index 3090c696e37..d3eb0a44122 100644 --- a/src/test/java/org/opentripplanner/generate/doc/OsmMapperDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/OsmMapperDocTest.java @@ -3,8 +3,8 @@ import static org.opentripplanner.framework.io.FileUtils.assertFileEquals; import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceSection; import static org.opentripplanner.openstreetmap.tagmapping.OsmTagMapperSource.ATLANTA; import static org.opentripplanner.openstreetmap.tagmapping.OsmTagMapperSource.CONSTANT_SPEED_FINLAND; @@ -20,6 +20,7 @@ import org.junit.jupiter.params.provider.MethodSource; import org.opentripplanner.framework.text.Table; import org.opentripplanner.framework.text.TableBuilder; +import org.opentripplanner.generate.doc.framework.DocsTestConstants; import org.opentripplanner.generate.doc.framework.GeneratesDocumentation; import org.opentripplanner.openstreetmap.tagmapping.OsmTagMapper; import org.opentripplanner.openstreetmap.tagmapping.OsmTagMapperSource; @@ -30,7 +31,7 @@ public class OsmMapperDocTest { private static final String FILE_NAME = "OsmMapper.md"; - private static final File TEMPLATE = new File(TEMPLATE_ROOT, FILE_NAME); + private static final File TEMPLATE = new File(TEMPLATE_PATH, FILE_NAME); private static final Set SKIP_MAPPERS = Set.of( ATLANTA, HOUSTON, @@ -70,7 +71,7 @@ public void updateDocs(OsmTagMapperSource source) { private static File outputFile(OsmTagMapper mapper) { var name = mapper.getClass().getSimpleName().replaceAll("Mapper", ".md"); - return new File("%s/osm/".formatted(DOCS_ROOT), name); + return new File("%s/osm/".formatted(USER_DOC_PATH), name); } private static Table propTable(WayPropertySet wps) { diff --git a/src/test/java/org/opentripplanner/generate/doc/RideHailingDocTest.java b/src/test/java/org/opentripplanner/generate/doc/RideHailingDocTest.java index 7ebd90260f1..2d04e002413 100644 --- a/src/test/java/org/opentripplanner/generate/doc/RideHailingDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/RideHailingDocTest.java @@ -5,8 +5,8 @@ import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; import static org.opentripplanner.framework.text.MarkdownFormatter.HEADER_4; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceSection; import static org.opentripplanner.standalone.config.framework.json.JsonSupport.jsonNodeFromResource; @@ -24,8 +24,8 @@ @GeneratesDocumentation public class RideHailingDocTest { - private static final File TEMPLATE = new File(TEMPLATE_ROOT, "RideHailing.md"); - private static final File OUT_FILE = new File(DOCS_ROOT + "/sandbox", "RideHailing.md"); + private static final File TEMPLATE = new File(TEMPLATE_PATH, "RideHailing.md"); + private static final File OUT_FILE = new File(USER_DOC_PATH + "/sandbox", "RideHailing.md"); private static final String ROUTER_CONFIG_PATH = "standalone/config/" + ROUTER_CONFIG_FILENAME; private static final SkipNodes SKIP_NODES = SkipNodes.of().build(); diff --git a/src/test/java/org/opentripplanner/generate/doc/RouteRequestDocTest.java b/src/test/java/org/opentripplanner/generate/doc/RouteRequestDocTest.java index 76642db3e5a..b4725c5e2a1 100644 --- a/src/test/java/org/opentripplanner/generate/doc/RouteRequestDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/RouteRequestDocTest.java @@ -5,8 +5,8 @@ import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; import static org.opentripplanner.framework.text.MarkdownFormatter.HEADER_3; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceJsonExample; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceParametersDetails; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceParametersTable; @@ -25,8 +25,8 @@ @GeneratesDocumentation public class RouteRequestDocTest { - private static final File TEMPLATE = new File(TEMPLATE_ROOT, "RouteRequest.md"); - private static final File OUT_FILE = new File(DOCS_ROOT, "RouteRequest.md"); + private static final File TEMPLATE = new File(TEMPLATE_PATH, "RouteRequest.md"); + private static final File OUT_FILE = new File(USER_DOC_PATH, "RouteRequest.md"); private static final String ROUTER_CONFIG_PATH = "standalone/config/" + ROUTER_CONFIG_FILENAME; private static final SkipNodes SKIP_NODES = SkipNodes .of() @@ -34,7 +34,7 @@ public class RouteRequestDocTest { .build(); /** - * NOTE! This test updates the {@code docs/RouteRequest.md} document based on the latest + * NOTE! This test updates the {@code doc/user/RouteRequest.md} document based on the latest * version of the code. The following is auto generated: *
      *
    • The configuration type table
    • diff --git a/src/test/java/org/opentripplanner/generate/doc/RouterConfigurationDocTest.java b/src/test/java/org/opentripplanner/generate/doc/RouterConfigurationDocTest.java index 90cdd9de975..77490c506fd 100644 --- a/src/test/java/org/opentripplanner/generate/doc/RouterConfigurationDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/RouterConfigurationDocTest.java @@ -5,8 +5,8 @@ import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; import static org.opentripplanner.framework.text.MarkdownFormatter.HEADER_3; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceJsonExample; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceParametersDetails; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceParametersTable; @@ -24,8 +24,8 @@ @GeneratesDocumentation public class RouterConfigurationDocTest { - private static final File TEMPLATE = new File(TEMPLATE_ROOT, "RouterConfiguration.md"); - private static final File OUT_FILE = new File(DOCS_ROOT, "RouterConfiguration.md"); + private static final File TEMPLATE = new File(TEMPLATE_PATH, "RouterConfiguration.md"); + private static final File OUT_FILE = new File(USER_DOC_PATH, "RouterConfiguration.md"); private static final String CONFIG_PATH = "standalone/config/" + ROUTER_CONFIG_FILENAME; private static final SkipNodes SKIP_NODES = SkipNodes @@ -40,7 +40,7 @@ public class RouterConfigurationDocTest { .build(); /** - * NOTE! This test updates the {@code docs/Configuration.md} document based on the latest + * NOTE! This test updates the {@code doc/user/Configuration.md} document based on the latest * version of the code. The following is auto generated: *
        *
      • The configuration type table
      • diff --git a/src/test/java/org/opentripplanner/generate/doc/RoutingModeDocTest.java b/src/test/java/org/opentripplanner/generate/doc/RoutingModeDocTest.java index 0c6edc7e16b..188101e3b5d 100644 --- a/src/test/java/org/opentripplanner/generate/doc/RoutingModeDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/RoutingModeDocTest.java @@ -3,8 +3,8 @@ import static org.opentripplanner.framework.io.FileUtils.assertFileEquals; import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceSection; import java.io.File; @@ -19,8 +19,8 @@ @GeneratesDocumentation public class RoutingModeDocTest { - private static final File TEMPLATE = new File(TEMPLATE_ROOT, "RoutingModes.md"); - private static final File OUT_FILE = new File(DOCS_ROOT, "RoutingModes.md"); + private static final File TEMPLATE = new File(TEMPLATE_PATH, "RoutingModes.md"); + private static final File OUT_FILE = new File(USER_DOC_PATH, "RoutingModes.md"); @Test public void updateDocs() { diff --git a/src/test/java/org/opentripplanner/generate/doc/SiriAzureConfigDocTest.java b/src/test/java/org/opentripplanner/generate/doc/SiriAzureConfigDocTest.java index 374ac2b4bbb..fcf93770f38 100644 --- a/src/test/java/org/opentripplanner/generate/doc/SiriAzureConfigDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/SiriAzureConfigDocTest.java @@ -5,8 +5,8 @@ import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; import static org.opentripplanner.framework.text.MarkdownFormatter.HEADER_4; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceSection; import static org.opentripplanner.standalone.config.framework.json.JsonSupport.jsonNodeFromResource; @@ -25,8 +25,8 @@ @GeneratesDocumentation public class SiriAzureConfigDocTest { - private static final File TEMPLATE = new File(TEMPLATE_ROOT, "sandbox/siri/SiriAzureUpdater.md"); - private static final File OUT_FILE = new File(DOCS_ROOT, "sandbox/siri/SiriAzureUpdater.md"); + private static final File TEMPLATE = new File(TEMPLATE_PATH, "sandbox/siri/SiriAzureUpdater.md"); + private static final File OUT_FILE = new File(USER_DOC_PATH, "sandbox/siri/SiriAzureUpdater.md"); private static final String ROUTER_CONFIG_PATH = "standalone/config/" + ROUTER_CONFIG_FILENAME; private static final Set INCLUDE_UPDATERS = Set.of( @@ -37,7 +37,7 @@ public class SiriAzureConfigDocTest { public static final ObjectMapper mapper = new ObjectMapper(); /** - * NOTE! This test updates the {@code docs/sandbox/SiriUpdater.md} document based on the latest + * NOTE! This test updates the {@code doc/user/sandbox/SiriUpdater.md} document based on the latest * version of the code. */ @Test diff --git a/src/test/java/org/opentripplanner/generate/doc/SiriConfigDocTest.java b/src/test/java/org/opentripplanner/generate/doc/SiriConfigDocTest.java index 2474f37c402..71df3027c7d 100644 --- a/src/test/java/org/opentripplanner/generate/doc/SiriConfigDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/SiriConfigDocTest.java @@ -5,8 +5,8 @@ import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; import static org.opentripplanner.framework.text.MarkdownFormatter.HEADER_4; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceSection; import static org.opentripplanner.standalone.config.framework.json.JsonSupport.jsonNodeFromResource; @@ -25,8 +25,8 @@ @GeneratesDocumentation public class SiriConfigDocTest { - private static final File TEMPLATE = new File(TEMPLATE_ROOT, "sandbox/siri/SiriUpdater.md"); - private static final File OUT_FILE = new File(DOCS_ROOT, "sandbox/siri/SiriUpdater.md"); + private static final File TEMPLATE = new File(TEMPLATE_PATH, "sandbox/siri/SiriUpdater.md"); + private static final File OUT_FILE = new File(USER_DOC_PATH, "sandbox/siri/SiriUpdater.md"); private static final String ROUTER_CONFIG_PATH = "standalone/config/" + ROUTER_CONFIG_FILENAME; private static final Set INCLUDE_UPDATERS = Set.of("siri-et-updater", "siri-sx-updater"); @@ -34,7 +34,7 @@ public class SiriConfigDocTest { public static final ObjectMapper mapper = new ObjectMapper(); /** - * NOTE! This test updates the {@code docs/sandbox/SiriUpdater.md} document based on the latest + * NOTE! This test updates the {@code doc/user/sandbox/SiriUpdater.md} document based on the latest * version of the code. */ @Test diff --git a/src/test/java/org/opentripplanner/generate/doc/SiriGooglePubSubConfigDocTest.java b/src/test/java/org/opentripplanner/generate/doc/SiriGooglePubSubConfigDocTest.java index f5aa3e130ed..ce414b298d0 100644 --- a/src/test/java/org/opentripplanner/generate/doc/SiriGooglePubSubConfigDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/SiriGooglePubSubConfigDocTest.java @@ -5,8 +5,8 @@ import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; import static org.opentripplanner.framework.text.MarkdownFormatter.HEADER_4; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceSection; import static org.opentripplanner.standalone.config.framework.json.JsonSupport.jsonNodeFromResource; @@ -26,11 +26,11 @@ public class SiriGooglePubSubConfigDocTest { private static final File TEMPLATE = new File( - TEMPLATE_ROOT, + TEMPLATE_PATH, "sandbox/siri/SiriGooglePubSubUpdater.md" ); private static final File OUT_FILE = new File( - DOCS_ROOT, + USER_DOC_PATH, "sandbox/siri/SiriGooglePubSubUpdater.md" ); @@ -40,7 +40,7 @@ public class SiriGooglePubSubConfigDocTest { public static final ObjectMapper mapper = new ObjectMapper(); /** - * NOTE! This test updates the {@code docs/sandbox/SiriGooglePubSubUpdater.md} document based on the latest + * NOTE! This test updates the {@code doc/user/sandbox/SiriGooglePubSubUpdater.md} document based on the latest * version of the code. */ @Test diff --git a/src/test/java/org/opentripplanner/generate/doc/StopConsolidationDocTest.java b/src/test/java/org/opentripplanner/generate/doc/StopConsolidationDocTest.java index 62f76032a09..2861253aac7 100644 --- a/src/test/java/org/opentripplanner/generate/doc/StopConsolidationDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/StopConsolidationDocTest.java @@ -3,8 +3,8 @@ import static org.opentripplanner.framework.io.FileUtils.assertFileEquals; import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceSection; import static org.opentripplanner.standalone.config.framework.json.JsonSupport.jsonNodeFromResource; @@ -21,8 +21,8 @@ public class StopConsolidationDocTest { private static final String FILE_NAME = "StopConsolidation.md"; - private static final File TEMPLATE = new File(TEMPLATE_ROOT, FILE_NAME); - private static final File OUT_FILE = new File(DOCS_ROOT + "/sandbox", FILE_NAME); + private static final File TEMPLATE = new File(TEMPLATE_PATH, FILE_NAME); + private static final File OUT_FILE = new File(USER_DOC_PATH + "/sandbox", FILE_NAME); private static final String CONFIG_FILENAME = "standalone/config/build-config.json"; diff --git a/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java b/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java index 264d0d6850a..7a3f2969c4d 100644 --- a/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/UpdaterConfigDocTest.java @@ -5,8 +5,8 @@ import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; import static org.opentripplanner.framework.text.MarkdownFormatter.HEADER_4; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceSection; import static org.opentripplanner.standalone.config.framework.json.JsonSupport.jsonNodeFromResource; @@ -25,8 +25,8 @@ @GeneratesDocumentation public class UpdaterConfigDocTest { - private static final File TEMPLATE = new File(TEMPLATE_ROOT, "UpdaterConfig.md"); - private static final File OUT_FILE = new File(DOCS_ROOT, "UpdaterConfig.md"); + private static final File TEMPLATE = new File(TEMPLATE_PATH, "UpdaterConfig.md"); + private static final File OUT_FILE = new File(USER_DOC_PATH, "UpdaterConfig.md"); private static final String ROUTER_CONFIG_PATH = "standalone/config/" + ROUTER_CONFIG_FILENAME; private static final Set SKIP_UPDATERS = Set.of( @@ -41,7 +41,7 @@ public class UpdaterConfigDocTest { public static final ObjectMapper mapper = new ObjectMapper(); /** - * NOTE! This test updates the {@code docs/Configuration.md} document based on the latest + * NOTE! This test updates the {@code doc/user/Configuration.md} document based on the latest * version of the code. The following is auto generated: *
          *
        • The configuration type table
        • diff --git a/src/test/java/org/opentripplanner/generate/doc/VehicleParkingDocTest.java b/src/test/java/org/opentripplanner/generate/doc/VehicleParkingDocTest.java index abc9ceee806..caffafdf72b 100644 --- a/src/test/java/org/opentripplanner/generate/doc/VehicleParkingDocTest.java +++ b/src/test/java/org/opentripplanner/generate/doc/VehicleParkingDocTest.java @@ -5,8 +5,8 @@ import static org.opentripplanner.framework.io.FileUtils.readFile; import static org.opentripplanner.framework.io.FileUtils.writeFile; import static org.opentripplanner.framework.text.MarkdownFormatter.HEADER_4; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.DOCS_ROOT; -import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_ROOT; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.TEMPLATE_PATH; +import static org.opentripplanner.generate.doc.framework.DocsTestConstants.USER_DOC_PATH; import static org.opentripplanner.generate.doc.framework.TemplateUtil.replaceSection; import static org.opentripplanner.standalone.config.framework.json.JsonSupport.jsonNodeFromResource; @@ -23,8 +23,8 @@ @GeneratesDocumentation public class VehicleParkingDocTest { - private static final File TEMPLATE = new File(TEMPLATE_ROOT, "VehicleParking.md"); - private static final File OUT_FILE = new File(DOCS_ROOT + "/sandbox", "VehicleParking.md"); + private static final File TEMPLATE = new File(TEMPLATE_PATH, "VehicleParking.md"); + private static final File OUT_FILE = new File(USER_DOC_PATH + "/sandbox", "VehicleParking.md"); private static final String ROUTER_CONFIG_PATH = "standalone/config/" + ROUTER_CONFIG_FILENAME; private static final SkipNodes SKIP_NODES = SkipNodes.of().build(); diff --git a/src/test/java/org/opentripplanner/generate/doc/framework/DocsTestConstants.java b/src/test/java/org/opentripplanner/generate/doc/framework/DocsTestConstants.java index d7a67a3590b..a7f7afa806a 100644 --- a/src/test/java/org/opentripplanner/generate/doc/framework/DocsTestConstants.java +++ b/src/test/java/org/opentripplanner/generate/doc/framework/DocsTestConstants.java @@ -9,8 +9,9 @@ */ public interface DocsTestConstants { Logger LOG = LoggerFactory.getLogger(DocsTestConstants.class); - File TEMPLATE_ROOT = new File("doc-templates"); - File DOCS_ROOT = new File("docs"); + File DOC_ROOT = new File("doc"); + File TEMPLATE_PATH = new File(DOC_ROOT, "templates"); + File USER_DOC_PATH = new File(DOC_ROOT, "user"); /** * This method return {@code true} if the /docs directory is available. If not, a warning is @@ -18,14 +19,14 @@ public interface DocsTestConstants { * annotation. */ static boolean docsExistOrWarn() { - if (DOCS_ROOT.exists()) { + if (USER_DOC_PATH.exists()) { return true; } LOG.warn( """ SKIP TEST - '/docs' NOT FOUND - The docs/doc-templates directory might not be available if you run the tests outside the + The doc/templates directory might not be available if you run the tests outside the root of the projects. This may happen if the project root is not the working directory, if you run tests using jar files or in a Maven multi-module project. diff --git a/src/test/java/org/opentripplanner/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/service/realtimevehicles/internal/DefaultRealtimeVehicleServiceTest.java b/src/test/java/org/opentripplanner/service/realtimevehicles/internal/DefaultRealtimeVehicleServiceTest.java new file mode 100644 index 00000000000..a1c9dddaab0 --- /dev/null +++ b/src/test/java/org/opentripplanner/service/realtimevehicles/internal/DefaultRealtimeVehicleServiceTest.java @@ -0,0 +1,55 @@ +package org.opentripplanner.service.realtimevehicles.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.opentripplanner.framework.geometry.WgsCoordinate.GREENWICH; +import static org.opentripplanner.transit.model._data.TransitModelForTest.route; +import static org.opentripplanner.transit.model._data.TransitModelForTest.tripPattern; + +import java.time.Instant; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.opentripplanner.service.realtimevehicles.model.RealtimeVehicle; +import org.opentripplanner.transit.model._data.TransitModelForTest; +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.service.DefaultTransitService; +import org.opentripplanner.transit.service.TransitModel; + +class DefaultRealtimeVehicleServiceTest { + + private static final Route ROUTE = route("r1").build(); + private static final TransitModelForTest MODEL = TransitModelForTest.of(); + private static final StopPattern STOP_PATTERN = TransitModelForTest.stopPattern( + MODEL.stop("1").build(), + MODEL.stop("2").build() + ); + private static final TripPattern ORIGINAL = tripPattern("original", ROUTE) + .withStopPattern(STOP_PATTERN) + .build(); + private static final Instant TIME = Instant.ofEpochSecond(1000); + private static final List VEHICLES = List.of( + RealtimeVehicle.builder().withTime(TIME).withCoordinates(GREENWICH).build() + ); + + @Test + void originalPattern() { + var service = new DefaultRealtimeVehicleService(new DefaultTransitService(new TransitModel())); + service.setRealtimeVehicles(ORIGINAL, VEHICLES); + var updates = service.getRealtimeVehicles(ORIGINAL); + assertEquals(VEHICLES, updates); + } + + @Test + void realtimeAddedPattern() { + var service = new DefaultRealtimeVehicleService(new DefaultTransitService(new TransitModel())); + var realtimePattern = tripPattern("realtime-added", ROUTE) + .withStopPattern(STOP_PATTERN) + .withOriginalTripPattern(ORIGINAL) + .withCreatedByRealtimeUpdater(true) + .build(); + service.setRealtimeVehicles(realtimePattern, VEHICLES); + var updates = service.getRealtimeVehicles(ORIGINAL); + assertEquals(VEHICLES, updates); + } +} diff --git a/src/test/java/org/opentripplanner/standalone/config/ExampleConfigTest.java b/src/test/java/org/opentripplanner/standalone/config/ExampleConfigTest.java index b250126c019..12b8749c1fe 100644 --- a/src/test/java/org/opentripplanner/standalone/config/ExampleConfigTest.java +++ b/src/test/java/org/opentripplanner/standalone/config/ExampleConfigTest.java @@ -22,7 +22,7 @@ @GeneratesDocumentation public class ExampleConfigTest { - @FilePatternSource(pattern = "docs/examples/**/" + ROUTER_CONFIG_FILENAME) + @FilePatternSource(pattern = "doc/user/examples/**/" + ROUTER_CONFIG_FILENAME) @ParameterizedTest(name = "Check validity of {0}") void routerConfig(Path filename) { testConfig(filename, a -> new RouterConfig(a, true)); @@ -30,7 +30,8 @@ void routerConfig(Path filename) { @FilePatternSource( pattern = { - "docs/examples/**/" + BUILD_CONFIG_FILENAME, "test/performance/**/" + BUILD_CONFIG_FILENAME, + "doc/user/examples/**/" + BUILD_CONFIG_FILENAME, + "test/performance/**/" + BUILD_CONFIG_FILENAME, } ) @ParameterizedTest(name = "Check validity of {0}") @@ -45,7 +46,9 @@ void speedTestConfig(Path filename) { } @FilePatternSource( - pattern = { "test/performance/**/otp-config.json", "docs/examples/**/" + OTP_CONFIG_FILENAME } + pattern = { + "test/performance/**/otp-config.json", "doc/user/examples/**/" + OTP_CONFIG_FILENAME, + } ) @ParameterizedTest(name = "Check validity of {0}") void otpConfig(Path filename) { diff --git a/src/test/java/org/opentripplanner/test/support/FilePatternArgumentsProvider.java b/src/test/java/org/opentripplanner/test/support/FilePatternArgumentsProvider.java index 35869a9094e..c084a81a73f 100644 --- a/src/test/java/org/opentripplanner/test/support/FilePatternArgumentsProvider.java +++ b/src/test/java/org/opentripplanner/test/support/FilePatternArgumentsProvider.java @@ -19,7 +19,7 @@ /** * This annotation processor allows you to provide a file pattern like - * "docs/examples/**\/build-config.json" as the input for a JUnit + * "doc/user/examples/**\/build-config.json" as the input for a JUnit * {@link org.junit.jupiter.params.ParameterizedTest}. *

          * Check the usages of {@link FilePatternSource} to see examples for how to use. 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 b9eb07ec1b0..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,6 +19,7 @@ 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; @@ -38,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; @@ -185,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) { @@ -308,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/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