From d57a298ba07652304fb5cb6e32c04b1df460e83c Mon Sep 17 00:00:00 2001 From: Alec Georgoff Date: Thu, 18 Jun 2026 12:41:24 -0700 Subject: [PATCH 01/15] Move react-intl to peer dependencies --- packages/timetable/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/timetable/package.json b/packages/timetable/package.json index d16017e59..ccc069b9c 100644 --- a/packages/timetable/package.json +++ b/packages/timetable/package.json @@ -11,13 +11,13 @@ "private": false, "peerDependencies": { "react": "catalog:", + "react-intl": "catalog:", "styled-components": "catalog:" }, "scripts": { "tsc": "tsc" }, "dependencies": { - "react-intl": "catalog:", "toposort": "^2.0.2" }, "devDependencies": { From ed2a0069846b427abf338e648f4161936106bdd9 Mon Sep 17 00:00:00 2001 From: Alec Georgoff Date: Thu, 18 Jun 2026 12:45:31 -0700 Subject: [PATCH 02/15] fix: Add classnames to timetable table components --- packages/timetable/src/index.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/timetable/src/index.tsx b/packages/timetable/src/index.tsx index a88b2715b..68774f7e0 100644 --- a/packages/timetable/src/index.tsx +++ b/packages/timetable/src/index.tsx @@ -314,21 +314,21 @@ const TimeTable = (props: TimeTableProps): JSX.Element => { }, [allTrips, comparator]); return ( - - +
+ {(showBlockId ? [{ id: "blockIdHeader", name: "Block ID" }] : []) .concat(filteredPatternStops) .map((s, index) => { return ( - ); })} - + {timetableTrips.map((t, index) => { const rowValues: string[] = showBlockId ? [t.blockId] : []; filteredPatternStops.forEach(patternStop => { @@ -348,9 +348,11 @@ const TimeTable = (props: TimeTableProps): JSX.Element => { }); return ( - + {rowValues.map((r, rowIndex) => ( - + ))} ); From b3eccbb743694f43476b576c43b963224d59d495 Mon Sep 17 00:00:00 2001 From: Alec Georgoff Date: Tue, 23 Jun 2026 12:48:19 -0700 Subject: [PATCH 03/15] Update snapshots --- .../__snapshots__/Timetable.story.tsx.snap | 144 +++++++++--------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/packages/timetable/src/stories/__snapshots__/Timetable.story.tsx.snap b/packages/timetable/src/stories/__snapshots__/Timetable.story.tsx.snap index f56e14bd7..089e909ea 100644 --- a/packages/timetable/src/stories/__snapshots__/Timetable.story.tsx.snap +++ b/packages/timetable/src/stories/__snapshots__/Timetable.story.tsx.snap @@ -1,199 +1,199 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`Timetable Default smoke-test 1`] = ` -
+ {s.name}
{r} + {r} +
- + - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - + - - - - - - - - - - - - - - + - - - - - - - - - - - - From ecf8ad77c067475d414c859060b9b2db71af39b9 Mon Sep 17 00:00:00 2001 From: miles-grant-ibigroup Date: Wed, 24 Jun 2026 11:57:17 -0400 Subject: [PATCH 04/15] fix: avoid name with commas being parsed as coordinate --- .../core-utils/src/__tests__/__snapshots__/query.js.snap | 4 ++++ packages/core-utils/src/__tests__/query.js | 7 +++++++ packages/core-utils/src/query.js | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/core-utils/src/__tests__/__snapshots__/query.js.snap b/packages/core-utils/src/__tests__/__snapshots__/query.js.snap index 7a77e7ccc..2f3ae17c3 100644 --- a/packages/core-utils/src/__tests__/__snapshots__/query.js.snap +++ b/packages/core-utils/src/__tests__/__snapshots__/query.js.snap @@ -263,6 +263,10 @@ exports[`query parseLocationString should return location with coordinates as na exports[`query parseLocationString should return null for null input 1`] = `null`; +exports[`query parseLocationString should return null with name as name with no coords (but with commas) for input 1`] = `null`; + +exports[`query parseLocationString should return null with name as name with no coords for input 1`] = `null`; + exports[`query planParamsToQuery should parse a depart at query 1`] = ` { "bannedRoutes": "897ABC", diff --git a/packages/core-utils/src/__tests__/query.js b/packages/core-utils/src/__tests__/query.js index 8a013422d..559f1286c 100644 --- a/packages/core-utils/src/__tests__/query.js +++ b/packages/core-utils/src/__tests__/query.js @@ -218,6 +218,13 @@ describe("query", () => { parseLocationString("33.983929829,-87.3892387982") ).toMatchSnapshot(); }); + + it("should return null with name as name with no coords for input", () => { + expect(parseLocationString("1 cool street")).toMatchSnapshot(); + }); + it("should return null with name as name with no coords (but with commas) for input", () => { + expect(parseLocationString("148, se street")).toMatchSnapshot(); + }); }); describe("planParamsToQuery", () => { diff --git a/packages/core-utils/src/query.js b/packages/core-utils/src/query.js index 0cc1f8446..ac5e200f1 100644 --- a/packages/core-utils/src/query.js +++ b/packages/core-utils/src/query.js @@ -265,7 +265,7 @@ export function parseLocationString(value) { ? stringToCoords(parts[1]) : stringToCoords(parts[0]); const name = parts[1] ? parts[0] : coordsToString(coordinates); - return coordinates.length === 2 + return coordinates.length === 2 && coordinates.every(c => !!c) ? { name: name || null, lat: coordinates[0] || null, From 4fe586e72b35b37e8f10eba5684db56170a8991b Mon Sep 17 00:00:00 2001 From: miles-grant-ibigroup Date: Wed, 24 Jun 2026 13:49:44 -0400 Subject: [PATCH 05/15] fix: notify user when geocoder fails --- packages/core-utils/src/query.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-utils/src/query.js b/packages/core-utils/src/query.js index ac5e200f1..75aba6338 100644 --- a/packages/core-utils/src/query.js +++ b/packages/core-utils/src/query.js @@ -247,7 +247,7 @@ async function getFirstGeocodeResult( if (firstResult) { return geocoder.getLocationFromGeocodedFeature(firstResult); } - return null; + return { name: `(Geocoder failure) ${text}` }; }); } From e602f5282b910f1a3691d9516131c5f85ed48c8c Mon Sep 17 00:00:00 2001 From: Alec Georgoff Date: Wed, 24 Jun 2026 15:21:08 -0700 Subject: [PATCH 06/15] feat: Add closed stops functionality --- packages/timetable/src/index.tsx | 31 +++++++++++++------ .../timetable/src/stories/Timetable.story.tsx | 23 +++++++++++++- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/packages/timetable/src/index.tsx b/packages/timetable/src/index.tsx index 68774f7e0..187c9909b 100644 --- a/packages/timetable/src/index.tsx +++ b/packages/timetable/src/index.tsx @@ -15,9 +15,10 @@ const TBody = styled.tbody` white-space: nowrap; `; -const TH = styled.th` +const TH = styled.th<{ closed?: boolean }>` min-width: ${COLUMN_WIDTH}; padding: 1rem; + text-decoration: ${props => (props.closed ? "line-through" : "")}; `; const TR = styled.tr` @@ -25,8 +26,9 @@ const TR = styled.tr` vertical-align: middle; `; -const TD = styled.td` +const TD = styled.td<{ closed?: boolean }>` padding: 1rem; + text-decoration: ${props => (props.closed ? "line-through" : "")}; `; interface PatternStop { @@ -176,6 +178,8 @@ interface TimeTableProps { route: Route; /** Whether to show only timepoint stops in the timetable */ timepointsOnly: boolean; + /** A set of gtfsId values for stops that are closed and should be shown with strikethrough */ + closedStops?: Set; /** If the topological sort of the stop IDs fails for any reason, a `false` value here * will cause the timetable to use a fallback "naive" stop sorting */ @@ -194,6 +198,7 @@ interface TimeTableProps { const TimeTable = (props: TimeTableProps): JSX.Element => { const { + closedStops, directionId, errorOnStopSorting, includeDwellStops, @@ -321,7 +326,12 @@ const TimeTable = (props: TimeTableProps): JSX.Element => { .concat(filteredPatternStops) .map((s, index) => { return ( - ); @@ -330,11 +340,14 @@ const TimeTable = (props: TimeTableProps): JSX.Element => { {timetableTrips.map((t, index) => { - const rowValues: string[] = showBlockId ? [t.blockId] : []; + const rowValues: { closed: boolean; value: string }[] = showBlockId + ? [{ closed: false, value: t.blockId }] + : []; filteredPatternStops.forEach(patternStop => { const stopDetail = t.stops.get(patternStop.id); - rowValues.push( - stopDetail + rowValues.push({ + closed: closedStops?.has(patternStop.id) || false, + value: stopDetail ? intl ? intl.formatTime(stopDetail.time) : stopDetail.time.toLocaleTimeString("en-us", { @@ -344,14 +357,14 @@ const TimeTable = (props: TimeTableProps): JSX.Element => { minute: "2-digit" }) : "-" - ); + }); }); return ( {rowValues.map((r, rowIndex) => ( - ))} diff --git a/packages/timetable/src/stories/Timetable.story.tsx b/packages/timetable/src/stories/Timetable.story.tsx index d3c4a76a7..87e990dfa 100644 --- a/packages/timetable/src/stories/Timetable.story.tsx +++ b/packages/timetable/src/stories/Timetable.story.tsx @@ -1,5 +1,6 @@ // eslint-disable-next-line prettier/prettier import type { Meta, StoryObj } from "@storybook/react-vite" +import React from "react"; import TimeTable from ".." @@ -7,13 +8,33 @@ import twinCitiesRouteMock from "../../__mocks__/route-mock.json" const meta = { component: TimeTable, - title: "Timetable" + title: "Timetable", + argTypes: { + closedStops: { + options: ["Grand St NE & 29th Ave NE (Direction: 1)", "46th St & I-35W (Direction: 0)"], + control: { type: "check" }, + mapping: { + "Grand St NE & 29th Ave NE (Direction: 1)": "2:14634", + "46th St & I-35W (Direction: 0)": "2:53545" + } + }, + directionId: { + control: { type: "radio" }, + options: [0, 1] + } + } } satisfies Meta; export default meta; type Story = StoryObj; export const Default: Story = { + // eslint-disable-next-line react/display-name + render: (args) => { + const { closedStops } = args; + // eslint-disable-next-line react/jsx-props-no-spreading + return + }, args: { directionId: 0, route: twinCitiesRouteMock.data.route, From 73e89429a7ca3ecc8e575dfdf58197b4c23ad2b1 Mon Sep 17 00:00:00 2001 From: Alec Georgoff Date: Wed, 24 Jun 2026 15:22:59 -0700 Subject: [PATCH 07/15] Update snapshots --- .../__snapshots__/Timetable.story.tsx.snap | 104 +++++++++--------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/packages/timetable/src/stories/__snapshots__/Timetable.story.tsx.snap b/packages/timetable/src/stories/__snapshots__/Timetable.story.tsx.snap index 089e909ea..e0e87a1ac 100644 --- a/packages/timetable/src/stories/__snapshots__/Timetable.story.tsx.snap +++ b/packages/timetable/src/stories/__snapshots__/Timetable.story.tsx.snap @@ -6,67 +6,67 @@ exports[`Timetable Default smoke-test 1`] = ` > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 21a24e6becd3fc61ac906e7230757d100c054685 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 25 Jun 2026 14:08:29 +0000 Subject: [PATCH 08/15] chore(release): 16.0.5 [skip ci] # [@opentripplanner/core-utils-v16.0.5](https://github.com/opentripplanner/otp-ui/compare/@opentripplanner/core-utils-v16.0.4...@opentripplanner/core-utils-v16.0.5) (2026-06-25) ### Bug Fixes * avoid name with commas being parsed as coordinate ([ecf8ad7](https://github.com/opentripplanner/otp-ui/commit/ecf8ad77c067475d414c859060b9b2db71af39b9)) * notify user when geocoder fails ([4fe586e](https://github.com/opentripplanner/otp-ui/commit/4fe586e72b35b37e8f10eba5684db56170a8991b)) --- packages/core-utils/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-utils/package.json b/packages/core-utils/package.json index 931592c3f..f19a64993 100644 --- a/packages/core-utils/package.json +++ b/packages/core-utils/package.json @@ -1,6 +1,6 @@ { "name": "@opentripplanner/core-utils", - "version": "16.0.4", + "version": "16.0.5", "description": "Core functionality that is shared among numerous UI components", "engines": { "node": ">=13" From e997c60d45aec42fa40f3142fc24c5123d153c69 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 26 Jun 2026 21:11:22 +0000 Subject: [PATCH 09/15] chore(release): 1.0.1 [skip ci] # [@opentripplanner/timetable-v1.0.1](https://github.com/opentripplanner/otp-ui/compare/@opentripplanner/timetable-v1.0.0...@opentripplanner/timetable-v1.0.1) (2026-06-26) ### Bug Fixes * Add classnames to timetable table components ([ed2a006](https://github.com/opentripplanner/otp-ui/commit/ed2a0069846b427abf338e648f4161936106bdd9)) --- packages/timetable/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/timetable/package.json b/packages/timetable/package.json index ccc069b9c..229eb5dd7 100644 --- a/packages/timetable/package.json +++ b/packages/timetable/package.json @@ -1,6 +1,6 @@ { "name": "@opentripplanner/timetable", - "version": "1.0.0", + "version": "1.0.1", "description": "A component to display a timetable for a given route pattern", "main": "lib/index.js", "module": "esm/index.js", From 0abd414ebf6bfcd677601492d89d10506a4f2dd6 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 26 Jun 2026 21:30:09 +0000 Subject: [PATCH 10/15] chore(release): 1.1.0 [skip ci] # [@opentripplanner/timetable-v1.1.0](https://github.com/opentripplanner/otp-ui/compare/@opentripplanner/timetable-v1.0.1...@opentripplanner/timetable-v1.1.0) (2026-06-26) ### Features * Add closed stops functionality ([e602f52](https://github.com/opentripplanner/otp-ui/commit/e602f5282b910f1a3691d9516131c5f85ed48c8c)) --- packages/timetable/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/timetable/package.json b/packages/timetable/package.json index 229eb5dd7..904a6f799 100644 --- a/packages/timetable/package.json +++ b/packages/timetable/package.json @@ -1,6 +1,6 @@ { "name": "@opentripplanner/timetable", - "version": "1.0.1", + "version": "1.1.0", "description": "A component to display a timetable for a given route pattern", "main": "lib/index.js", "module": "esm/index.js", From ee63ce8a107990e4781d25e4d860994761dbc8da Mon Sep 17 00:00:00 2001 From: danielhep Date: Mon, 27 Apr 2026 15:28:25 -0700 Subject: [PATCH 11/15] chore: upgrade TS target to es2020, fix dependencies, add missing type deps - Set TS compile target to es2020 - Remove dep hoisting, add pnpm-workspace.yaml config - Add missing type dependencies to packages without strict-mode branches - Fix husky setup --- pnpm-lock.yaml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e414afbfa..c6f256c7b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5261,6 +5261,7 @@ packages: conventional-changelog-core@5.0.1: resolution: {integrity: sha512-Rvi5pH+LvgsqGwZPZ3Cq/tz4ty7mjijhr3qR4m9IBXNbxGGYgTVVO+duXzz9aArmHxFtwZ+LRkrNIMDQzgoY4A==} engines: {node: '>=14'} + deprecated: Deprecated and no longer maintained. Please use conventional-changelog instead. conventional-changelog-preset-loader@3.0.0: resolution: {integrity: sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA==} @@ -6455,7 +6456,7 @@ packages: git-raw-commits@3.0.0: resolution: {integrity: sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw==} engines: {node: '>=14'} - deprecated: This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead. + deprecated: Deprecated and no longer maintained. Use @conventional-changelog/git-client instead. hasBin: true git-remote-origin-url@2.0.0: @@ -6465,7 +6466,7 @@ packages: git-semver-tags@5.0.1: resolution: {integrity: sha512-hIvOeZwRbQ+7YEUmCkHqo8FOLQZCEn18yevLHADlFPZY02KJGsu5FZt9YW/lybfK2uhWFI7Qg/07LekJiTv7iA==} engines: {node: '>=14'} - deprecated: This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead. + deprecated: Deprecated and no longer maintained. Use @conventional-changelog/git-client instead. hasBin: true git-up@6.0.0: @@ -20083,7 +20084,7 @@ snapshots: dependencies: find-up-simple: 1.0.0 read-pkg: 9.0.1 - type-fest: 4.26.1 + type-fest: 4.41.0 read-package-up@12.0.0: dependencies: @@ -21779,11 +21780,11 @@ snapshots: wait-on@7.2.0: dependencies: - axios: 1.7.7 + axios: 1.15.2 joi: 17.13.3 - lodash: 4.17.21 + lodash: 4.18.1 minimist: 1.2.8 - rxjs: 7.8.1 + rxjs: 7.8.2 transitivePeerDependencies: - debug From 8d8888c707467fd23cc9cd2c5eb568c82e88190f Mon Sep 17 00:00:00 2001 From: danielhep Date: Tue, 28 Apr 2026 12:25:02 -0700 Subject: [PATCH 12/15] add more missing deps to sub packages --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c6f256c7b..ec7ac3de2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17979,7 +17979,7 @@ snapshots: jest-haste-map@30.2.0: dependencies: '@jest/types': 30.2.0 - '@types/node': 15.14.9 + '@types/node': 25.6.0 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 From 5ae2266368ff3812be62d724849cb341a4b0a3f6 Mon Sep 17 00:00:00 2001 From: danielhep Date: Tue, 28 Apr 2026 13:28:27 -0700 Subject: [PATCH 13/15] add missing playwright dep --- pnpm-lock.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ec7ac3de2..762eb2241 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12804,7 +12804,7 @@ snapshots: js-yaml: 4.1.0 libnpmpublish: 9.0.9 load-json-file: 6.2.0 - lodash: 4.17.21 + lodash: 4.18.1 make-dir: 4.0.0 minimatch: 3.0.5 multimatch: 5.0.0 @@ -17422,7 +17422,7 @@ snapshots: cli-width: 3.0.0 external-editor: 3.1.0 figures: 3.2.0 - lodash: 4.17.21 + lodash: 4.18.1 mute-stream: 0.0.8 ora: 5.4.1 run-async: 2.4.1 @@ -18355,7 +18355,7 @@ snapshots: junit-report-builder@5.1.1: dependencies: - lodash: 4.17.21 + lodash: 4.18.1 make-dir: 3.1.0 xmlbuilder: 15.1.1 From 8bb76729ab0c0f6d25ce2c6c4d14b834dce1689f Mon Sep 17 00:00:00 2001 From: danielhep Date: Tue, 28 Apr 2026 13:36:09 -0700 Subject: [PATCH 14/15] add wait-on dep --- pnpm-lock.yaml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 762eb2241..a3856f8c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4738,6 +4738,9 @@ packages: axios@1.15.2: resolution: {integrity: sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==} + axios@1.15.2: + resolution: {integrity: sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==} + axios@1.7.7: resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} @@ -14995,6 +14998,14 @@ snapshots: transitivePeerDependencies: - debug + axios@1.15.2: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.5 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + axios@1.7.7: dependencies: follow-redirects: 1.15.9 @@ -21798,6 +21809,16 @@ snapshots: transitivePeerDependencies: - debug + wait-on@9.0.5: + dependencies: + axios: 1.15.2 + joi: 18.1.2 + lodash: 4.18.1 + minimist: 1.2.8 + rxjs: 7.8.2 + transitivePeerDependencies: + - debug + wait-port@0.2.14: dependencies: chalk: 2.4.2 From f626c74be2b256d93f8ef13f3d324ae012cc8dde Mon Sep 17 00:00:00 2001 From: danielhep Date: Mon, 27 Apr 2026 15:31:34 -0700 Subject: [PATCH 15/15] feat(transitive-overlay): enable strict mode - Enable TypeScript strict mode - Add TransitiveSegment type to @opentripplanner/types - Make stop_name optional in TransitiveStop - Add missing type dependencies --- packages/transitive-overlay/package.json | 13 +- packages/transitive-overlay/src/index.tsx | 53 +++-- .../transitive-overlay/src/route-layers.ts | 14 +- packages/transitive-overlay/src/util.ts | 101 +++++---- packages/transitive-overlay/tsconfig.json | 3 +- packages/types/package.json | 3 + packages/types/src/index.ts | 27 +-- pnpm-lock.yaml | 195 +++++++++--------- 8 files changed, 224 insertions(+), 185 deletions(-) diff --git a/packages/transitive-overlay/package.json b/packages/transitive-overlay/package.json index 8083e4fea..f0345d62b 100644 --- a/packages/transitive-overlay/package.json +++ b/packages/transitive-overlay/package.json @@ -23,18 +23,19 @@ "@opentripplanner/building-blocks": "workspace:^", "@opentripplanner/core-utils": "workspace:^", "@opentripplanner/itinerary-body": "workspace:^", - "@turf/bbox": "^6.5.0", - "@turf/bearing": "^6.5.0", - "@turf/destination": "^6.5.0", - "@turf/distance": "^6.5.0", - "@turf/line-arc": "^6.5.0", + "@turf/bbox": "^7.3.5", + "@turf/bearing": "^7.3.5", + "@turf/destination": "^7.3.5", + "@turf/distance": "^7.3.5", + "@turf/line-arc": "^7.3.5", "@turf/line-distance": "^4.7.3", - "@turf/midpoint": "^6.5.0", + "@turf/midpoint": "^7.3.5", "lodash.isequal": "^4.5.0" }, "devDependencies": { "@opentripplanner/endpoints-overlay": "workspace:^", "@opentripplanner/types": "workspace:^", + "@types/mapbox__polyline": "^1.0.5", "maplibre-gl": "catalog:" }, "peerDependencies": { diff --git a/packages/transitive-overlay/src/index.tsx b/packages/transitive-overlay/src/index.tsx index b8f7cb255..bda2f5c61 100644 --- a/packages/transitive-overlay/src/index.tsx +++ b/packages/transitive-overlay/src/index.tsx @@ -14,6 +14,12 @@ import colors from "@opentripplanner/building-blocks"; import bbox from "@turf/bbox"; +import { + FeatureCollection, + GeoJsonProperties, + Geometry, + LineString +} from "geojson"; import { getRouteLayerLayout, patternToRouteFeature } from "./route-layers"; import { drawArc, getFromToAnchors, itineraryToTransitive } from "./util"; import routeArrow from "./images/route_arrow.png"; @@ -35,7 +41,7 @@ import capsule16 from "./images/16.png"; import capsule17 from "./images/17.png"; import rectangle from "./images/square.png"; -const CAPSULES = { +const CAPSULES: Record = { 3: capsule3, 4: capsule4, 5: capsule5, @@ -54,7 +60,7 @@ const CAPSULES = { }; // These are based on the sprites in the image folder -const WIDTH_IMAGE_SIZES = { +const WIDTH_IMAGE_SIZES: Record = { 6: 1283, 7: 1450, 8: 1617, @@ -79,7 +85,7 @@ const { blue, grey, red } = colors; const MICROMOBILITY_ORANGE = "#f5a729"; // TODO: BETTER COLORS -const modeColorMap = { +const modeColorMap: Record = { CAR: grey[500], BICYCLE: red[600], SCOOTER: MICROMOBILITY_ORANGE, @@ -199,7 +205,7 @@ const TransitiveCanvasOverlay = ({ function generateCapsulePadding( width: number - ): [number, number, number, number] { + ): [number, number, number, number] | undefined { // Low widths have no padding if (!WIDTH_IMAGE_SIZES[width]) { return undefined; @@ -266,12 +272,12 @@ const TransitiveCanvasOverlay = ({ }); useEffect(() => { - loadImages(map, mapImages); + map && loadImages(map, mapImages); }, []); const geojson: GeoJSON.FeatureCollection< GeoJSON.Geometry, - Record + GeoJsonProperties > = { type: "FeatureCollection", features: transitiveData @@ -293,22 +299,27 @@ const TransitiveCanvasOverlay = ({ ...(transitiveData.journeys || []).flatMap( (journey: TransitiveJourney) => journey.segments - .filter(segment => segment.streetEdges?.length > 0) + .filter( + segment => + segment.streetEdges?.length && + segment.streetEdges?.length > 0 + ) .map(segment => ({ ...segment, - geometries: segment.streetEdges.map(edge => { - return transitiveData.streetEdges.find( - entry => entry.edge_id === edge + geometries: segment.streetEdges?.flatMap(edge => { + const entry = transitiveData.streetEdges.find( + e => e.edge_id === edge ); + return entry ? [entry] : []; }) })) - .flatMap(segment => { - return segment.geometries.map(geometry => { + .flatMap(segment => + (segment.geometries ?? []).map(geometry => { const straight = polyline.toGeoJSON( geometry.geometry.points ); return { - type: "Feature", + type: "Feature" as const, properties: { type: "street-edge", color: @@ -318,9 +329,9 @@ const TransitiveCanvasOverlay = ({ mode: segment.type }, geometry: segment.arc ? drawArc(straight) : straight - } as GeoJSON.Feature; - }); - }) + }; + }) + ) ), // Extract the first and last stops of each transit segment for display. ...(transitiveData.journeys || []) @@ -328,7 +339,7 @@ const TransitiveCanvasOverlay = ({ .filter(segment => segment.type === "TRANSIT") .map(segment => transitiveData.patterns.find( - p => p.pattern_id === segment.patterns[0]?.pattern_id + p => p.pattern_id === segment.patterns?.[0]?.pattern_id ) ) .filter(pattern => !!pattern) @@ -346,10 +357,10 @@ const TransitiveCanvasOverlay = ({ stop => ({ type: "Feature", - properties: { name: stop.stop_name, type: "stop" }, + properties: { name: stop?.stop_name, type: "stop" }, geometry: { type: "Point", - coordinates: [stop.stop_lon, stop.stop_lat] + coordinates: [stop?.stop_lon, stop?.stop_lat] } } as GeoJSON.Feature) ), @@ -362,7 +373,9 @@ const TransitiveCanvasOverlay = ({ : [] }; - const zoomToGeoJSON = geoJson => { + const zoomToGeoJSON = ( + geoJson: FeatureCollection | LineString + ) => { if (!boundsFitting) return; const b = bbox(geoJson); const bounds: [number, number, number, number] = [b[0], b[1], b[2], b[3]]; diff --git a/packages/transitive-overlay/src/route-layers.ts b/packages/transitive-overlay/src/route-layers.ts index c6cd7698b..6966157f4 100644 --- a/packages/transitive-overlay/src/route-layers.ts +++ b/packages/transitive-overlay/src/route-layers.ts @@ -18,21 +18,23 @@ export function patternToRouteFeature( // Concatenate geometries (arrays of coordinates) to help maplibre spread out labels (not perfect). const concatenatedLines = pattern.stops .map(stop => stop.geometry) - .filter(geometry => !!geometry) - .reduce((result, geom, index) => { + .filter((geo): geo is string => !!geo) + .reduce<[number, number][]>((result, geom, index) => { const coords = polyline.decode(geom); // Remove the first element (except for the first array) because it is a duplicate // of the last element of the previous array. if (index !== 0) coords.shift(); return result.concat(coords); }, []); - const routeName = route.route_short_name || route.route_long_name || ""; + const routeName = route?.route_short_name || route?.route_long_name || ""; const properties = { - color: route.route_color ? `#${route.route_color}` : blue[900], + color: route?.route_color ? `#${route.route_color}` : blue[900], name: routeName, - routeType: route.route_type, - textColor: route.route_text_color ? `#${route.route_text_color}` : grey[50], + routeType: route?.route_type, + textColor: route?.route_text_color + ? `#${route.route_text_color}` + : grey[50], type: "route" }; diff --git a/packages/transitive-overlay/src/util.ts b/packages/transitive-overlay/src/util.ts index 7bee3e738..956cf7082 100644 --- a/packages/transitive-overlay/src/util.ts +++ b/packages/transitive-overlay/src/util.ts @@ -1,9 +1,9 @@ +import bearing from "@turf/bearing"; +import destination from "@turf/destination"; +import distance from "@turf/distance"; import lineArc from "@turf/line-arc"; import lineDistance from "@turf/line-distance"; import midpoint from "@turf/midpoint"; -import destination from "@turf/destination"; -import bearing from "@turf/bearing"; -import distance from "@turf/distance"; import coreUtils from "@opentripplanner/core-utils"; import { getPlaceName } from "@opentripplanner/itinerary-body"; @@ -13,11 +13,15 @@ import { Leg, Place, TransitiveData, + TransitiveJourney, + TransitivePattern, TransitivePlace, + TransitiveRoute, TransitiveStop } from "@opentripplanner/types"; -import { IntlShape } from "react-intl"; import { PositionAnchor } from "maplibre-gl"; +import { IntlShape } from "react-intl"; +import { LineString } from "geojson"; const { getLegBounds, @@ -48,7 +52,7 @@ function stopToTransitive( return { stop_id: id, // Don't render this stop name if another one or similar exists. - stop_name: stopNameExists ? null : normalizedStopName, + stop_name: stopNameExists ? undefined : normalizedStopName, stop_lat: stop.lat, stop_lon: stop.lon }; @@ -58,7 +62,7 @@ function stopToTransitive( * Helper function to obtain the stop ID for an OTP2 or OTP1 place. */ function getStopId(place: Place) { - return typeof place.stop === "object" ? place.stop.id : place.stopId; + return (place.stop?.id ? place.stop.id : place.stopId) ?? ""; } /** @@ -70,7 +74,7 @@ function addStop( knownStopNames: Record ) { const stopId = getStopId(place); - if (!stops[stopId]) { + if (stopId && !stops[stopId]) { stops[stopId] = stopToTransitive(place, stopId, knownStopNames); } } @@ -99,13 +103,18 @@ export function getFromToAnchors( // Compute general direction of travel to better position from/to markers. let direction; - if (fromPlace && toPlace) { + if ( + fromPlace?.place_lat && + fromPlace.place_lon && + toPlace?.place_lat && + toPlace.place_lon + ) { const directionLat = fromPlace.place_lat < toPlace.place_lat ? "N" : "S"; const directionLon = fromPlace.place_lon < toPlace.place_lon ? "E" : "W"; direction = `${directionLat}${directionLon}`; } - let fromAnchor; - let toAnchor; + let fromAnchor: PositionAnchor = "top-left"; + let toAnchor: PositionAnchor = "top-left"; switch (direction) { case "NE": fromAnchor = "top-right"; @@ -231,7 +240,7 @@ export function itineraryToTransitive( } = options; const isFlexCheck = isFlexOverride || isFlex; - const tdata = { + const tdata: TransitiveData = { journeys: [], streetEdges: [], places: [], @@ -239,11 +248,11 @@ export function itineraryToTransitive( routes: [], stops: [] }; - const routes = {}; + const routes: Record = {}; const knownStopNames = {}; let patternId = 0; - const journey = { + const journey: TransitiveJourney = { journey_id: "itin", // This string is not shown in the UI journey_name: "Itinerary-derived Journey", @@ -280,13 +289,13 @@ export function itineraryToTransitive( "from", streetEdgeId, leg, - idx > 0 ? itin.legs[idx - 1] : null + idx > 0 ? itin.legs[idx - 1] : undefined ); const toPlaceId = getPlaceId( "to", streetEdgeId, leg, - idx < itin.legs.length - 1 ? itin.legs[idx + 1] : null, + idx < itin.legs.length - 1 ? itin.legs[idx + 1] : undefined, toVertexType ); let addFromPlace = false; @@ -362,7 +371,7 @@ export function itineraryToTransitive( const hasIntermediateStopGeometry = hasInterStopGeometry && leg.intermediateStops && - leg.interStopGeometry.length === leg.intermediateStops.length + 1; + leg.interStopGeometry?.length === leg.intermediateStops.length + 1; // Coordinates of the leg geometry, used to draw the stop marker on the line, // otherwise the logical stop is often times off the line. @@ -373,16 +382,18 @@ export function itineraryToTransitive( // create leg-specific pattern const ptnId = `ptn_${patternId}`; - const pattern = { + const pattern: TransitivePattern = { pattern_id: ptnId, - // This string is not shown in the UI pattern_name: `Pattern ${patternId}`, - route_id: routeId, + route_id: routeId ?? "", stops: [] }; // Add the "from" end of transit legs to the list of stops. - const fromStop = makeStop(leg.from, hasLegGeometry && legCoords[0]); + const fromStop = makeStop( + leg.from, + hasLegGeometry ? legCoords[0] : undefined + ); addStop(fromStop, newStops, knownStopNames); pattern.stops.push({ stop_id: getStopId(leg.from) }); @@ -397,18 +408,24 @@ export function itineraryToTransitive( leg.intermediateStops.forEach((stop, i) => { // FIXME: line up the coordinates of the stops so they appear on the line. addStop(stop, newStops, knownStopNames); - pattern.stops.push({ - // Intermediate stops are still exclusively using stopId. - stop_id: stop.stopId, - geometry: - hasIntermediateStopGeometry && leg.interStopGeometry[i].points - }); + // TODO: Should we be looking at using stop.stop.id? or the getStopId function? + if (stop.stopId) { + pattern.stops.push({ + // Intermediate stops are still exclusively using stopId. + stop_id: stop.stopId, + geometry: hasIntermediateStopGeometry + ? leg.interStopGeometry?.[i]?.points + : undefined + }); + } }); } // Add the "to" end of transit legs to the list of stops. // (Do not label stop names if they repeat.) - const lastCoord = hasLegGeometry && legCoords[legCoords.length - 1]; + const lastCoord = hasLegGeometry + ? legCoords[legCoords.length - 1] + : undefined; const modifiedToStop = makeStop( renameLegFlexStops(leg, isFlexCheck), lastCoord @@ -416,12 +433,10 @@ export function itineraryToTransitive( addStop(modifiedToStop, newStops, knownStopNames); pattern.stops.push({ stop_id: getStopId(modifiedToStop), - geometry: - // Some legs don't have intermediateStopGeometry, but do have valid legGeometry - (hasInterStopGeometry || hasLegGeometry) && - (hasIntermediateStopGeometry - ? leg.interStopGeometry[leg.interStopGeometry.length - 1].points - : leg.legGeometry.points) + // Some legs don't have intermediateStopGeometry, but do have valid legGeometry + geometry: hasIntermediateStopGeometry + ? leg.interStopGeometry?.[leg.interStopGeometry.length - 1]?.points + : leg.legGeometry?.points }); // add route to the route dictionary @@ -432,24 +447,24 @@ export function itineraryToTransitive( : getLegRouteShortName(leg)) || ""; const basicRouteAttributes = { - agency_id: leg.agencyId, - route_id: routeId, + agency_id: leg.agencyId ?? "", + route_id: routeId ?? "", route_short_name: routeLabel }; - if (typeof leg.route === "object" && leg.route !== null) { + if (typeof leg.route === "object" && leg.route !== null && routeId) { routes[routeId] = { ...basicRouteAttributes, route_long_name: leg.route.longName || "", - route_type: leg.route.type, + route_type: leg.route.type ?? 0, route_color: leg.route.color, route_text_color: leg.route.textColor }; - } else { + } else if (routeId) { routes[routeId] = { ...basicRouteAttributes, route_long_name: leg.routeLongName || "", - route_type: leg.routeType, + route_type: leg.routeType ?? 0, route_color: leg.routeColor, route_text_color: leg.routeTextColor }; @@ -460,10 +475,7 @@ export function itineraryToTransitive( // add the pattern reference to the journey object journey.segments.push({ - arc: - typeof disableFlexArc === "undefined" - ? isFlexCheck(leg) - : !disableFlexArc, + arc: disableFlexArc === undefined ? isFlexCheck(leg) : !disableFlexArc, type: "TRANSIT", patterns: [ { @@ -492,8 +504,7 @@ export function itineraryToTransitive( return tdata; } -// typescript TODO: TYPE -const drawArc = (straight: any) => { +const drawArc = (straight: LineString) => { // Create clone of plain route that only includes first and last point straight.coordinates = [ straight.coordinates[0], diff --git a/packages/transitive-overlay/tsconfig.json b/packages/transitive-overlay/tsconfig.json index 1501a23ae..88f577299 100644 --- a/packages/transitive-overlay/tsconfig.json +++ b/packages/transitive-overlay/tsconfig.json @@ -5,7 +5,8 @@ "lib": ["es2019", "dom"], "outDir": "./lib", "rootDir": "./src", - "skipLibCheck": true + "skipLibCheck": true, + "strict": true }, "include": ["src/**/*"], "references": [ diff --git a/packages/types/package.json b/packages/types/package.json index b45560678..8d609991b 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -31,5 +31,8 @@ "private": false, "scripts": { "tsc": "tsc" + }, + "dependencies": { + "@types/geojson": "^7946.0.16" } } diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 4447d3fdf..b9689b67b 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -580,21 +580,22 @@ export type TransitivePlace = { placeId?: string; type: string; }; +export type TransitiveSegment = { + arc?: boolean; + from?: TransitivePlace; + patterns?: { + pattern_id: string; + from_stop_index: number; + to_stop_index: number; + }[]; + streetEdges?: number[]; + to?: TransitivePlace; + type: string; +}; export type TransitiveJourney = { journey_id: string; journey_name: string; - segments: { - arc?: boolean; - from: TransitivePlace; - patterns?: { - pattern_id: string; - from_stop_index: number; - to_stop_index: number; - }[]; - streetEdges: number[]; - to: TransitivePlace; - type: string; - }[]; + segments: TransitiveSegment[]; }; export type TransitivePattern = { pattern_id: string; @@ -616,7 +617,7 @@ export type TransitiveRoute = { }; export type TransitiveStop = { stop_id: string; - stop_name: string; + stop_name?: string; stop_lat: number; stop_lon: number; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a3856f8c8..c0a4379a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1175,26 +1175,26 @@ importers: specifier: workspace:^ version: link:../itinerary-body '@turf/bbox': - specifier: ^6.5.0 - version: 6.5.0 + specifier: ^7.3.5 + version: 7.3.5 '@turf/bearing': - specifier: ^6.5.0 - version: 6.5.0 + specifier: ^7.3.5 + version: 7.3.5 '@turf/destination': - specifier: ^6.5.0 - version: 6.5.0 + specifier: ^7.3.5 + version: 7.3.5 '@turf/distance': - specifier: ^6.5.0 - version: 6.5.0 + specifier: ^7.3.5 + version: 7.3.5 '@turf/line-arc': - specifier: ^6.5.0 - version: 6.5.0 + specifier: ^7.3.5 + version: 7.3.5 '@turf/line-distance': specifier: ^4.7.3 version: 4.7.3 '@turf/midpoint': - specifier: ^6.5.0 - version: 6.5.0 + specifier: ^7.3.5 + version: 7.3.5 lodash.isequal: specifier: ^4.5.0 version: 4.5.0 @@ -1211,6 +1211,9 @@ importers: '@opentripplanner/types': specifier: workspace:^ version: link:../types + '@types/mapbox__polyline': + specifier: ^1.0.5 + version: 1.0.5 maplibre-gl: specifier: 'catalog:' version: 5.6.1 @@ -1329,7 +1332,11 @@ importers: specifier: 'catalog:' version: 5.6.1 - packages/types: {} + packages/types: + dependencies: + '@types/geojson': + specifier: ^7946.0.16 + version: 7946.0.16 packages/vehicle-rental-overlay: dependencies: @@ -3885,53 +3892,53 @@ packages: '@turf/along@7.3.1': resolution: {integrity: sha512-z84b9PKsUB69BhkeHA6oPqRO7VaJHwTid1SpuIbwWzDqHTpq8buJBKlrKgHIIthuVr5P/AZiEXmf3R4ifRhDmw==} - '@turf/bbox@6.5.0': - resolution: {integrity: sha512-RBbLaao5hXTYyyg577iuMtDB8ehxMlUqHEJiMs8jT1GHkFhr6sYre3lmLsPeYEi/ZKj5TP5tt7fkzNdJ4GIVyw==} - - '@turf/bearing@6.5.0': - resolution: {integrity: sha512-dxINYhIEMzgDOztyMZc20I7ssYVNEpSv04VbMo5YPQsqa80KO3TFvbuCahMsCAW5z8Tncc8dwBlEFrmRjJG33A==} + '@turf/bbox@7.3.5': + resolution: {integrity: sha512-oG1ya/HtBjAIg4TimbWx+nOYPbY0bCvt82Bq8tm6sBw3qqtbOyRSfDz79Sq90TnH7DXJprJ1qnVGKNtZ6jemfw==} '@turf/bearing@7.3.1': resolution: {integrity: sha512-ex78l/LiY6uO6jO8AJepyWE6/tiWEbXjKLOgqUfJSkW23UcMVlhbAKzXDjbsdz9T66sXFC/6QNAh8oaZzmoo6w==} - '@turf/circle@6.5.0': - resolution: {integrity: sha512-oU1+Kq9DgRnoSbWFHKnnUdTmtcRUMmHoV9DjTXu9vOLNV5OWtAAh1VZ+mzsioGGzoDNT/V5igbFOkMfBQc0B6A==} + '@turf/bearing@7.3.5': + resolution: {integrity: sha512-/qabIt/IuPsGlE6RukJ0zOirc6afNxoK7fK1WBNVnHuJjeOpSkW+7q1QW5XQGXBMv3odcD0ZgRR+MBiAHduYSA==} - '@turf/destination@6.5.0': - resolution: {integrity: sha512-4cnWQlNC8d1tItOz9B4pmJdWpXqS0vEvv65bI/Pj/genJnsL7evI0/Xw42RvEGROS481MPiU80xzvwxEvhQiMQ==} + '@turf/circle@7.3.5': + resolution: {integrity: sha512-Tse7GJrx0rxaQ5BdY6pHZ9HFPrZwRMry2yaqq94UsUyonhy1m7U2icB3Zp4M8o4XjHIGTCq9i2BYcGJram51Sw==} '@turf/destination@7.3.1': resolution: {integrity: sha512-yyiJtbQJ4AB9Ny/FKDDNuWI9Sg4Jtd2PMpQPqOV3AFq8NNkg0xJSNmDHDxupb3oPqPWYPxyfVI3tBoF+Xhhoig==} + '@turf/destination@7.3.5': + resolution: {integrity: sha512-x6ylChhOlIbucRSw7wF6z2gSEqqQl+dE0nSH5AL/ojZkqqGYahiw+2P4A8ZMuDM6rzH+FIqRYDes0o4nSArZCw==} + '@turf/distance@4.7.3': resolution: {integrity: sha512-nluvFy7qjZFq4B6E+KAlKdwdg9fbgJMIe/5O7jveg56gcXZ5Bvgx3lFUxAY016+l907OeX9lIZCNr/Y96FMyEA==} - '@turf/distance@6.5.0': - resolution: {integrity: sha512-xzykSLfoURec5qvQJcfifw/1mJa+5UwByZZ5TZ8iaqjGYN0vomhV9aiSLeYdUGtYRESZ+DYC/OzY+4RclZYgMg==} - '@turf/distance@7.3.1': resolution: {integrity: sha512-DK//doTGgYYjBkcWUywAe7wbZYcdP97hdEJ6rXYVYRoULwGGR3lhY96GNjozg6gaW9q2eSNYnZLpcL5iFVHqgw==} + '@turf/distance@7.3.5': + resolution: {integrity: sha512-uQAC63zg/l91KUxzfhqio7Ii3+UXTrPOVJScIdRj6EO6+9XHI4kC+AdyIS4cPAv14sZfJLIBxzMnzcGrss+kEA==} + '@turf/helpers@4.7.3': resolution: {integrity: sha512-hk5ANVazb80zK6tjJYAG76oCRTWMQo6SAFu5E1dVnlb2kT3FHLoPcOB9P11duwDafMwywz24KZ+FUorB5e728w==} - '@turf/helpers@6.5.0': - resolution: {integrity: sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==} - '@turf/helpers@7.3.1': resolution: {integrity: sha512-zkL34JVhi5XhsuMEO0MUTIIFEJ8yiW1InMu4hu/oRqamlY4mMoZql0viEmH6Dafh/p+zOl8OYvMJ3Vm3rFshgg==} + '@turf/helpers@7.3.5': + resolution: {integrity: sha512-E/NMGV5MwbjjP7AJXBtsanC3yY8N2MQ87IGdIgkB2ji5AtBpwnH4L3gEqpYN4RlCJJWbLbzO91BbKv2waUd0eg==} + '@turf/invariant@4.7.3': resolution: {integrity: sha512-MtlIn9aPC4gZsflLEnfBl/o/SJjZU/yuvj+fo9qRXUOI8wBiKaUcZIT4x1jdLW0NGvU7wMPWJSFJanLCQ9eU1w==} - '@turf/invariant@6.5.0': - resolution: {integrity: sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==} - '@turf/invariant@7.3.1': resolution: {integrity: sha512-IdZJfDjIDCLH+Gu2yLFoSM7H23sdetIo5t4ET1/25X8gi3GE2XSqbZwaGjuZgNh02nisBewLqNiJs2bo+hrqZA==} - '@turf/line-arc@6.5.0': - resolution: {integrity: sha512-I6c+V6mIyEwbtg9P9zSFF89T7QPe1DPTG3MJJ6Cm1MrAY0MdejwQKOpsvNl8LDU2ekHOlz2kHpPVR7VJsoMllA==} + '@turf/invariant@7.3.5': + resolution: {integrity: sha512-ZVIvsBvjr8lO7WxC5zYNjRsjSDvyGvWkJMjuWaJjTU8x+1tmfNnw3gDX/TI2Sit83gcRYLYkNo23lB/udqx/Hg==} + + '@turf/line-arc@7.3.5': + resolution: {integrity: sha512-XrPicIoN7XCtiJ7e7ELje7GjMZrBw/nuJnz0mQoCwwHwmX8Oz9oRfb6uaiS8NeR4WBzUVdkmVR/ro0kW4lypog==} '@turf/line-distance@4.7.3': resolution: {integrity: sha512-U6sFReqpMJq/LhOoDna4w1Vs2gliJUhFa1g94WrZrVz7l4IHldy7Q0Va0CyQ00qN5n3f09/vf4F+q2+ThHbL4g==} @@ -3940,11 +3947,11 @@ packages: '@turf/meta@4.7.4': resolution: {integrity: sha512-cvwz4EI9BjrgRHxmJ3Z3HKxq6k/fj/V95kwNZ8uWNLncCvrb7x1jUBDkQUo1tShnYdZ8eBgA+a2bvJmAM+MJ0A==} - '@turf/meta@6.5.0': - resolution: {integrity: sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==} + '@turf/meta@7.3.5': + resolution: {integrity: sha512-r+ohqxoyqeigFB0oFrQx/YEHIkOKqcKpCjvZkvZs7Tkv+IFco5MezAd2zd4rzK+0DfFgDP3KpJc7HqrYjvEjhg==} - '@turf/midpoint@6.5.0': - resolution: {integrity: sha512-MyTzV44IwmVI6ec9fB2OgZ53JGNlgOpaYl9ArKoF49rXpL84F9rNATndbe0+MQIhdkw8IlzA6xVP4lZzfMNVCw==} + '@turf/midpoint@7.3.5': + resolution: {integrity: sha512-y92O2YDDBkZp7jAUuUPgof/HiXHjJSkKjEtQRjWXZcjTlzHd/Fqg6/twarY2wNkENK2EuaaiV9MDy74FeRG2Ow==} '@tybys/wasm-util@0.9.0': resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} @@ -4738,9 +4745,6 @@ packages: axios@1.15.2: resolution: {integrity: sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==} - axios@1.15.2: - resolution: {integrity: sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==} - axios@1.7.7: resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} @@ -14078,15 +14082,12 @@ snapshots: '@types/geojson': 7946.0.16 tslib: 2.8.1 - '@turf/bbox@6.5.0': + '@turf/bbox@7.3.5': dependencies: - '@turf/helpers': 6.5.0 - '@turf/meta': 6.5.0 - - '@turf/bearing@6.5.0': - dependencies: - '@turf/helpers': 6.5.0 - '@turf/invariant': 6.5.0 + '@turf/helpers': 7.3.5 + '@turf/meta': 7.3.5 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 '@turf/bearing@7.3.1': dependencies: @@ -14095,15 +14096,19 @@ snapshots: '@types/geojson': 7946.0.16 tslib: 2.8.1 - '@turf/circle@6.5.0': + '@turf/bearing@7.3.5': dependencies: - '@turf/destination': 6.5.0 - '@turf/helpers': 6.5.0 + '@turf/helpers': 7.3.5 + '@turf/invariant': 7.3.5 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 - '@turf/destination@6.5.0': + '@turf/circle@7.3.5': dependencies: - '@turf/helpers': 6.5.0 - '@turf/invariant': 6.5.0 + '@turf/destination': 7.3.5 + '@turf/helpers': 7.3.5 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 '@turf/destination@7.3.1': dependencies: @@ -14112,16 +14117,18 @@ snapshots: '@types/geojson': 7946.0.16 tslib: 2.8.1 + '@turf/destination@7.3.5': + dependencies: + '@turf/helpers': 7.3.5 + '@turf/invariant': 7.3.5 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 + '@turf/distance@4.7.3': dependencies: '@turf/helpers': 4.7.3 '@turf/invariant': 4.7.3 - '@turf/distance@6.5.0': - dependencies: - '@turf/helpers': 6.5.0 - '@turf/invariant': 6.5.0 - '@turf/distance@7.3.1': dependencies: '@turf/helpers': 7.3.1 @@ -14129,20 +14136,26 @@ snapshots: '@types/geojson': 7946.0.16 tslib: 2.8.1 - '@turf/helpers@4.7.3': {} + '@turf/distance@7.3.5': + dependencies: + '@turf/helpers': 7.3.5 + '@turf/invariant': 7.3.5 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 - '@turf/helpers@6.5.0': {} + '@turf/helpers@4.7.3': {} '@turf/helpers@7.3.1': dependencies: '@types/geojson': 7946.0.16 tslib: 2.8.1 - '@turf/invariant@4.7.3': {} - - '@turf/invariant@6.5.0': + '@turf/helpers@7.3.5': dependencies: - '@turf/helpers': 6.5.0 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 + + '@turf/invariant@4.7.3': {} '@turf/invariant@7.3.1': dependencies: @@ -14150,11 +14163,19 @@ snapshots: '@types/geojson': 7946.0.16 tslib: 2.8.1 - '@turf/line-arc@6.5.0': + '@turf/invariant@7.3.5': + dependencies: + '@turf/helpers': 7.3.5 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 + + '@turf/line-arc@7.3.5': dependencies: - '@turf/circle': 6.5.0 - '@turf/destination': 6.5.0 - '@turf/helpers': 6.5.0 + '@turf/circle': 7.3.5 + '@turf/destination': 7.3.5 + '@turf/helpers': 7.3.5 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 '@turf/line-distance@4.7.3': dependencies: @@ -14164,16 +14185,20 @@ snapshots: '@turf/meta@4.7.4': {} - '@turf/meta@6.5.0': + '@turf/meta@7.3.5': dependencies: - '@turf/helpers': 6.5.0 + '@turf/helpers': 7.3.5 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 - '@turf/midpoint@6.5.0': + '@turf/midpoint@7.3.5': dependencies: - '@turf/bearing': 6.5.0 - '@turf/destination': 6.5.0 - '@turf/distance': 6.5.0 - '@turf/helpers': 6.5.0 + '@turf/bearing': 7.3.5 + '@turf/destination': 7.3.5 + '@turf/distance': 7.3.5 + '@turf/helpers': 7.3.5 + '@types/geojson': 7946.0.16 + tslib: 2.8.1 '@tybys/wasm-util@0.9.0': dependencies: @@ -14998,14 +15023,6 @@ snapshots: transitivePeerDependencies: - debug - axios@1.15.2: - dependencies: - follow-redirects: 1.16.0 - form-data: 4.0.5 - proxy-from-env: 2.1.0 - transitivePeerDependencies: - - debug - axios@1.7.7: dependencies: follow-redirects: 1.15.9 @@ -21809,16 +21826,6 @@ snapshots: transitivePeerDependencies: - debug - wait-on@9.0.5: - dependencies: - axios: 1.15.2 - joi: 18.1.2 - lodash: 4.18.1 - minimist: 1.2.8 - rxjs: 7.8.2 - transitivePeerDependencies: - - debug - wait-port@0.2.14: dependencies: chalk: 2.4.2
Pleasant Ave & 50th St / Busch Terrace Nicollet Ave S & 46th St E 46th St & I-35W 4th Ave S & 38th St E 4th Ave S & Lake St E 3rd Ave S & Franklin Ave E Nicollet Mall & Alice Rainville Pl Nicollet Mall & 7th St S Nicollet Mall & 3rd St S Hennepin Ave E & Wilder St Lowry Ave NE & 2nd St / 1st St NE Grand St NE & 29th Ave NE Columbia Heights Transit Center C
+
- + 06:00 + 06:02 + 06:06 + 06:11 + 06:16 + 06:20 + 06:24 + 06:28 + 06:31 + 06:39 + 06:42 + 06:52
+
16:10 + - + 16:26 + 16:31 + 16:36 + - + - + - + - + - + - + - + -
+
- + 01:37 + 01:39 + 01:43 + 01:47 + 01:52 + 01:56 + 02:00 + 02:13 + 02:15 + 02:23 + 02:26 + 02:36
+ {s.name}
- {r} + + {r.value}
Pleasant Ave & 50th St / Busch Terrace Nicollet Ave S & 46th St E 46th St & I-35W 4th Ave S & 38th St E 4th Ave S & Lake St E 3rd Ave S & Franklin Ave E Nicollet Mall & Alice Rainville Pl Nicollet Mall & 7th St S Nicollet Mall & 3rd St S Hennepin Ave E & Wilder St Lowry Ave NE & 2nd St / 1st St NE Grand St NE & 29th Ave NE Columbia Heights Transit Center C @@ -75,125 +75,125 @@ exports[`Timetable Default smoke-test 1`] = `
+ - + 06:00 + 06:02 + 06:06 + 06:11 + 06:16 + 06:20 + 06:24 + 06:28 + 06:31 + 06:39 + 06:42 + 06:52
+ 16:10 + - + 16:26 + 16:31 + 16:36 + - + - + - + - + - + - + - + -
+ - + 01:37 + 01:39 + 01:43 + 01:47 + 01:52 + 01:56 + 02:00 + 02:13 + 02:15 + 02:23 + 02:26 + 02:36