minotor 1.0.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/.cspell.json +43 -0
- package/.czrc +3 -0
- package/.editorconfig +10 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +29 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +4 -0
- package/.github/workflows/minotor.yml +85 -0
- package/.prettierrc +7 -0
- package/.releaserc.json +27 -0
- package/CHANGELOG.md +6 -0
- package/LICENSE +21 -0
- package/README.md +166 -0
- package/dist/bundle.cjs.js +16507 -0
- package/dist/bundle.cjs.js.map +1 -0
- package/dist/bundle.esm.js +16496 -0
- package/dist/bundle.esm.js.map +1 -0
- package/dist/bundle.umd.js +2 -0
- package/dist/bundle.umd.js.map +1 -0
- package/dist/cli/__tests__/minotor.test.d.ts +1 -0
- package/dist/cli/minotor.d.ts +5 -0
- package/dist/cli/repl.d.ts +1 -0
- package/dist/cli/utils.d.ts +3 -0
- package/dist/cli.mjs +20504 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/gtfs/__tests__/parser.test.d.ts +1 -0
- package/dist/gtfs/__tests__/routes.test.d.ts +1 -0
- package/dist/gtfs/__tests__/services.test.d.ts +1 -0
- package/dist/gtfs/__tests__/stops.test.d.ts +1 -0
- package/dist/gtfs/__tests__/time.test.d.ts +1 -0
- package/dist/gtfs/__tests__/transfers.test.d.ts +1 -0
- package/dist/gtfs/__tests__/trips.test.d.ts +1 -0
- package/dist/gtfs/__tests__/utils.test.d.ts +1 -0
- package/dist/gtfs/parser.d.ts +34 -0
- package/dist/gtfs/profiles/__tests__/ch.test.d.ts +1 -0
- package/dist/gtfs/profiles/ch.d.ts +2 -0
- package/dist/gtfs/profiles/standard.d.ts +2 -0
- package/dist/gtfs/routes.d.ts +11 -0
- package/dist/gtfs/services.d.ts +19 -0
- package/dist/gtfs/stops.d.ts +20 -0
- package/dist/gtfs/time.d.ts +17 -0
- package/dist/gtfs/transfers.d.ts +22 -0
- package/dist/gtfs/trips.d.ts +26 -0
- package/dist/gtfs/utils.d.ts +21 -0
- package/dist/index.d.ts +11 -0
- package/dist/routing/__tests__/router.test.d.ts +1 -0
- package/dist/routing/plotter.d.ts +11 -0
- package/dist/routing/query.d.ts +35 -0
- package/dist/routing/result.d.ts +28 -0
- package/dist/routing/route.d.ts +25 -0
- package/dist/routing/router.d.ts +33 -0
- package/dist/stops/__tests__/io.test.d.ts +1 -0
- package/dist/stops/__tests__/stopFinder.test.d.ts +1 -0
- package/dist/stops/i18n.d.ts +10 -0
- package/dist/stops/io.d.ts +4 -0
- package/dist/stops/proto/stops.d.ts +53 -0
- package/dist/stops/stops.d.ts +16 -0
- package/dist/stops/stopsIndex.d.ts +52 -0
- package/dist/timetable/__tests__/io.test.d.ts +1 -0
- package/dist/timetable/__tests__/timetable.test.d.ts +1 -0
- package/dist/timetable/duration.d.ts +51 -0
- package/dist/timetable/io.d.ts +8 -0
- package/dist/timetable/proto/timetable.d.ts +122 -0
- package/dist/timetable/time.d.ts +98 -0
- package/dist/timetable/timetable.d.ts +82 -0
- package/dist/umdIndex.d.ts +9 -0
- package/eslint.config.mjs +52 -0
- package/package.json +109 -0
- package/rollup.config.js +44 -0
- package/src/cli/__tests__/minotor.test.ts +23 -0
- package/src/cli/minotor.ts +112 -0
- package/src/cli/repl.ts +200 -0
- package/src/cli/utils.ts +36 -0
- package/src/gtfs/__tests__/parser.test.ts +591 -0
- package/src/gtfs/__tests__/resources/sample-feed/agency.txt +2 -0
- package/src/gtfs/__tests__/resources/sample-feed/calendar.txt +3 -0
- package/src/gtfs/__tests__/resources/sample-feed/calendar_dates.txt +2 -0
- package/src/gtfs/__tests__/resources/sample-feed/fare_attributes.txt +3 -0
- package/src/gtfs/__tests__/resources/sample-feed/fare_rules.txt +5 -0
- package/src/gtfs/__tests__/resources/sample-feed/frequencies.txt +12 -0
- package/src/gtfs/__tests__/resources/sample-feed/routes.txt +6 -0
- package/src/gtfs/__tests__/resources/sample-feed/sample-feed.zip +0 -0
- package/src/gtfs/__tests__/resources/sample-feed/shapes.txt +1 -0
- package/src/gtfs/__tests__/resources/sample-feed/stop_times.txt +34 -0
- package/src/gtfs/__tests__/resources/sample-feed/stops.txt +10 -0
- package/src/gtfs/__tests__/resources/sample-feed/trips.txt +13 -0
- package/src/gtfs/__tests__/resources/sample-feed.zip +0 -0
- package/src/gtfs/__tests__/routes.test.ts +63 -0
- package/src/gtfs/__tests__/services.test.ts +209 -0
- package/src/gtfs/__tests__/stops.test.ts +177 -0
- package/src/gtfs/__tests__/time.test.ts +27 -0
- package/src/gtfs/__tests__/transfers.test.ts +117 -0
- package/src/gtfs/__tests__/trips.test.ts +463 -0
- package/src/gtfs/__tests__/utils.test.ts +13 -0
- package/src/gtfs/parser.ts +154 -0
- package/src/gtfs/profiles/__tests__/ch.test.ts +43 -0
- package/src/gtfs/profiles/ch.ts +70 -0
- package/src/gtfs/profiles/standard.ts +39 -0
- package/src/gtfs/routes.ts +48 -0
- package/src/gtfs/services.ts +98 -0
- package/src/gtfs/stops.ts +112 -0
- package/src/gtfs/time.ts +33 -0
- package/src/gtfs/transfers.ts +102 -0
- package/src/gtfs/trips.ts +228 -0
- package/src/gtfs/utils.ts +42 -0
- package/src/index.ts +28 -0
- package/src/routing/__tests__/router.test.ts +760 -0
- package/src/routing/plotter.ts +70 -0
- package/src/routing/query.ts +74 -0
- package/src/routing/result.ts +108 -0
- package/src/routing/route.ts +94 -0
- package/src/routing/router.ts +262 -0
- package/src/stops/__tests__/io.test.ts +43 -0
- package/src/stops/__tests__/stopFinder.test.ts +185 -0
- package/src/stops/i18n.ts +40 -0
- package/src/stops/io.ts +94 -0
- package/src/stops/proto/stops.proto +26 -0
- package/src/stops/proto/stops.ts +445 -0
- package/src/stops/stops.ts +24 -0
- package/src/stops/stopsIndex.ts +151 -0
- package/src/timetable/__tests__/io.test.ts +175 -0
- package/src/timetable/__tests__/timetable.test.ts +180 -0
- package/src/timetable/duration.ts +85 -0
- package/src/timetable/io.ts +265 -0
- package/src/timetable/proto/timetable.proto +76 -0
- package/src/timetable/proto/timetable.ts +1304 -0
- package/src/timetable/time.ts +192 -0
- package/src/timetable/timetable.ts +286 -0
- package/src/umdIndex.ts +14 -0
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { Readable } from 'node:stream';
|
|
3
|
+
import { describe, it } from 'node:test';
|
|
4
|
+
|
|
5
|
+
import { parseRoutes } from '../routes.js';
|
|
6
|
+
|
|
7
|
+
describe('GTFS routes parser', () => {
|
|
8
|
+
describe('parsing a well formed stream', () => {
|
|
9
|
+
it('should find valid routes present in the source', async () => {
|
|
10
|
+
const mockedStream = new Readable();
|
|
11
|
+
mockedStream.push(
|
|
12
|
+
'route_id,agency_id,route_short_name,route_long_name,route_desc,route_type\n',
|
|
13
|
+
);
|
|
14
|
+
mockedStream.push('"routeA","agencyA","B1","BUS 1","A bus","3"\n');
|
|
15
|
+
mockedStream.push('"routeB","agencyB","T1","TRAM 1","A tram","0"\n');
|
|
16
|
+
mockedStream.push(null);
|
|
17
|
+
|
|
18
|
+
const routes = await parseRoutes(mockedStream);
|
|
19
|
+
assert.deepEqual(
|
|
20
|
+
routes,
|
|
21
|
+
new Map([
|
|
22
|
+
[
|
|
23
|
+
'routeA',
|
|
24
|
+
{
|
|
25
|
+
type: 'BUS',
|
|
26
|
+
name: 'B1',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
'routeB',
|
|
31
|
+
{
|
|
32
|
+
type: 'TRAM',
|
|
33
|
+
name: 'T1',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
]),
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
it('should ignore routes with invalid route type', async () => {
|
|
40
|
+
const mockedStream = new Readable();
|
|
41
|
+
mockedStream.push(
|
|
42
|
+
'route_id,agency_id,route_short_name,route_long_name,route_desc,route_type\n',
|
|
43
|
+
);
|
|
44
|
+
mockedStream.push('"routeA","agencyA","B1","BUS 1","A bus","3"\n');
|
|
45
|
+
mockedStream.push('"routeB","agencyB","T1","TRAM 1","A tram","8"\n');
|
|
46
|
+
mockedStream.push(null);
|
|
47
|
+
|
|
48
|
+
const routes = await parseRoutes(mockedStream);
|
|
49
|
+
assert.deepEqual(
|
|
50
|
+
routes,
|
|
51
|
+
new Map([
|
|
52
|
+
[
|
|
53
|
+
'routeA',
|
|
54
|
+
{
|
|
55
|
+
type: 'BUS',
|
|
56
|
+
name: 'B1',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
]),
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import * as assert from 'node:assert/strict';
|
|
2
|
+
import { Readable } from 'node:stream';
|
|
3
|
+
import { describe, it } from 'node:test';
|
|
4
|
+
|
|
5
|
+
import { DateTime } from 'luxon';
|
|
6
|
+
|
|
7
|
+
import { parseCalendar, parseCalendarDates, ServiceIds } from '../services.js';
|
|
8
|
+
describe('GTFS calendar parser', () => {
|
|
9
|
+
describe('parsing a well formed stream', () => {
|
|
10
|
+
it('should find valid services present in the source', async () => {
|
|
11
|
+
const mockedStream = new Readable();
|
|
12
|
+
mockedStream.push(
|
|
13
|
+
'service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date\n',
|
|
14
|
+
);
|
|
15
|
+
mockedStream.push(
|
|
16
|
+
'"testId","0","0","0","0","0","1","0","20231115","20231120"\n',
|
|
17
|
+
);
|
|
18
|
+
mockedStream.push(
|
|
19
|
+
'"otherTestId","0","0","0","0","0","1","0","20231010","20231220"\n',
|
|
20
|
+
);
|
|
21
|
+
mockedStream.push(null);
|
|
22
|
+
|
|
23
|
+
const validServiceIds: ServiceIds = new Set();
|
|
24
|
+
await parseCalendar(
|
|
25
|
+
mockedStream,
|
|
26
|
+
validServiceIds,
|
|
27
|
+
DateTime.fromISO('2023-11-18T12:00:00+00:00'),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
assert.deepEqual(validServiceIds, new Set(['testId', 'otherTestId']));
|
|
31
|
+
});
|
|
32
|
+
it('should not alter valid services already present in the input', async () => {
|
|
33
|
+
const mockedStream = new Readable();
|
|
34
|
+
mockedStream.push(
|
|
35
|
+
'service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date\n',
|
|
36
|
+
);
|
|
37
|
+
mockedStream.push(
|
|
38
|
+
'"testId","0","0","0","0","0","1","0","20231115","20231120"\n',
|
|
39
|
+
);
|
|
40
|
+
mockedStream.push(
|
|
41
|
+
'"otherTestId","0","0","0","0","0","1","0","20231010","20231220"\n',
|
|
42
|
+
);
|
|
43
|
+
mockedStream.push(null);
|
|
44
|
+
|
|
45
|
+
const validServiceIds: ServiceIds = new Set([
|
|
46
|
+
'testId',
|
|
47
|
+
'yetAnotherTestId',
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
await parseCalendar(
|
|
51
|
+
mockedStream,
|
|
52
|
+
validServiceIds,
|
|
53
|
+
DateTime.fromISO('2023-11-18T12:00:00+00:00'),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
assert.deepEqual(
|
|
57
|
+
validServiceIds,
|
|
58
|
+
new Set(['testId', 'otherTestId', 'yetAnotherTestId']),
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
it('should ignore services not valid on the right weekday', async () => {
|
|
62
|
+
const mockedStream = new Readable();
|
|
63
|
+
mockedStream.push(
|
|
64
|
+
'service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date\n',
|
|
65
|
+
);
|
|
66
|
+
mockedStream.push(
|
|
67
|
+
'"testId","1","0","0","0","0","0","0","20231115","20231120"\n',
|
|
68
|
+
);
|
|
69
|
+
mockedStream.push(
|
|
70
|
+
'"otherTestId","0","0","0","0","0","1","0","20231010","20231220"\n',
|
|
71
|
+
);
|
|
72
|
+
mockedStream.push(null);
|
|
73
|
+
|
|
74
|
+
const validServiceIds: ServiceIds = new Set();
|
|
75
|
+
await parseCalendar(
|
|
76
|
+
mockedStream,
|
|
77
|
+
validServiceIds,
|
|
78
|
+
DateTime.fromISO('2023-11-18T12:00:00+00:00'),
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
assert.deepEqual(validServiceIds, new Set(['otherTestId']));
|
|
82
|
+
});
|
|
83
|
+
it('should ignore services not valid on the date window', async () => {
|
|
84
|
+
const mockedStream = new Readable();
|
|
85
|
+
mockedStream.push(
|
|
86
|
+
'service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date\n',
|
|
87
|
+
);
|
|
88
|
+
mockedStream.push(
|
|
89
|
+
'"testId","0","0","0","0","0","1","0","20231119","20231120"\n',
|
|
90
|
+
);
|
|
91
|
+
mockedStream.push(
|
|
92
|
+
'"otherTestId","0","0","0","0","0","1","0","20231010","20231220"\n',
|
|
93
|
+
);
|
|
94
|
+
mockedStream.push(null);
|
|
95
|
+
|
|
96
|
+
const validServiceIds: ServiceIds = new Set();
|
|
97
|
+
await parseCalendar(
|
|
98
|
+
mockedStream,
|
|
99
|
+
validServiceIds,
|
|
100
|
+
DateTime.fromISO('2023-11-18T12:00:00+00:00'),
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
assert.deepEqual(validServiceIds, new Set(['otherTestId']));
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('GTFS calendar dates parser', () => {
|
|
109
|
+
describe('parsing a well formed stream', () => {
|
|
110
|
+
it('should find valid services present in the source', async () => {
|
|
111
|
+
const mockedStream = new Readable();
|
|
112
|
+
mockedStream.push('service_id,date,exception_type\n');
|
|
113
|
+
mockedStream.push('"testId","20231115","1"\n');
|
|
114
|
+
mockedStream.push('"otherTestId","20231115","1"\n');
|
|
115
|
+
mockedStream.push(null);
|
|
116
|
+
|
|
117
|
+
const validServiceIds: ServiceIds = new Set();
|
|
118
|
+
await parseCalendarDates(
|
|
119
|
+
mockedStream,
|
|
120
|
+
validServiceIds,
|
|
121
|
+
DateTime.fromISO('2023-11-15T12:00:00+00:00'),
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
assert.deepEqual(validServiceIds, new Set(['testId', 'otherTestId']));
|
|
125
|
+
});
|
|
126
|
+
it('should ignore services valid on a different date', async () => {
|
|
127
|
+
const mockedStream = new Readable();
|
|
128
|
+
mockedStream.push('service_id,date,exception_type\n');
|
|
129
|
+
mockedStream.push('"testId","20231116","1"\n');
|
|
130
|
+
mockedStream.push('"otherTestId","20231115","1"\n');
|
|
131
|
+
mockedStream.push(null);
|
|
132
|
+
|
|
133
|
+
const validServiceIds: ServiceIds = new Set();
|
|
134
|
+
await parseCalendarDates(
|
|
135
|
+
mockedStream,
|
|
136
|
+
validServiceIds,
|
|
137
|
+
DateTime.fromISO('2023-11-15T12:00:00+00:00'),
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
assert.deepEqual(validServiceIds, new Set(['otherTestId']));
|
|
141
|
+
});
|
|
142
|
+
it('should not alter valid services already present in the input', async () => {
|
|
143
|
+
const mockedStream = new Readable();
|
|
144
|
+
mockedStream.push('service_id,date,exception_type\n');
|
|
145
|
+
mockedStream.push('"testId","20231115","1"\n');
|
|
146
|
+
mockedStream.push('"otherTestId","20231115","1"\n');
|
|
147
|
+
mockedStream.push(null);
|
|
148
|
+
|
|
149
|
+
const validServiceIds: ServiceIds = new Set([
|
|
150
|
+
'testId',
|
|
151
|
+
'yetAnotherTestId',
|
|
152
|
+
]);
|
|
153
|
+
await parseCalendarDates(
|
|
154
|
+
mockedStream,
|
|
155
|
+
validServiceIds,
|
|
156
|
+
DateTime.fromISO('2023-11-15T12:00:00+00:00'),
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
assert.deepEqual(
|
|
160
|
+
validServiceIds,
|
|
161
|
+
new Set(['testId', 'otherTestId', 'yetAnotherTestId']),
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
it('should remove services from the input in case of exception', async () => {
|
|
165
|
+
const mockedStream = new Readable();
|
|
166
|
+
mockedStream.push('service_id,date,exception_type\n');
|
|
167
|
+
mockedStream.push('"testId","20231115","2"\n');
|
|
168
|
+
mockedStream.push('"otherTestId","20231115","1"\n');
|
|
169
|
+
mockedStream.push(null);
|
|
170
|
+
|
|
171
|
+
const validServiceIds: ServiceIds = new Set([
|
|
172
|
+
'testId',
|
|
173
|
+
'yetAnotherTestId',
|
|
174
|
+
]);
|
|
175
|
+
await parseCalendarDates(
|
|
176
|
+
mockedStream,
|
|
177
|
+
validServiceIds,
|
|
178
|
+
DateTime.fromISO('2023-11-15T12:00:00+00:00'),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
assert.deepEqual(
|
|
182
|
+
validServiceIds,
|
|
183
|
+
new Set(['otherTestId', 'yetAnotherTestId']),
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
it('should ignore exceptions on a different date', async () => {
|
|
187
|
+
const mockedStream = new Readable();
|
|
188
|
+
mockedStream.push('service_id,date,exception_type\n');
|
|
189
|
+
mockedStream.push('"testId","20231114","2"\n');
|
|
190
|
+
mockedStream.push('"otherTestId","20231115","1"\n');
|
|
191
|
+
mockedStream.push(null);
|
|
192
|
+
|
|
193
|
+
const validServiceIds: ServiceIds = new Set([
|
|
194
|
+
'testId',
|
|
195
|
+
'yetAnotherTestId',
|
|
196
|
+
]);
|
|
197
|
+
await parseCalendarDates(
|
|
198
|
+
mockedStream,
|
|
199
|
+
validServiceIds,
|
|
200
|
+
DateTime.fromISO('2023-11-15T12:00:00+00:00'),
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
assert.deepEqual(
|
|
204
|
+
validServiceIds,
|
|
205
|
+
new Set(['testId', 'otherTestId', 'yetAnotherTestId']),
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
});
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { Readable } from 'node:stream';
|
|
3
|
+
import { describe, it } from 'node:test';
|
|
4
|
+
|
|
5
|
+
import { StopsMap } from '../../stops/stops.js';
|
|
6
|
+
import { chGtfsProfile } from '../profiles/ch.js';
|
|
7
|
+
import { parseStops } from '../stops.js';
|
|
8
|
+
|
|
9
|
+
describe('GTFS stops parser', () => {
|
|
10
|
+
describe('parsing a well formed stream', () => {
|
|
11
|
+
it('should parse valid stops present in the source', async () => {
|
|
12
|
+
const mockedStream = new Readable();
|
|
13
|
+
mockedStream.push(
|
|
14
|
+
'stop_id,stop_name,stop_lat,stop_lon,location_type,parent_station\n',
|
|
15
|
+
);
|
|
16
|
+
mockedStream.push(
|
|
17
|
+
'"Parent8587255","Fribourg, Tilleul/Cathédrale","46.8061375857565","7.16145029437328","1",""\n',
|
|
18
|
+
);
|
|
19
|
+
mockedStream.push(
|
|
20
|
+
'"Parent8504100","Fribourg/Freiburg","46.8031492395272","7.15104780338173","1",""\n',
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
mockedStream.push(null);
|
|
24
|
+
const stops = await parseStops(
|
|
25
|
+
mockedStream,
|
|
26
|
+
chGtfsProfile.platformParser,
|
|
27
|
+
);
|
|
28
|
+
const expectedStops: StopsMap = new Map([
|
|
29
|
+
[
|
|
30
|
+
'Parent8587255',
|
|
31
|
+
{
|
|
32
|
+
id: 'Parent8587255',
|
|
33
|
+
lat: 46.8061375857565,
|
|
34
|
+
locationType: 'STATION',
|
|
35
|
+
lon: 7.16145029437328,
|
|
36
|
+
name: 'Fribourg, Tilleul/Cathédrale',
|
|
37
|
+
children: [],
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
[
|
|
41
|
+
'Parent8504100',
|
|
42
|
+
{
|
|
43
|
+
id: 'Parent8504100',
|
|
44
|
+
lat: 46.8031492395272,
|
|
45
|
+
locationType: 'STATION',
|
|
46
|
+
lon: 7.15104780338173,
|
|
47
|
+
name: 'Fribourg/Freiburg',
|
|
48
|
+
children: [],
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
assert.deepEqual(stops, expectedStops);
|
|
54
|
+
});
|
|
55
|
+
it('should parse nested stops', async () => {
|
|
56
|
+
const mockedStream = new Readable();
|
|
57
|
+
mockedStream.push(
|
|
58
|
+
'stop_id,stop_name,stop_lat,stop_lon,location_type,parent_station\n',
|
|
59
|
+
);
|
|
60
|
+
mockedStream.push(
|
|
61
|
+
'"8504100:0:1","Fribourg/Freiburg","46.8018210323626","7.14993389242926","","Parent8504100"\n',
|
|
62
|
+
);
|
|
63
|
+
mockedStream.push(
|
|
64
|
+
'"8504100:0:2","Fribourg/Freiburg","46.8010031847878","7.14920625704902","","Parent8504100"\n',
|
|
65
|
+
);
|
|
66
|
+
mockedStream.push(
|
|
67
|
+
'"Parent8504100","Fribourg/Freiburg","46.8031492395272","7.15104780338173","1",""\n',
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
mockedStream.push(null);
|
|
71
|
+
|
|
72
|
+
const stops = await parseStops(mockedStream);
|
|
73
|
+
const expectedStops: StopsMap = new Map([
|
|
74
|
+
[
|
|
75
|
+
'8504100:0:1',
|
|
76
|
+
{
|
|
77
|
+
id: '8504100:0:1',
|
|
78
|
+
lat: 46.8018210323626,
|
|
79
|
+
lon: 7.14993389242926,
|
|
80
|
+
name: 'Fribourg/Freiburg',
|
|
81
|
+
parent: 'Parent8504100',
|
|
82
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
83
|
+
children: [],
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
[
|
|
87
|
+
'8504100:0:2',
|
|
88
|
+
{
|
|
89
|
+
id: '8504100:0:2',
|
|
90
|
+
lat: 46.8010031847878,
|
|
91
|
+
lon: 7.14920625704902,
|
|
92
|
+
name: 'Fribourg/Freiburg',
|
|
93
|
+
parent: 'Parent8504100',
|
|
94
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
95
|
+
children: [],
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
[
|
|
99
|
+
'Parent8504100',
|
|
100
|
+
{
|
|
101
|
+
id: 'Parent8504100',
|
|
102
|
+
children: ['8504100:0:1', '8504100:0:2'],
|
|
103
|
+
lat: 46.8031492395272,
|
|
104
|
+
locationType: 'STATION',
|
|
105
|
+
lon: 7.15104780338173,
|
|
106
|
+
name: 'Fribourg/Freiburg',
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
]);
|
|
110
|
+
|
|
111
|
+
assert.deepEqual(stops, expectedStops);
|
|
112
|
+
});
|
|
113
|
+
it('should parse the platform', async () => {
|
|
114
|
+
const mockedStream = new Readable();
|
|
115
|
+
mockedStream.push(
|
|
116
|
+
'stop_id,stop_name,stop_lat,stop_lon,location_type,parent_station\n',
|
|
117
|
+
);
|
|
118
|
+
mockedStream.push(
|
|
119
|
+
'"8504100:0:1","Fribourg/Freiburg","46.8018210323626","7.14993389242926","","Parent8504100"\n',
|
|
120
|
+
);
|
|
121
|
+
mockedStream.push(
|
|
122
|
+
'"8504100:0:2","Fribourg/Freiburg","46.8010031847878","7.14920625704902","","Parent8504100"\n',
|
|
123
|
+
);
|
|
124
|
+
mockedStream.push(
|
|
125
|
+
'"Parent8504100","Fribourg/Freiburg","46.8031492395272","7.15104780338173","1",""\n',
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
mockedStream.push(null);
|
|
129
|
+
|
|
130
|
+
const stops = await parseStops(
|
|
131
|
+
mockedStream,
|
|
132
|
+
chGtfsProfile.platformParser,
|
|
133
|
+
);
|
|
134
|
+
const expectedStops: StopsMap = new Map([
|
|
135
|
+
[
|
|
136
|
+
'8504100:0:1',
|
|
137
|
+
{
|
|
138
|
+
id: '8504100:0:1',
|
|
139
|
+
lat: 46.8018210323626,
|
|
140
|
+
lon: 7.14993389242926,
|
|
141
|
+
name: 'Fribourg/Freiburg',
|
|
142
|
+
parent: 'Parent8504100',
|
|
143
|
+
platform: '1',
|
|
144
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
145
|
+
children: [],
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
[
|
|
149
|
+
'8504100:0:2',
|
|
150
|
+
{
|
|
151
|
+
id: '8504100:0:2',
|
|
152
|
+
lat: 46.8010031847878,
|
|
153
|
+
lon: 7.14920625704902,
|
|
154
|
+
name: 'Fribourg/Freiburg',
|
|
155
|
+
parent: 'Parent8504100',
|
|
156
|
+
platform: '2',
|
|
157
|
+
locationType: 'SIMPLE_STOP_OR_PLATFORM',
|
|
158
|
+
children: [],
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
[
|
|
162
|
+
'Parent8504100',
|
|
163
|
+
{
|
|
164
|
+
id: 'Parent8504100',
|
|
165
|
+
children: ['8504100:0:1', '8504100:0:2'],
|
|
166
|
+
lat: 46.8031492395272,
|
|
167
|
+
locationType: 'STATION',
|
|
168
|
+
lon: 7.15104780338173,
|
|
169
|
+
name: 'Fribourg/Freiburg',
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
]);
|
|
173
|
+
|
|
174
|
+
assert.deepEqual(stops, expectedStops);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as assert from 'node:assert/strict';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
|
|
4
|
+
import { DateTime } from 'luxon';
|
|
5
|
+
|
|
6
|
+
import { toGtfsDate, toTime } from '../time.js';
|
|
7
|
+
|
|
8
|
+
describe('Date converter', () => {
|
|
9
|
+
it('should convert a valid date to GTFS format', () => {
|
|
10
|
+
assert.equal(
|
|
11
|
+
toGtfsDate(DateTime.fromISO('2007-01-10T12:00:00+00:00')),
|
|
12
|
+
20070110,
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('Time converter', () => {
|
|
18
|
+
it('should convert a valid service time to numerical format', () => {
|
|
19
|
+
assert.equal(toTime('10:23:54').toSeconds(), 37434);
|
|
20
|
+
});
|
|
21
|
+
it('should convert a valid service time after midnight to numerical format', () => {
|
|
22
|
+
assert.equal(toTime('25:13:31').toSeconds(), 90811);
|
|
23
|
+
});
|
|
24
|
+
it('should throw when trying to convert an invalid time', () => {
|
|
25
|
+
assert.throws(() => toTime('2513:31'));
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { Readable } from 'node:stream';
|
|
3
|
+
import { describe, it } from 'node:test';
|
|
4
|
+
|
|
5
|
+
import { Duration } from '../../timetable/duration.js';
|
|
6
|
+
import { parseTransfers } from '../transfers.js';
|
|
7
|
+
|
|
8
|
+
describe('GTFS transfers parser', () => {
|
|
9
|
+
it('should correctly parse valid transfers', async () => {
|
|
10
|
+
const mockedStream = new Readable();
|
|
11
|
+
mockedStream.push(
|
|
12
|
+
'from_stop_id,to_stop_id,transfer_type,min_transfer_time\n',
|
|
13
|
+
);
|
|
14
|
+
mockedStream.push('"1100084","8014440:0:1","2","180"\n');
|
|
15
|
+
mockedStream.push('"1100097","8014447","2","240"\n');
|
|
16
|
+
mockedStream.push(null);
|
|
17
|
+
|
|
18
|
+
const transfers = await parseTransfers(mockedStream);
|
|
19
|
+
const expectedTransfers = new Map([
|
|
20
|
+
[
|
|
21
|
+
'1100084',
|
|
22
|
+
[
|
|
23
|
+
{
|
|
24
|
+
destination: '8014440:0:1',
|
|
25
|
+
type: 'REQUIRES_MINIMAL_TIME',
|
|
26
|
+
minTransferTime: Duration.fromSeconds(180),
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
],
|
|
30
|
+
[
|
|
31
|
+
'1100097',
|
|
32
|
+
[
|
|
33
|
+
{
|
|
34
|
+
destination: '8014447',
|
|
35
|
+
type: 'REQUIRES_MINIMAL_TIME',
|
|
36
|
+
minTransferTime: Duration.fromSeconds(240),
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
],
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
assert.deepEqual(transfers, expectedTransfers);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should ignore impossible transfer types', async () => {
|
|
46
|
+
const mockedStream = new Readable();
|
|
47
|
+
mockedStream.push(
|
|
48
|
+
'from_stop_id,to_stop_id,transfer_type,min_transfer_time\n',
|
|
49
|
+
);
|
|
50
|
+
mockedStream.push('"1100084","8014440:0:1","3","180"\n');
|
|
51
|
+
mockedStream.push('"1100097","8014447","5","240"\n');
|
|
52
|
+
mockedStream.push(null);
|
|
53
|
+
|
|
54
|
+
const transfers = await parseTransfers(mockedStream);
|
|
55
|
+
assert.deepEqual(transfers, new Map());
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should ignore unsupported transfer types between routes', async () => {
|
|
59
|
+
const mockedStream = new Readable();
|
|
60
|
+
mockedStream.push(
|
|
61
|
+
'from_route_id,to_route_id,transfer_type,min_transfer_time\n',
|
|
62
|
+
);
|
|
63
|
+
mockedStream.push('"1100084","8014440","2","180"\n');
|
|
64
|
+
mockedStream.push(null);
|
|
65
|
+
|
|
66
|
+
const transfers = await parseTransfers(mockedStream);
|
|
67
|
+
assert.deepEqual(transfers, new Map());
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should ignore unsupported transfer types between trips', async () => {
|
|
71
|
+
const mockedStream = new Readable();
|
|
72
|
+
mockedStream.push(
|
|
73
|
+
'from_trip_id,to_trip_id,transfer_type,min_transfer_time\n',
|
|
74
|
+
);
|
|
75
|
+
mockedStream.push('"1100084","8014440","2","180"\n');
|
|
76
|
+
mockedStream.push(null);
|
|
77
|
+
|
|
78
|
+
const transfers = await parseTransfers(mockedStream);
|
|
79
|
+
assert.deepEqual(transfers, new Map());
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should allow missing minimum transfer time', async () => {
|
|
83
|
+
const mockedStream = new Readable();
|
|
84
|
+
mockedStream.push(
|
|
85
|
+
'from_stop_id,to_stop_id,transfer_type,min_transfer_time\n',
|
|
86
|
+
);
|
|
87
|
+
mockedStream.push('"1100084","8014440:0:1","2"\n');
|
|
88
|
+
mockedStream.push(null);
|
|
89
|
+
|
|
90
|
+
const transfers = await parseTransfers(mockedStream);
|
|
91
|
+
assert.deepEqual(
|
|
92
|
+
transfers,
|
|
93
|
+
new Map([
|
|
94
|
+
[
|
|
95
|
+
'1100084',
|
|
96
|
+
[
|
|
97
|
+
{
|
|
98
|
+
destination: '8014440:0:1',
|
|
99
|
+
type: 'REQUIRES_MINIMAL_TIME',
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
],
|
|
103
|
+
]),
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should handle empty transfers', async () => {
|
|
108
|
+
const mockedStream = new Readable();
|
|
109
|
+
mockedStream.push(
|
|
110
|
+
'from_stop_id,to_stop_id,transfer_type,min_transfer_time\n',
|
|
111
|
+
);
|
|
112
|
+
mockedStream.push(null);
|
|
113
|
+
|
|
114
|
+
const transfers = await parseTransfers(mockedStream);
|
|
115
|
+
assert.deepEqual(transfers, new Map());
|
|
116
|
+
});
|
|
117
|
+
});
|