minotor 9.0.2 → 9.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/minotor.yml +36 -23
- package/CHANGELOG.md +3 -3
- package/dist/cli.mjs +86 -54
- package/dist/cli.mjs.map +1 -1
- package/dist/parser.cjs.js +19 -10
- package/dist/parser.cjs.js.map +1 -1
- package/dist/parser.esm.js +19 -10
- package/dist/parser.esm.js.map +1 -1
- package/dist/router.cjs.js +1 -1
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.esm.js +1 -1
- package/dist/router.esm.js.map +1 -1
- package/dist/router.umd.js +1 -1
- package/dist/router.umd.js.map +1 -1
- package/dist/routing/result.d.ts +9 -1
- package/dist/stops/stopsIndex.d.ts +7 -1
- package/eslint.config.mjs +0 -1
- package/package.json +25 -25
- package/src/__e2e__/router.test.ts +40 -53
- package/src/__e2e__/timetable/stops.bin +2 -2
- package/src/__e2e__/timetable/timetable.bin +2 -2
- package/src/routing/__tests__/result.test.ts +81 -0
- package/src/routing/result.ts +46 -14
- package/src/stops/__tests__/stopFinder.test.ts +9 -0
- package/src/stops/proto/stops.ts +1 -1
- package/src/stops/stopsIndex.ts +12 -1
- package/src/timetable/proto/timetable.ts +1 -1
package/dist/routing/result.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Timetable } from '../router.js';
|
|
2
|
-
import { SourceStopId } from '../stops/stops.js';
|
|
2
|
+
import { SourceStopId, StopId } from '../stops/stops.js';
|
|
3
3
|
import { StopsIndex } from '../stops/stopsIndex.js';
|
|
4
4
|
import { Query } from './query.js';
|
|
5
5
|
import { Route } from './route.js';
|
|
@@ -10,6 +10,14 @@ export declare class Result {
|
|
|
10
10
|
readonly stopsIndex: StopsIndex;
|
|
11
11
|
readonly timetable: Timetable;
|
|
12
12
|
constructor(query: Query, routingState: RoutingState, stopsIndex: StopsIndex, timetable: Timetable);
|
|
13
|
+
/**
|
|
14
|
+
* Reconstructs the best route to a stop by StopId.
|
|
15
|
+
* (to any stop reachable in less time / transfers than the destination(s) of the query)
|
|
16
|
+
*
|
|
17
|
+
* @param to The destination stop by StopId.
|
|
18
|
+
* @returns a route to the destination stop if it exists.
|
|
19
|
+
*/
|
|
20
|
+
bestRouteToStopId(to: StopId | Set<StopId>): Route | undefined;
|
|
13
21
|
/**
|
|
14
22
|
* Reconstructs the best route to a stop.
|
|
15
23
|
* (to any stop reachable in less time / transfers than the destination(s) of the query)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SourceStopId, Stop, StopId } from './stops.js';
|
|
2
2
|
/**
|
|
3
|
-
* The
|
|
3
|
+
* The StopsIndex class provides functionality to search for public transport stops
|
|
4
4
|
* by name or geographic location. It leverages text search and geospatial indexing
|
|
5
5
|
* to efficiently find stops based on user queries.
|
|
6
6
|
*/
|
|
@@ -66,4 +66,10 @@ export declare class StopsIndex {
|
|
|
66
66
|
* Find ids of all sibling stops.
|
|
67
67
|
*/
|
|
68
68
|
equivalentStops(sourceId: SourceStopId): Stop[];
|
|
69
|
+
/**
|
|
70
|
+
* Makes the StopsIndex iterable, allowing iteration over all stops.
|
|
71
|
+
*
|
|
72
|
+
* @returns An iterator for the stops.
|
|
73
|
+
*/
|
|
74
|
+
[Symbol.iterator](): Iterator<Stop>;
|
|
69
75
|
}
|
package/eslint.config.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "minotor",
|
|
3
|
-
"version": "9.0
|
|
3
|
+
"version": "9.2.0",
|
|
4
4
|
"description": "A lightweight client-side transit routing library.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"minotor",
|
|
@@ -42,11 +42,11 @@
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
|
45
|
-
"node": ">=
|
|
45
|
+
"node": ">=24.11.0",
|
|
46
46
|
"npm": ">=11.5.2"
|
|
47
47
|
},
|
|
48
48
|
"volta": {
|
|
49
|
-
"node": "
|
|
49
|
+
"node": "24.12.0",
|
|
50
50
|
"npm": "11.5.2"
|
|
51
51
|
},
|
|
52
52
|
"publishConfig": {
|
|
@@ -74,44 +74,44 @@
|
|
|
74
74
|
"semantic-release": "semantic-release"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@eslint/js": "^9.
|
|
78
|
-
"@rollup/plugin-commonjs": "^
|
|
79
|
-
"@rollup/plugin-node-resolve": "^16.0.
|
|
77
|
+
"@eslint/js": "^9.39.2",
|
|
78
|
+
"@rollup/plugin-commonjs": "^29.0.0",
|
|
79
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
80
80
|
"@rollup/plugin-terser": "^0.4.4",
|
|
81
|
-
"@rollup/plugin-typescript": "^12.
|
|
81
|
+
"@rollup/plugin-typescript": "^12.3.0",
|
|
82
82
|
"@ryansonshine/commitizen": "^4.2.8",
|
|
83
83
|
"@ryansonshine/cz-conventional-changelog": "^3.3.4",
|
|
84
84
|
"@semantic-release/changelog": "^6.0.3",
|
|
85
85
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
86
|
-
"@semantic-release/github": "^
|
|
87
|
-
"@semantic-release/npm": "^
|
|
88
|
-
"@semantic-release/release-notes-generator": "^14.0
|
|
86
|
+
"@semantic-release/github": "^12.0.2",
|
|
87
|
+
"@semantic-release/npm": "^13.1.3",
|
|
88
|
+
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
89
89
|
"@types/geokdbush": "^1.1.5",
|
|
90
90
|
"@types/luxon": "^3.7.1",
|
|
91
|
-
"@types/node": "^
|
|
91
|
+
"@types/node": "^25.0.3",
|
|
92
92
|
"c8": "^10.1.3",
|
|
93
|
-
"cspell": "^9.
|
|
94
|
-
"eslint": "^9.
|
|
93
|
+
"cspell": "^9.4.0",
|
|
94
|
+
"eslint": "^9.39.2",
|
|
95
95
|
"eslint-config-prettier": "^10.1.8",
|
|
96
96
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
97
|
-
"prettier": "^3.
|
|
98
|
-
"rimraf": "^6.
|
|
99
|
-
"rollup": "^4.
|
|
100
|
-
"semantic-release": "^
|
|
101
|
-
"ts-proto": "^2.
|
|
102
|
-
"tsx": "^4.
|
|
103
|
-
"typescript": "^5.9.
|
|
104
|
-
"typescript-eslint": "^8.
|
|
97
|
+
"prettier": "^3.7.4",
|
|
98
|
+
"rimraf": "^6.1.2",
|
|
99
|
+
"rollup": "^4.55.1",
|
|
100
|
+
"semantic-release": "^25.0.2",
|
|
101
|
+
"ts-proto": "^2.10.1",
|
|
102
|
+
"tsx": "^4.21.0",
|
|
103
|
+
"typescript": "^5.9.3",
|
|
104
|
+
"typescript-eslint": "^8.52.0"
|
|
105
105
|
},
|
|
106
106
|
"dependencies": {
|
|
107
|
-
"@bufbuild/protobuf": "^2.
|
|
108
|
-
"commander": "^14.0.
|
|
107
|
+
"@bufbuild/protobuf": "^2.10.2",
|
|
108
|
+
"commander": "^14.0.2",
|
|
109
109
|
"csv-parse": "^6.1.0",
|
|
110
110
|
"geokdbush": "^2.0.1",
|
|
111
111
|
"kdbush": "^4.0.2",
|
|
112
112
|
"loglevel": "^1.9.2",
|
|
113
|
-
"luxon": "^3.7.
|
|
113
|
+
"luxon": "^3.7.2",
|
|
114
114
|
"node-stream-zip": "^1.15.0",
|
|
115
|
-
"slimsearch": "^2.
|
|
115
|
+
"slimsearch": "^2.3.0"
|
|
116
116
|
}
|
|
117
117
|
}
|
|
@@ -15,7 +15,7 @@ const routes = [
|
|
|
15
15
|
from: '8504100:0:2',
|
|
16
16
|
to: '8504086:0:2',
|
|
17
17
|
departure: '08:34',
|
|
18
|
-
arrival: '09:
|
|
18
|
+
arrival: '09:10',
|
|
19
19
|
route: { type: 'RAIL', name: 'RE2' },
|
|
20
20
|
},
|
|
21
21
|
{
|
|
@@ -33,13 +33,13 @@ const routes = [
|
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
from: '8504077:0:1',
|
|
36
|
-
to: '8577737',
|
|
36
|
+
to: '8577737:0:B',
|
|
37
37
|
type: 'REQUIRES_MINIMAL_TIME',
|
|
38
38
|
minTransferTime: '02:00',
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
|
-
from: '8577737',
|
|
42
|
-
to: '8504880',
|
|
41
|
+
from: '8577737:0:B',
|
|
42
|
+
to: '8504880:0:10000',
|
|
43
43
|
departure: '09:33',
|
|
44
44
|
arrival: '09:44',
|
|
45
45
|
route: { type: 'BUS', name: '263' },
|
|
@@ -60,54 +60,28 @@ const routes = [
|
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
62
|
from: '8503000:0:33',
|
|
63
|
-
to: '8503000:0:
|
|
63
|
+
to: '8503000:0:6',
|
|
64
64
|
type: 'REQUIRES_MINIMAL_TIME',
|
|
65
65
|
minTransferTime: '07:00',
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
|
-
from: '8503000:0:
|
|
69
|
-
to: '
|
|
68
|
+
from: '8503000:0:6',
|
|
69
|
+
to: '8509000:0:9',
|
|
70
70
|
departure: '13:38',
|
|
71
|
-
arrival: '14:
|
|
71
|
+
arrival: '14:52',
|
|
72
72
|
route: { type: 'RAIL', name: 'IC3' },
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
|
-
from: '
|
|
76
|
-
to: '
|
|
75
|
+
from: '8509000:0:9',
|
|
76
|
+
to: '8509000:0:10',
|
|
77
77
|
type: 'REQUIRES_MINIMAL_TIME',
|
|
78
|
-
minTransferTime: '
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
from: '8509002:0:6',
|
|
82
|
-
to: '8509269:0:3',
|
|
83
|
-
departure: '14:49',
|
|
84
|
-
arrival: '15:52',
|
|
85
|
-
route: { type: 'RAIL', name: 'RE24' },
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
from: '8509269:0:3',
|
|
89
|
-
to: '8509269:0:4',
|
|
90
|
-
type: 'REQUIRES_MINIMAL_TIME',
|
|
91
|
-
minTransferTime: '01:00',
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
from: '8509269:0:4',
|
|
95
|
-
to: '8509251:0:3',
|
|
96
|
-
departure: '15:54',
|
|
97
|
-
arrival: '16:43',
|
|
98
|
-
route: { type: 'RAIL', name: 'R15' },
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
from: '8509251:0:3',
|
|
102
|
-
to: '8509251:0:2',
|
|
103
|
-
type: 'REQUIRES_MINIMAL_TIME',
|
|
104
|
-
minTransferTime: '01:00',
|
|
78
|
+
minTransferTime: '03:00',
|
|
105
79
|
},
|
|
106
80
|
{
|
|
107
|
-
from: '
|
|
81
|
+
from: '8509000:0:10',
|
|
108
82
|
to: '8509253:0:1',
|
|
109
|
-
departure: '
|
|
110
|
-
arrival: '
|
|
83
|
+
departure: '14:58',
|
|
84
|
+
arrival: '16:55',
|
|
111
85
|
route: { type: 'RAIL', name: 'IR38' },
|
|
112
86
|
},
|
|
113
87
|
],
|
|
@@ -121,7 +95,7 @@ const routes = [
|
|
|
121
95
|
from: '8500010:0:33',
|
|
122
96
|
to: '8718213',
|
|
123
97
|
departure: '17:08',
|
|
124
|
-
arrival: '17:
|
|
98
|
+
arrival: '17:16',
|
|
125
99
|
route: { type: 'RAIL', name: 'TER' },
|
|
126
100
|
},
|
|
127
101
|
{
|
|
@@ -140,13 +114,26 @@ const routes = [
|
|
|
140
114
|
route: [
|
|
141
115
|
{
|
|
142
116
|
from: '8504100:0:3',
|
|
143
|
-
to: '
|
|
144
|
-
departure: '
|
|
145
|
-
arrival: '
|
|
146
|
-
route: { type: 'RAIL', name: '
|
|
117
|
+
to: '8507000:0:10',
|
|
118
|
+
departure: '08:33',
|
|
119
|
+
arrival: '08:56',
|
|
120
|
+
route: { type: 'RAIL', name: 'IR15' },
|
|
147
121
|
},
|
|
148
122
|
{
|
|
149
|
-
from: '
|
|
123
|
+
from: '8507000:0:10',
|
|
124
|
+
to: '8507000:0:2',
|
|
125
|
+
type: 'REQUIRES_MINIMAL_TIME',
|
|
126
|
+
minTransferTime: '06:00',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
from: '8507000:0:2',
|
|
130
|
+
to: '8503000:0:34',
|
|
131
|
+
departure: '09:02',
|
|
132
|
+
arrival: '09:58',
|
|
133
|
+
route: { type: 'RAIL', name: 'IC81' },
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
from: '8503000:0:34',
|
|
150
137
|
to: '8503000:0:10',
|
|
151
138
|
type: 'REQUIRES_MINIMAL_TIME',
|
|
152
139
|
minTransferTime: '07:00',
|
|
@@ -154,8 +141,8 @@ const routes = [
|
|
|
154
141
|
{
|
|
155
142
|
from: '8503000:0:10',
|
|
156
143
|
to: '8509002:0:2',
|
|
157
|
-
departure: '10:
|
|
158
|
-
arrival: '11:
|
|
144
|
+
departure: '10:07',
|
|
145
|
+
arrival: '11:11',
|
|
159
146
|
route: { type: 'RAIL', name: 'IC3' },
|
|
160
147
|
},
|
|
161
148
|
{
|
|
@@ -166,10 +153,10 @@ const routes = [
|
|
|
166
153
|
},
|
|
167
154
|
{
|
|
168
155
|
from: '8509002:0:6',
|
|
169
|
-
to: '8509073:0:
|
|
170
|
-
departure: '11:
|
|
171
|
-
arrival: '
|
|
172
|
-
route: { type: 'RAIL', name: '
|
|
156
|
+
to: '8509073:0:2',
|
|
157
|
+
departure: '11:20',
|
|
158
|
+
arrival: '12:27',
|
|
159
|
+
route: { type: 'RAIL', name: 'RE13' },
|
|
173
160
|
},
|
|
174
161
|
],
|
|
175
162
|
},
|
|
@@ -204,7 +191,7 @@ describe('E2E Tests for Transit Router', () => {
|
|
|
204
191
|
|
|
205
192
|
const result = router.route(queryObject);
|
|
206
193
|
const bestRoute = result.bestRoute(toStop.sourceStopId);
|
|
207
|
-
|
|
194
|
+
console.log();
|
|
208
195
|
assert.ok(bestRoute, 'No route found');
|
|
209
196
|
const actualRoute = bestRoute.asJson();
|
|
210
197
|
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:8147a3beb12e5dd65fe2bb5ed4013adaa0897ac84bd9722f53a7f38ebfaa9100
|
|
3
|
+
size 4542160
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
version https://git-lfs.github.com/spec/v1
|
|
2
|
-
oid sha256:
|
|
3
|
-
size
|
|
2
|
+
oid sha256:f18f8795ee3dcb04accc3f372cde7041ec4c6d2812fee03e2a0c9de39d4149a6
|
|
3
|
+
size 18441262
|
|
@@ -409,6 +409,87 @@ describe('Result', () => {
|
|
|
409
409
|
});
|
|
410
410
|
});
|
|
411
411
|
|
|
412
|
+
describe('bestRouteToStopId', () => {
|
|
413
|
+
it('should return route when given a single StopId', () => {
|
|
414
|
+
const earliestArrivals = new Map([
|
|
415
|
+
[0, { arrival: Time.fromHMS(8, 0, 0), legNumber: 0 }], // origin
|
|
416
|
+
[2, { arrival: Time.fromHMS(9, 0, 0), legNumber: 1 }], // destination
|
|
417
|
+
]);
|
|
418
|
+
|
|
419
|
+
const vehicleEdge: VehicleEdge = {
|
|
420
|
+
arrival: Time.fromHMS(9, 0, 0),
|
|
421
|
+
from: 0,
|
|
422
|
+
to: 2,
|
|
423
|
+
routeId: 0,
|
|
424
|
+
tripIndex: 0,
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
const graph: Map<StopId, RoutingEdge>[] = [
|
|
428
|
+
new Map<StopId, RoutingEdge>([[0, { arrival: Time.fromHMS(8, 0, 0) }]]), // Round 0 - origins
|
|
429
|
+
new Map<StopId, RoutingEdge>([[2, vehicleEdge]]), // Round 1
|
|
430
|
+
];
|
|
431
|
+
|
|
432
|
+
const result = new Result(
|
|
433
|
+
mockQuery,
|
|
434
|
+
{
|
|
435
|
+
earliestArrivals,
|
|
436
|
+
graph,
|
|
437
|
+
destinations: [2],
|
|
438
|
+
},
|
|
439
|
+
mockStopsIndex,
|
|
440
|
+
mockTimetable,
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
const route = result.bestRouteToStopId(2); // Using StopId instead of SourceStopId
|
|
444
|
+
assert(route);
|
|
445
|
+
assert.strictEqual(route.legs.length, 1);
|
|
446
|
+
const firstLeg = route.legs[0];
|
|
447
|
+
assert(firstLeg);
|
|
448
|
+
assert.strictEqual(firstLeg.from.id, 0);
|
|
449
|
+
assert.strictEqual(firstLeg.to.id, 2);
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it('should return route to closest destination when given a Set of StopIds', () => {
|
|
453
|
+
const earliestArrivals = new Map([
|
|
454
|
+
[0, { arrival: Time.fromHMS(8, 0, 0), legNumber: 0 }], // origin
|
|
455
|
+
[2, { arrival: Time.fromHMS(9, 0, 0), legNumber: 1 }], // destination (faster)
|
|
456
|
+
[3, { arrival: Time.fromHMS(9, 45, 0), legNumber: 1 }], // destination (slower)
|
|
457
|
+
]);
|
|
458
|
+
|
|
459
|
+
const vehicleEdge: VehicleEdge = {
|
|
460
|
+
arrival: Time.fromHMS(9, 0, 0),
|
|
461
|
+
from: 0,
|
|
462
|
+
to: 2,
|
|
463
|
+
routeId: 0,
|
|
464
|
+
tripIndex: 0,
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
const graph: Map<StopId, RoutingEdge>[] = [
|
|
468
|
+
new Map<StopId, RoutingEdge>([[0, { arrival: Time.fromHMS(8, 0, 0) }]]), // Round 0 - origins
|
|
469
|
+
new Map<StopId, RoutingEdge>([[2, vehicleEdge]]), // Round 1
|
|
470
|
+
];
|
|
471
|
+
|
|
472
|
+
const result = new Result(
|
|
473
|
+
mockQuery,
|
|
474
|
+
{
|
|
475
|
+
earliestArrivals,
|
|
476
|
+
graph,
|
|
477
|
+
destinations: [2, 3],
|
|
478
|
+
},
|
|
479
|
+
mockStopsIndex,
|
|
480
|
+
mockTimetable,
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
const route = result.bestRouteToStopId(new Set([2, 3])); // Using Set of StopIds
|
|
484
|
+
assert(route);
|
|
485
|
+
assert.strictEqual(route.legs.length, 1);
|
|
486
|
+
const firstLeg = route.legs[0];
|
|
487
|
+
assert(firstLeg);
|
|
488
|
+
assert.strictEqual(firstLeg.from.id, 0);
|
|
489
|
+
assert.strictEqual(firstLeg.to.id, 2); // Should route to stop 2 (faster arrival)
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
|
|
412
493
|
describe('continuous trips', () => {
|
|
413
494
|
it('should handle single continuous trip correctly', () => {
|
|
414
495
|
const earliestArrivals = new Map([
|
package/src/routing/result.ts
CHANGED
|
@@ -23,6 +23,37 @@ export class Result {
|
|
|
23
23
|
this.timetable = timetable;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Reconstructs the best route to a stop by StopId.
|
|
28
|
+
* (to any stop reachable in less time / transfers than the destination(s) of the query)
|
|
29
|
+
*
|
|
30
|
+
* @param to The destination stop by StopId.
|
|
31
|
+
* @returns a route to the destination stop if it exists.
|
|
32
|
+
*/
|
|
33
|
+
bestRouteToStopId(to: StopId | Set<StopId>): Route | undefined {
|
|
34
|
+
const sourceStopIds =
|
|
35
|
+
to instanceof Set
|
|
36
|
+
? new Set(
|
|
37
|
+
Array.from(to)
|
|
38
|
+
.map(
|
|
39
|
+
(stopId) => this.stopsIndex.findStopById(stopId)?.sourceStopId,
|
|
40
|
+
)
|
|
41
|
+
.filter(
|
|
42
|
+
(sourceId): sourceId is SourceStopId => sourceId !== undefined,
|
|
43
|
+
),
|
|
44
|
+
)
|
|
45
|
+
: this.stopsIndex.findStopById(to)?.sourceStopId;
|
|
46
|
+
|
|
47
|
+
if (
|
|
48
|
+
sourceStopIds === undefined ||
|
|
49
|
+
(sourceStopIds instanceof Set && sourceStopIds.size === 0)
|
|
50
|
+
) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return this.bestRoute(sourceStopIds);
|
|
55
|
+
}
|
|
56
|
+
|
|
26
57
|
/**
|
|
27
58
|
* Reconstructs the best route to a stop.
|
|
28
59
|
* (to any stop reachable in less time / transfers than the destination(s) of the query)
|
|
@@ -37,23 +68,24 @@ export class Result {
|
|
|
37
68
|
: to
|
|
38
69
|
? [to]
|
|
39
70
|
: Array.from(this.query.to);
|
|
40
|
-
const destinations = destinationList.flatMap((destination) =>
|
|
41
|
-
this.stopsIndex.equivalentStops(destination),
|
|
42
|
-
);
|
|
43
71
|
// find the first reached destination
|
|
44
72
|
let fastestDestination: StopId | undefined = undefined;
|
|
45
73
|
let fastestTime: Arrival | undefined = undefined;
|
|
46
|
-
for (const
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
74
|
+
for (const sourceDestination of destinationList) {
|
|
75
|
+
const equivalentStops =
|
|
76
|
+
this.stopsIndex.equivalentStops(sourceDestination);
|
|
77
|
+
for (const destination of equivalentStops) {
|
|
78
|
+
const arrivalTime = this.routingState.earliestArrivals.get(
|
|
79
|
+
destination.id,
|
|
80
|
+
);
|
|
81
|
+
if (arrivalTime !== undefined) {
|
|
82
|
+
if (
|
|
83
|
+
fastestTime === undefined ||
|
|
84
|
+
arrivalTime.arrival.isBefore(fastestTime.arrival)
|
|
85
|
+
) {
|
|
86
|
+
fastestDestination = destination.id;
|
|
87
|
+
fastestTime = arrivalTime;
|
|
88
|
+
}
|
|
57
89
|
}
|
|
58
90
|
}
|
|
59
91
|
}
|
|
@@ -170,4 +170,13 @@ describe('Stop Finder', () => {
|
|
|
170
170
|
assert.deepStrictEqual(equivalentStops, []);
|
|
171
171
|
});
|
|
172
172
|
});
|
|
173
|
+
|
|
174
|
+
describe('iterator', () => {
|
|
175
|
+
it('should iterate over all stops', () => {
|
|
176
|
+
const stops = [...stopFinder];
|
|
177
|
+
assert.strictEqual(stops.length, 7);
|
|
178
|
+
assert.strictEqual(stops[0]?.id, 0);
|
|
179
|
+
assert.strictEqual(stops[6]?.id, 6);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
173
182
|
});
|
package/src/stops/proto/stops.ts
CHANGED
package/src/stops/stopsIndex.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { SourceStopId, SourceStopsMap, Stop, StopId } from './stops.js';
|
|
|
11
11
|
type StopPoint = { id: StopId; lat: number; lon: number };
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* The
|
|
14
|
+
* The StopsIndex class provides functionality to search for public transport stops
|
|
15
15
|
* by name or geographic location. It leverages text search and geospatial indexing
|
|
16
16
|
* to efficiently find stops based on user queries.
|
|
17
17
|
*/
|
|
@@ -187,4 +187,15 @@ export class StopsIndex {
|
|
|
187
187
|
(stopId) => this.stops[stopId] as Stop,
|
|
188
188
|
);
|
|
189
189
|
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Makes the StopsIndex iterable, allowing iteration over all stops.
|
|
193
|
+
*
|
|
194
|
+
* @returns An iterator for the stops.
|
|
195
|
+
*/
|
|
196
|
+
*[Symbol.iterator](): Iterator<Stop> {
|
|
197
|
+
for (const stop of this.stops) {
|
|
198
|
+
yield stop;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
190
201
|
}
|