gtfs-to-html 2.4.4 → 2.5.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/CHANGELOG.md +7 -0
- package/app/index.js +5 -3
- package/lib/geojson-utils.js +19 -26
- package/lib/gtfs-to-html.js +8 -15
- package/lib/log-utils.js +2 -2
- package/lib/utils.js +291 -326
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.5.0] - 2022-12-31
|
|
9
|
+
|
|
10
|
+
### Updated
|
|
11
|
+
|
|
12
|
+
- Updated to node-gtfs v4
|
|
13
|
+
- Dependency updates
|
|
14
|
+
|
|
8
15
|
## [2.4.4] - 2022-12-22
|
|
9
16
|
|
|
10
17
|
### Updated
|
package/app/index.js
CHANGED
|
@@ -39,7 +39,9 @@ config.log = console.log;
|
|
|
39
39
|
config.logWarning = console.warn;
|
|
40
40
|
config.logError = console.error;
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
try {
|
|
43
|
+
openDb(config);
|
|
44
|
+
} catch (error) {
|
|
43
45
|
if (error instanceof Error && error.code === 'SQLITE_CANTOPEN') {
|
|
44
46
|
config.logError(
|
|
45
47
|
`Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
|
|
@@ -47,7 +49,7 @@ openDb(config).catch((error) => {
|
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
throw error;
|
|
50
|
-
}
|
|
52
|
+
}
|
|
51
53
|
|
|
52
54
|
/*
|
|
53
55
|
* Show all timetable pages
|
|
@@ -56,7 +58,7 @@ router.get('/', async (request, response, next) => {
|
|
|
56
58
|
try {
|
|
57
59
|
const timetablePages = [];
|
|
58
60
|
const timetablePageIds = map(
|
|
59
|
-
|
|
61
|
+
getTimetablePagesForAgency(config),
|
|
60
62
|
'timetable_page_id'
|
|
61
63
|
);
|
|
62
64
|
|
package/lib/geojson-utils.js
CHANGED
|
@@ -61,27 +61,22 @@ const simplifyGeoJSON = (geojson, config) => {
|
|
|
61
61
|
/*
|
|
62
62
|
* Get the geoJSON for a timetable.
|
|
63
63
|
*/
|
|
64
|
-
export
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
timetable.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
trip_id: timetable.orderedTrips.map((trip) => trip.trip_id),
|
|
81
|
-
})
|
|
82
|
-
)
|
|
83
|
-
),
|
|
84
|
-
]);
|
|
64
|
+
export function getTimetableGeoJSON(timetable, config) {
|
|
65
|
+
const shapesGeojsons = timetable.route_ids.map((routeId) =>
|
|
66
|
+
getShapesAsGeoJSON({
|
|
67
|
+
route_id: routeId,
|
|
68
|
+
direction_id: timetable.direction_id,
|
|
69
|
+
trip_id: timetable.orderedTrips.map((trip) => trip.trip_id),
|
|
70
|
+
})
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const stopsGeojsons = timetable.route_ids.map((routeId) =>
|
|
74
|
+
getStopsAsGeoJSON({
|
|
75
|
+
route_id: routeId,
|
|
76
|
+
direction_id: timetable.direction_id,
|
|
77
|
+
trip_id: timetable.orderedTrips.map((trip) => trip.trip_id),
|
|
78
|
+
})
|
|
79
|
+
);
|
|
85
80
|
|
|
86
81
|
const geojson = mergeGeojson(...shapesGeojsons, ...stopsGeojsons);
|
|
87
82
|
return simplifyGeoJSON(geojson, config);
|
|
@@ -90,11 +85,9 @@ export async function getTimetableGeoJSON(timetable, config) {
|
|
|
90
85
|
/*
|
|
91
86
|
* Get the geoJSON for an agency (all routes and stops).
|
|
92
87
|
*/
|
|
93
|
-
export
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
getStopsAsGeoJSON(),
|
|
97
|
-
]);
|
|
88
|
+
export function getAgencyGeoJSON(config) {
|
|
89
|
+
const shapesGeojsons = getShapesAsGeoJSON();
|
|
90
|
+
const stopsGeojsons = getStopsAsGeoJSON();
|
|
98
91
|
|
|
99
92
|
const geojson = mergeGeojson(shapesGeojsons, stopsGeojsons);
|
|
100
93
|
return simplifyGeoJSON(geojson, config);
|
package/lib/gtfs-to-html.js
CHANGED
|
@@ -2,7 +2,7 @@ import path from 'node:path';
|
|
|
2
2
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
3
|
|
|
4
4
|
import { map } from 'lodash-es';
|
|
5
|
-
import { openDb,
|
|
5
|
+
import { openDb, importGtfs } from 'gtfs';
|
|
6
6
|
import sanitize from 'sanitize-filename';
|
|
7
7
|
import Timer from 'timer-machine';
|
|
8
8
|
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
/*
|
|
36
36
|
* Generate HTML timetables from GTFS.
|
|
37
37
|
*/
|
|
38
|
+
/* eslint-disable complexity */
|
|
38
39
|
const gtfsToHtml = async (initialConfig) => {
|
|
39
40
|
const config = setDefaultConfig(initialConfig);
|
|
40
41
|
const timer = new Timer();
|
|
@@ -45,7 +46,9 @@ const gtfsToHtml = async (initialConfig) => {
|
|
|
45
46
|
|
|
46
47
|
timer.start();
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
try {
|
|
50
|
+
openDb(config);
|
|
51
|
+
} catch (error) {
|
|
49
52
|
if (error instanceof Error && error.code === 'SQLITE_CANTOPEN') {
|
|
50
53
|
config.logError(
|
|
51
54
|
`Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
|
|
@@ -53,17 +56,6 @@ const gtfsToHtml = async (initialConfig) => {
|
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
throw error;
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
if (config.debug === true) {
|
|
59
|
-
const db = getDb();
|
|
60
|
-
|
|
61
|
-
db.on('profile', (sql, nsecs) => {
|
|
62
|
-
if (sql.startsWith('SELECT')) {
|
|
63
|
-
console.log(sql);
|
|
64
|
-
console.log(nsecs);
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
59
|
}
|
|
68
60
|
|
|
69
61
|
if (!config.agencies || config.agencies.length === 0) {
|
|
@@ -91,7 +83,7 @@ const gtfsToHtml = async (initialConfig) => {
|
|
|
91
83
|
|
|
92
84
|
const timetablePages = [];
|
|
93
85
|
const timetablePageIds = map(
|
|
94
|
-
|
|
86
|
+
getTimetablePagesForAgency(config),
|
|
95
87
|
'timetable_page_id'
|
|
96
88
|
);
|
|
97
89
|
await prepDirectory(exportPath);
|
|
@@ -190,7 +182,7 @@ const gtfsToHtml = async (initialConfig) => {
|
|
|
190
182
|
}
|
|
191
183
|
|
|
192
184
|
// Generate output log.txt
|
|
193
|
-
const logText =
|
|
185
|
+
const logText = generateLogText(outputStats, config);
|
|
194
186
|
await writeFile(path.join(exportPath, 'log.txt'), logText);
|
|
195
187
|
|
|
196
188
|
// Zip output, if specified
|
|
@@ -217,5 +209,6 @@ const gtfsToHtml = async (initialConfig) => {
|
|
|
217
209
|
|
|
218
210
|
timer.stop();
|
|
219
211
|
};
|
|
212
|
+
/* eslint-enable complexity */
|
|
220
213
|
|
|
221
214
|
export default gtfsToHtml;
|
package/lib/log-utils.js
CHANGED
|
@@ -11,8 +11,8 @@ pe.start();
|
|
|
11
11
|
/*
|
|
12
12
|
* Creates text for a log of output details.
|
|
13
13
|
*/
|
|
14
|
-
export
|
|
15
|
-
const feedInfo =
|
|
14
|
+
export function generateLogText(outputStats, config) {
|
|
15
|
+
const feedInfo = getFeedInfo();
|
|
16
16
|
const feedVersion =
|
|
17
17
|
feedInfo.length > 0 && feedInfo[0].feed_version
|
|
18
18
|
? feedInfo[0].feed_version
|
package/lib/utils.js
CHANGED
|
@@ -27,7 +27,6 @@ import {
|
|
|
27
27
|
getTimetableNotesReferences,
|
|
28
28
|
getTimetableNotes,
|
|
29
29
|
getRoutes,
|
|
30
|
-
getDb,
|
|
31
30
|
getCalendars,
|
|
32
31
|
getTimetableStopOrders,
|
|
33
32
|
getStops,
|
|
@@ -37,6 +36,7 @@ import {
|
|
|
37
36
|
getTimetables,
|
|
38
37
|
getTimetablePages,
|
|
39
38
|
getAgencies,
|
|
39
|
+
openDb,
|
|
40
40
|
} from 'gtfs';
|
|
41
41
|
import { stringify } from 'csv-stringify';
|
|
42
42
|
import moment from 'moment';
|
|
@@ -275,8 +275,8 @@ const sortTripsByStoptimeAtStop = (trips, stopId) =>
|
|
|
275
275
|
/*
|
|
276
276
|
* Get all calendar dates for a specific timetable.
|
|
277
277
|
*/
|
|
278
|
-
const getCalendarDatesForTimetable =
|
|
279
|
-
const calendarDates =
|
|
278
|
+
const getCalendarDatesForTimetable = (timetable, config) => {
|
|
279
|
+
const calendarDates = getCalendarDates(
|
|
280
280
|
{
|
|
281
281
|
service_id: timetable.service_ids,
|
|
282
282
|
},
|
|
@@ -334,8 +334,8 @@ const getDaysFromCalendars = (calendars) => {
|
|
|
334
334
|
/*
|
|
335
335
|
* Get the `trip_headsign` for a specific timetable.
|
|
336
336
|
*/
|
|
337
|
-
const getDirectionHeadsignFromTimetable =
|
|
338
|
-
const trips =
|
|
337
|
+
const getDirectionHeadsignFromTimetable = (timetable) => {
|
|
338
|
+
const trips = getTrips(
|
|
339
339
|
{
|
|
340
340
|
direction_id: timetable.direction_id,
|
|
341
341
|
route_id: timetable.route_ids,
|
|
@@ -353,31 +353,31 @@ const getDirectionHeadsignFromTimetable = async (timetable) => {
|
|
|
353
353
|
/*
|
|
354
354
|
* Get the notes for a specific timetable.
|
|
355
355
|
*/
|
|
356
|
-
const getTimetableNotesForTimetable =
|
|
356
|
+
const getTimetableNotesForTimetable = (timetable, config) => {
|
|
357
357
|
const noteReferences = [
|
|
358
358
|
// Get all notes for this timetable.
|
|
359
|
-
...
|
|
359
|
+
...getTimetableNotesReferences({
|
|
360
360
|
timetable_id: timetable.timetable_id,
|
|
361
|
-
})
|
|
361
|
+
}),
|
|
362
362
|
|
|
363
363
|
// Get all notes for this route.
|
|
364
|
-
...
|
|
364
|
+
...getTimetableNotesReferences({
|
|
365
365
|
route_id: timetable.routes.map((route) => route.route_id),
|
|
366
366
|
timetable_id: null,
|
|
367
|
-
})
|
|
367
|
+
}),
|
|
368
368
|
|
|
369
369
|
// Get all notes for all trips in this timetable.
|
|
370
|
-
...
|
|
370
|
+
...getTimetableNotesReferences({
|
|
371
371
|
trip_id: timetable.orderedTrips.map((trip) => trip.trip_id),
|
|
372
|
-
})
|
|
372
|
+
}),
|
|
373
373
|
|
|
374
374
|
// Get all notes for all stops in this timetable.
|
|
375
|
-
...
|
|
375
|
+
...getTimetableNotesReferences({
|
|
376
376
|
stop_id: timetable.stops.map((stop) => stop.stop_id),
|
|
377
377
|
trip_id: null,
|
|
378
378
|
route_id: null,
|
|
379
379
|
timetable_id: null,
|
|
380
|
-
})
|
|
380
|
+
}),
|
|
381
381
|
];
|
|
382
382
|
|
|
383
383
|
const usedNoteReferences = [];
|
|
@@ -416,7 +416,7 @@ const getTimetableNotesForTimetable = async (timetable, config) => {
|
|
|
416
416
|
}
|
|
417
417
|
}
|
|
418
418
|
|
|
419
|
-
const notes =
|
|
419
|
+
const notes = getTimetableNotes({
|
|
420
420
|
note_id: usedNoteReferences.map((noteReference) => noteReference.note_id),
|
|
421
421
|
});
|
|
422
422
|
|
|
@@ -445,14 +445,14 @@ const getTimetableNotesForTimetable = async (timetable, config) => {
|
|
|
445
445
|
* Create a timetable page from a single timetable. Used if no
|
|
446
446
|
* `timetable_pages.txt` is present.
|
|
447
447
|
*/
|
|
448
|
-
const convertTimetableToTimetablePage =
|
|
448
|
+
const convertTimetableToTimetablePage = (timetable, config) => {
|
|
449
449
|
if (!timetable.routes) {
|
|
450
|
-
timetable.routes =
|
|
450
|
+
timetable.routes = getRoutes({
|
|
451
451
|
route_id: timetable.route_ids,
|
|
452
452
|
});
|
|
453
453
|
}
|
|
454
454
|
|
|
455
|
-
const filename =
|
|
455
|
+
const filename = generateFileName(timetable, config);
|
|
456
456
|
|
|
457
457
|
return {
|
|
458
458
|
timetable_page_id: timetable.timetable_id,
|
|
@@ -517,61 +517,47 @@ const convertRouteToTimetablePage = (
|
|
|
517
517
|
* Create timetable pages for all routes in an agency. Used if no
|
|
518
518
|
* `timetables.txt` is present.
|
|
519
519
|
*/
|
|
520
|
-
const convertRoutesToTimetablePages =
|
|
521
|
-
const db =
|
|
522
|
-
const routes =
|
|
523
|
-
const calendars =
|
|
520
|
+
const convertRoutesToTimetablePages = (config) => {
|
|
521
|
+
const db = openDb(config);
|
|
522
|
+
const routes = getRoutes();
|
|
523
|
+
const calendars = getCalendars();
|
|
524
524
|
|
|
525
525
|
// Find all calendar dates with service_ids not present in `calendar.txt`.
|
|
526
526
|
const serviceIds = calendars.map((calendar) => calendar.service_id);
|
|
527
|
-
const calendarDates =
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
527
|
+
const calendarDates = db
|
|
528
|
+
.prepare(
|
|
529
|
+
`SELECT * FROM calendar_dates WHERE exception_type = 1 AND service_id NOT IN (${serviceIds
|
|
530
|
+
.map((serviceId) => `'${serviceId}'`)
|
|
531
|
+
.join(', ')})`
|
|
532
|
+
)
|
|
533
|
+
.all();
|
|
534
|
+
|
|
535
|
+
const timetablePages = routes.map((route) => {
|
|
536
|
+
const trips = getTrips(
|
|
537
|
+
{
|
|
538
|
+
route_id: route.route_id,
|
|
539
|
+
},
|
|
540
|
+
['trip_headsign', 'direction_id']
|
|
541
|
+
);
|
|
542
|
+
const directions = uniqBy(trips, (trip) => trip.direction_id);
|
|
543
|
+
const dayGroups = groupBy(calendars, calendarToCalendarCode);
|
|
544
|
+
const calendarDateGroups = groupBy(calendarDates, 'service_id');
|
|
532
545
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
return Promise.all(
|
|
546
|
-
directions.map((direction) =>
|
|
547
|
-
Promise.all([
|
|
548
|
-
Promise.all(
|
|
549
|
-
Object.values(dayGroups).map((calendars) =>
|
|
550
|
-
convertRouteToTimetablePage(
|
|
551
|
-
route,
|
|
552
|
-
direction,
|
|
553
|
-
calendars,
|
|
554
|
-
null,
|
|
555
|
-
config
|
|
556
|
-
)
|
|
557
|
-
)
|
|
558
|
-
),
|
|
559
|
-
Promise.all(
|
|
560
|
-
Object.values(calendarDateGroups).map((calendarDates) =>
|
|
561
|
-
convertRouteToTimetablePage(
|
|
562
|
-
route,
|
|
563
|
-
direction,
|
|
564
|
-
null,
|
|
565
|
-
calendarDates,
|
|
566
|
-
config
|
|
567
|
-
)
|
|
568
|
-
)
|
|
569
|
-
),
|
|
570
|
-
])
|
|
546
|
+
return directions.map((direction) => [
|
|
547
|
+
Object.values(dayGroups).map((calendars) =>
|
|
548
|
+
convertRouteToTimetablePage(route, direction, calendars, null, config)
|
|
549
|
+
),
|
|
550
|
+
Object.values(calendarDateGroups).map((calendarDates) =>
|
|
551
|
+
convertRouteToTimetablePage(
|
|
552
|
+
route,
|
|
553
|
+
direction,
|
|
554
|
+
null,
|
|
555
|
+
calendarDates,
|
|
556
|
+
config
|
|
571
557
|
)
|
|
572
|
-
)
|
|
573
|
-
|
|
574
|
-
);
|
|
558
|
+
),
|
|
559
|
+
]);
|
|
560
|
+
});
|
|
575
561
|
|
|
576
562
|
return compact(flattenDeep(timetablePages));
|
|
577
563
|
};
|
|
@@ -652,9 +638,9 @@ const duplicateStopsForDifferentArrivalDeparture = (
|
|
|
652
638
|
/*
|
|
653
639
|
* Get a sorted array of stop_ids for a specific timetable.
|
|
654
640
|
*/
|
|
655
|
-
const getStopOrder =
|
|
641
|
+
const getStopOrder = (timetable, config) => {
|
|
656
642
|
// First, check if `timetable_stop_order.txt` for route exists
|
|
657
|
-
const timetableStopOrders =
|
|
643
|
+
const timetableStopOrders = getTimetableStopOrders(
|
|
658
644
|
{
|
|
659
645
|
timetable_id: timetable.timetable_id,
|
|
660
646
|
},
|
|
@@ -714,45 +700,43 @@ const getStopOrder = async (timetable, config) => {
|
|
|
714
700
|
/*
|
|
715
701
|
* Get an array of stops for a specific timetable.
|
|
716
702
|
*/
|
|
717
|
-
const getStopsForTimetable =
|
|
703
|
+
const getStopsForTimetable = (timetable, config) => {
|
|
718
704
|
if (timetable.orderedTrips.length === 0) {
|
|
719
705
|
return [];
|
|
720
706
|
}
|
|
721
707
|
|
|
722
|
-
const orderedStopIds =
|
|
723
|
-
const orderedStops =
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
});
|
|
708
|
+
const orderedStopIds = getStopOrder(timetable, config);
|
|
709
|
+
const orderedStops = orderedStopIds.map((stopId, index) => {
|
|
710
|
+
const stops = getStops({
|
|
711
|
+
stop_id: stopId,
|
|
712
|
+
});
|
|
728
713
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
714
|
+
if (stops.length === 0) {
|
|
715
|
+
throw new Error(
|
|
716
|
+
`No stop found found for stop_id=${stopId} in timetable_id=${timetable.timetable_id}`
|
|
717
|
+
);
|
|
718
|
+
}
|
|
734
719
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
720
|
+
const stop = {
|
|
721
|
+
...stops[0],
|
|
722
|
+
trips: [],
|
|
723
|
+
};
|
|
739
724
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
725
|
+
if (
|
|
726
|
+
index < orderedStopIds.length - 1 &&
|
|
727
|
+
stopId === orderedStopIds[index + 1]
|
|
728
|
+
) {
|
|
729
|
+
stop.type = 'arrival';
|
|
730
|
+
} else if (index > 0 && stopId === orderedStopIds[index - 1]) {
|
|
731
|
+
stop.type = 'departure';
|
|
732
|
+
}
|
|
748
733
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
);
|
|
734
|
+
return stop;
|
|
735
|
+
});
|
|
752
736
|
|
|
753
737
|
// If `showStopCity` is true, look up stop attributes.
|
|
754
738
|
if (timetable.showStopCity) {
|
|
755
|
-
const stopAttributes =
|
|
739
|
+
const stopAttributes = getStopAttributes({
|
|
756
740
|
stop_id: orderedStopIds,
|
|
757
741
|
});
|
|
758
742
|
|
|
@@ -773,8 +757,8 @@ const getStopsForTimetable = async (timetable, config) => {
|
|
|
773
757
|
/*
|
|
774
758
|
* Get all calendars from a specific timetable.
|
|
775
759
|
*/
|
|
776
|
-
const getCalendarsFromTimetable =
|
|
777
|
-
const db =
|
|
760
|
+
const getCalendarsFromTimetable = (timetable) => {
|
|
761
|
+
const db = openDb();
|
|
778
762
|
let whereClause = '';
|
|
779
763
|
const whereClauses = [];
|
|
780
764
|
|
|
@@ -808,14 +792,14 @@ const getCalendarsFromTimetable = async (timetable) => {
|
|
|
808
792
|
whereClause = `WHERE ${whereClauses.join(' AND ')}`;
|
|
809
793
|
}
|
|
810
794
|
|
|
811
|
-
return db.
|
|
795
|
+
return db.prepare(`SELECT * FROM calendar ${whereClause}`).all();
|
|
812
796
|
};
|
|
813
797
|
|
|
814
798
|
/*
|
|
815
799
|
* Get all calendar date service ids for an agency between two dates.
|
|
816
800
|
*/
|
|
817
|
-
const getCalendarDatesServiceIds =
|
|
818
|
-
const db =
|
|
801
|
+
const getCalendarDatesServiceIds = (startDate, endDate) => {
|
|
802
|
+
const db = openDb();
|
|
819
803
|
const whereClauses = ['exception_type = 1'];
|
|
820
804
|
|
|
821
805
|
if (endDate) {
|
|
@@ -826,11 +810,13 @@ const getCalendarDatesServiceIds = async (startDate, endDate) => {
|
|
|
826
810
|
whereClauses.push(`date >= ${sqlString.escape(startDate)}`);
|
|
827
811
|
}
|
|
828
812
|
|
|
829
|
-
const calendarDates =
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
813
|
+
const calendarDates = db
|
|
814
|
+
.prepare(
|
|
815
|
+
`SELECT DISTINCT service_id FROM calendar_dates WHERE ${whereClauses.join(
|
|
816
|
+
' AND '
|
|
817
|
+
)}`
|
|
818
|
+
)
|
|
819
|
+
.all();
|
|
834
820
|
return calendarDates.map((calendarDate) => calendarDate.service_id);
|
|
835
821
|
};
|
|
836
822
|
|
|
@@ -839,8 +825,8 @@ const getCalendarDatesServiceIds = async (startDate, endDate) => {
|
|
|
839
825
|
* and the stop_id of parent station itself. If no parent station, it returns the
|
|
840
826
|
* stop_id.
|
|
841
827
|
*/
|
|
842
|
-
const getAllStationStopIds =
|
|
843
|
-
const stops =
|
|
828
|
+
const getAllStationStopIds = (stopId) => {
|
|
829
|
+
const stops = getStops({
|
|
844
830
|
stop_id: stopId,
|
|
845
831
|
});
|
|
846
832
|
|
|
@@ -850,7 +836,7 @@ const getAllStationStopIds = async (stopId) => {
|
|
|
850
836
|
return [stopId];
|
|
851
837
|
}
|
|
852
838
|
|
|
853
|
-
const stopsInParentStation =
|
|
839
|
+
const stopsInParentStation = getStops(
|
|
854
840
|
{
|
|
855
841
|
parent_station: stop.parent_station,
|
|
856
842
|
},
|
|
@@ -866,8 +852,8 @@ const getAllStationStopIds = async (stopId) => {
|
|
|
866
852
|
/*
|
|
867
853
|
* Get trips with the same `block_id`.
|
|
868
854
|
*/
|
|
869
|
-
const getTripsWithSameBlock =
|
|
870
|
-
const trips =
|
|
855
|
+
const getTripsWithSameBlock = (trip, timetable) => {
|
|
856
|
+
const trips = getTrips(
|
|
871
857
|
{
|
|
872
858
|
block_id: trip.block_id,
|
|
873
859
|
service_id: timetable.service_ids,
|
|
@@ -875,26 +861,24 @@ const getTripsWithSameBlock = async (trip, timetable) => {
|
|
|
875
861
|
['trip_id', 'route_id']
|
|
876
862
|
);
|
|
877
863
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
);
|
|
864
|
+
for (const blockTrip of trips) {
|
|
865
|
+
const stopTimes = getStoptimes(
|
|
866
|
+
{
|
|
867
|
+
trip_id: blockTrip.trip_id,
|
|
868
|
+
},
|
|
869
|
+
[],
|
|
870
|
+
[['stop_sequence', 'ASC']]
|
|
871
|
+
);
|
|
887
872
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
873
|
+
if (stopTimes.length === 0) {
|
|
874
|
+
throw new Error(
|
|
875
|
+
`No stoptimes found found for trip_id=${blockTrip.trip_id}`
|
|
876
|
+
);
|
|
877
|
+
}
|
|
893
878
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
);
|
|
879
|
+
blockTrip.firstStoptime = first(stopTimes);
|
|
880
|
+
blockTrip.lastStoptime = last(stopTimes);
|
|
881
|
+
}
|
|
898
882
|
|
|
899
883
|
return sortBy(trips, (trip) => trip.firstStoptime.departure_timestamp);
|
|
900
884
|
};
|
|
@@ -903,7 +887,7 @@ const getTripsWithSameBlock = async (trip, timetable) => {
|
|
|
903
887
|
* Get next trip and previous trip with the same `block_id` if it arrives or
|
|
904
888
|
* departs from the same stop and is a different route.
|
|
905
889
|
*/
|
|
906
|
-
const addTripContinuation =
|
|
890
|
+
const addTripContinuation = (trip, timetable) => {
|
|
907
891
|
if (!trip.block_id) {
|
|
908
892
|
return;
|
|
909
893
|
}
|
|
@@ -911,10 +895,10 @@ const addTripContinuation = async (trip, timetable) => {
|
|
|
911
895
|
const maxContinuesAsWaitingTimeSeconds = 60 * 60;
|
|
912
896
|
|
|
913
897
|
const firstStoptime = first(trip.stoptimes);
|
|
914
|
-
const firstStopIds =
|
|
898
|
+
const firstStopIds = getAllStationStopIds(firstStoptime.stop_id);
|
|
915
899
|
const lastStoptime = last(trip.stoptimes);
|
|
916
|
-
const lastStopIds =
|
|
917
|
-
const blockTrips =
|
|
900
|
+
const lastStopIds = getAllStationStopIds(lastStoptime.stop_id);
|
|
901
|
+
const blockTrips = getTripsWithSameBlock(trip, timetable);
|
|
918
902
|
|
|
919
903
|
// "Continues From" trips must be the previous trip chronologically.
|
|
920
904
|
const previousTrip = findLast(
|
|
@@ -937,7 +921,7 @@ const addTripContinuation = async (trip, timetable) => {
|
|
|
937
921
|
firstStoptime.departure_timestamp - maxContinuesAsWaitingTimeSeconds &&
|
|
938
922
|
firstStopIds.includes(previousTrip.lastStoptime.stop_id)
|
|
939
923
|
) {
|
|
940
|
-
const routes =
|
|
924
|
+
const routes = getRoutes({
|
|
941
925
|
route_id: previousTrip.route_id,
|
|
942
926
|
});
|
|
943
927
|
|
|
@@ -968,7 +952,7 @@ const addTripContinuation = async (trip, timetable) => {
|
|
|
968
952
|
lastStoptime.arrival_timestamp + maxContinuesAsWaitingTimeSeconds &&
|
|
969
953
|
lastStopIds.includes(nextTrip.firstStoptime.stop_id)
|
|
970
954
|
) {
|
|
971
|
-
const routes =
|
|
955
|
+
const routes = getRoutes({
|
|
972
956
|
route_id: nextTrip.route_id,
|
|
973
957
|
});
|
|
974
958
|
|
|
@@ -1021,7 +1005,9 @@ const filterTrips = (timetable) => {
|
|
|
1021
1005
|
/*
|
|
1022
1006
|
* Get all trips from a timetable.
|
|
1023
1007
|
*/
|
|
1024
|
-
|
|
1008
|
+
|
|
1009
|
+
/* eslint-disable complexity */
|
|
1010
|
+
const getTripsForTimetable = (timetable, calendars, config) => {
|
|
1025
1011
|
const tripQuery = {
|
|
1026
1012
|
route_id: timetable.route_ids,
|
|
1027
1013
|
service_id: timetable.service_ids,
|
|
@@ -1031,7 +1017,7 @@ const getTripsForTimetable = async (timetable, calendars, config) => {
|
|
|
1031
1017
|
tripQuery.direction_id = timetable.direction_id;
|
|
1032
1018
|
}
|
|
1033
1019
|
|
|
1034
|
-
const trips =
|
|
1020
|
+
const trips = getTrips(tripQuery);
|
|
1035
1021
|
|
|
1036
1022
|
if (trips.length === 0) {
|
|
1037
1023
|
timetable.warnings.push(
|
|
@@ -1043,7 +1029,7 @@ const getTripsForTimetable = async (timetable, calendars, config) => {
|
|
|
1043
1029
|
);
|
|
1044
1030
|
}
|
|
1045
1031
|
|
|
1046
|
-
const frequencies =
|
|
1032
|
+
const frequencies = getFrequencies({
|
|
1047
1033
|
trip_id: trips.map((trip) => trip.trip_id),
|
|
1048
1034
|
});
|
|
1049
1035
|
|
|
@@ -1051,81 +1037,78 @@ const getTripsForTimetable = async (timetable, calendars, config) => {
|
|
|
1051
1037
|
timetable.service_ids = uniq(trips.map((trip) => trip.service_id));
|
|
1052
1038
|
|
|
1053
1039
|
const formattedTrips = [];
|
|
1054
|
-
await Promise.all(
|
|
1055
|
-
trips.map(async (trip) => {
|
|
1056
|
-
const formattedTrip = formatTrip(trip, timetable, calendars, config);
|
|
1057
|
-
formattedTrip.stoptimes = await getStoptimes(
|
|
1058
|
-
{
|
|
1059
|
-
trip_id: formattedTrip.trip_id,
|
|
1060
|
-
},
|
|
1061
|
-
[],
|
|
1062
|
-
[['stop_sequence', 'ASC']]
|
|
1063
|
-
);
|
|
1064
1040
|
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1041
|
+
for (const trip of trips) {
|
|
1042
|
+
const formattedTrip = formatTrip(trip, timetable, calendars, config);
|
|
1043
|
+
formattedTrip.stoptimes = getStoptimes(
|
|
1044
|
+
{
|
|
1045
|
+
trip_id: formattedTrip.trip_id,
|
|
1046
|
+
},
|
|
1047
|
+
[],
|
|
1048
|
+
[['stop_sequence', 'ASC']]
|
|
1049
|
+
);
|
|
1074
1050
|
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
timetable.
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
}
|
|
1051
|
+
if (formattedTrip.stoptimes.length === 0) {
|
|
1052
|
+
timetable.warnings.push(
|
|
1053
|
+
`No stoptimes found for trip_id=${
|
|
1054
|
+
formattedTrip.trip_id
|
|
1055
|
+
}, route_id=${timetable.route_ids.join('_')}, timetable_id=${
|
|
1056
|
+
timetable.timetable_id
|
|
1057
|
+
}`
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1085
1060
|
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
}
|
|
1061
|
+
// Exclude trips before timetable `start_timestamp`
|
|
1062
|
+
if (
|
|
1063
|
+
timetable.start_timestamp !== '' &&
|
|
1064
|
+
timetable.start_timestamp !== null &&
|
|
1065
|
+
timetable.start_timestamp !== undefined &&
|
|
1066
|
+
trip.stoptimes[0].arrival_timestamp < timetable.start_timestamp
|
|
1067
|
+
) {
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1096
1070
|
|
|
1097
|
-
|
|
1098
|
-
|
|
1071
|
+
// Exclude trips after timetable `end_timestamp`
|
|
1072
|
+
if (
|
|
1073
|
+
timetable.end_timestamp !== '' &&
|
|
1074
|
+
timetable.end_timestamp !== null &&
|
|
1075
|
+
timetable.end_timestamp !== undefined &&
|
|
1076
|
+
trip.stoptimes[0].arrival_timestamp >= timetable.end_timestamp
|
|
1077
|
+
) {
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1099
1080
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
}
|
|
1081
|
+
if (timetable.show_trip_continuation) {
|
|
1082
|
+
addTripContinuation(formattedTrip, timetable);
|
|
1103
1083
|
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
}
|
|
1084
|
+
if (formattedTrip.continues_as_route) {
|
|
1085
|
+
timetable.has_continues_as_route = true;
|
|
1107
1086
|
}
|
|
1108
1087
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
);
|
|
1112
|
-
|
|
1113
|
-
if (tripFrequencies.length === 0) {
|
|
1114
|
-
formattedTrips.push(formattedTrip);
|
|
1115
|
-
} else {
|
|
1116
|
-
const frequencyTrips = generateTripsByFrequencies(
|
|
1117
|
-
formattedTrip,
|
|
1118
|
-
frequencies,
|
|
1119
|
-
config
|
|
1120
|
-
);
|
|
1121
|
-
formattedTrips.push(...frequencyTrips);
|
|
1122
|
-
timetable.frequencies = frequencies;
|
|
1123
|
-
timetable.frequencyExactTimes = some(frequencies, {
|
|
1124
|
-
exact_times: 1,
|
|
1125
|
-
});
|
|
1088
|
+
if (formattedTrip.continues_from_route) {
|
|
1089
|
+
timetable.has_continues_from_route = true;
|
|
1126
1090
|
}
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
const tripFrequencies = frequencies.filter(
|
|
1094
|
+
(frequency) => frequency.trip_id === trip.trip_id
|
|
1095
|
+
);
|
|
1096
|
+
|
|
1097
|
+
if (tripFrequencies.length === 0) {
|
|
1098
|
+
formattedTrips.push(formattedTrip);
|
|
1099
|
+
} else {
|
|
1100
|
+
const frequencyTrips = generateTripsByFrequencies(
|
|
1101
|
+
formattedTrip,
|
|
1102
|
+
frequencies,
|
|
1103
|
+
config
|
|
1104
|
+
);
|
|
1105
|
+
formattedTrips.push(...frequencyTrips);
|
|
1106
|
+
timetable.frequencies = frequencies;
|
|
1107
|
+
timetable.frequencyExactTimes = some(frequencies, {
|
|
1108
|
+
exact_times: 1,
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1129
1112
|
|
|
1130
1113
|
if (config.useParentStation) {
|
|
1131
1114
|
const stopIds = [];
|
|
@@ -1136,7 +1119,7 @@ const getTripsForTimetable = async (timetable, calendars, config) => {
|
|
|
1136
1119
|
}
|
|
1137
1120
|
}
|
|
1138
1121
|
|
|
1139
|
-
const stops =
|
|
1122
|
+
const stops = getStops(
|
|
1140
1123
|
{
|
|
1141
1124
|
stop_id: uniq(stopIds),
|
|
1142
1125
|
},
|
|
@@ -1156,73 +1139,65 @@ const getTripsForTimetable = async (timetable, calendars, config) => {
|
|
|
1156
1139
|
|
|
1157
1140
|
return sortTrips(formattedTrips, config);
|
|
1158
1141
|
};
|
|
1142
|
+
/* eslint-enable complexity */
|
|
1159
1143
|
|
|
1160
1144
|
/*
|
|
1161
1145
|
* Format timetables for display.
|
|
1162
1146
|
*/
|
|
1163
|
-
const formatTimetables =
|
|
1164
|
-
const formattedTimetables =
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
}
|
|
1147
|
+
const formatTimetables = (timetables, config) => {
|
|
1148
|
+
const formattedTimetables = timetables.map((timetable) => {
|
|
1149
|
+
timetable.warnings = [];
|
|
1150
|
+
const dayList = formatDays(timetable, config);
|
|
1151
|
+
const calendars = getCalendarsFromTimetable(timetable);
|
|
1152
|
+
let serviceIds = calendars.map((calendar) => calendar.service_id);
|
|
1153
|
+
|
|
1154
|
+
if (timetable.include_exceptions === 1) {
|
|
1155
|
+
const calendarDatesServiceIds = getCalendarDatesServiceIds(
|
|
1156
|
+
timetable.start_date,
|
|
1157
|
+
timetable.end_date
|
|
1158
|
+
);
|
|
1159
|
+
serviceIds = uniq([...serviceIds, ...calendarDatesServiceIds]);
|
|
1160
|
+
}
|
|
1178
1161
|
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1162
|
+
Object.assign(timetable, {
|
|
1163
|
+
noServiceSymbolUsed: false,
|
|
1164
|
+
requestDropoffSymbolUsed: false,
|
|
1165
|
+
noDropoffSymbolUsed: false,
|
|
1166
|
+
requestPickupSymbolUsed: false,
|
|
1167
|
+
noPickupSymbolUsed: false,
|
|
1168
|
+
interpolatedStopSymbolUsed: false,
|
|
1169
|
+
showStopCity: config.showStopCity,
|
|
1170
|
+
showStopDescription: config.showStopDescription,
|
|
1171
|
+
noServiceSymbol: config.noServiceSymbol,
|
|
1172
|
+
requestDropoffSymbol: config.requestDropoffSymbol,
|
|
1173
|
+
noDropoffSymbol: config.noDropoffSymbol,
|
|
1174
|
+
requestPickupSymbol: config.requestPickupSymbol,
|
|
1175
|
+
noPickupSymbol: config.noPickupSymbol,
|
|
1176
|
+
interpolatedStopSymbol: config.interpolatedStopSymbol,
|
|
1177
|
+
orientation: timetable.orientation || config.defaultOrientation,
|
|
1178
|
+
service_ids: serviceIds,
|
|
1179
|
+
dayList,
|
|
1180
|
+
dayListLong: formatDaysLong(dayList, config),
|
|
1181
|
+
});
|
|
1199
1182
|
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
timetable.stops = await getStopsForTimetable(timetable, config);
|
|
1206
|
-
timetable.calendarDates = await getCalendarDatesForTimetable(
|
|
1207
|
-
timetable,
|
|
1208
|
-
config
|
|
1209
|
-
);
|
|
1210
|
-
timetable.timetable_label = formatTimetableLabel(timetable);
|
|
1211
|
-
timetable.notes = await getTimetableNotesForTimetable(timetable, config);
|
|
1183
|
+
timetable.orderedTrips = getTripsForTimetable(timetable, calendars, config);
|
|
1184
|
+
timetable.stops = getStopsForTimetable(timetable, config);
|
|
1185
|
+
timetable.calendarDates = getCalendarDatesForTimetable(timetable, config);
|
|
1186
|
+
timetable.timetable_label = formatTimetableLabel(timetable);
|
|
1187
|
+
timetable.notes = getTimetableNotesForTimetable(timetable, config);
|
|
1212
1188
|
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1189
|
+
if (config.showMap) {
|
|
1190
|
+
timetable.geojson = getTimetableGeoJSON(timetable, config);
|
|
1191
|
+
}
|
|
1216
1192
|
|
|
1217
|
-
|
|
1218
|
-
|
|
1193
|
+
// Filter trips after all timetable properties are assigned
|
|
1194
|
+
timetable.orderedTrips = filterTrips(timetable);
|
|
1219
1195
|
|
|
1220
|
-
|
|
1221
|
-
|
|
1196
|
+
// Format stops after all timetable properties are assigned
|
|
1197
|
+
timetable.stops = formatStops(timetable, config);
|
|
1222
1198
|
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
);
|
|
1199
|
+
return timetable;
|
|
1200
|
+
});
|
|
1226
1201
|
|
|
1227
1202
|
if (config.allowEmptyTimetables) {
|
|
1228
1203
|
return formattedTimetables;
|
|
@@ -1236,15 +1211,15 @@ const formatTimetables = async (timetables, config) => {
|
|
|
1236
1211
|
/*
|
|
1237
1212
|
* Get all timetable pages for an agency.
|
|
1238
1213
|
*/
|
|
1239
|
-
export
|
|
1240
|
-
const timetables = mergeTimetablesWithSameId(
|
|
1214
|
+
export function getTimetablePagesForAgency(config) {
|
|
1215
|
+
const timetables = mergeTimetablesWithSameId(getTimetables());
|
|
1241
1216
|
|
|
1242
1217
|
// If no timetables, build each route and direction into a timetable.
|
|
1243
1218
|
if (timetables.length === 0) {
|
|
1244
1219
|
return convertRoutesToTimetablePages(config);
|
|
1245
1220
|
}
|
|
1246
1221
|
|
|
1247
|
-
const timetablePages =
|
|
1222
|
+
const timetablePages = getTimetablePages(
|
|
1248
1223
|
{},
|
|
1249
1224
|
[],
|
|
1250
1225
|
[['timetable_page_id', 'ASC']]
|
|
@@ -1253,48 +1228,44 @@ export async function getTimetablePagesForAgency(config) {
|
|
|
1253
1228
|
// Check if there are any timetable pages defined in `timetable_pages.txt`.
|
|
1254
1229
|
if (timetablePages.length === 0) {
|
|
1255
1230
|
// If no timetablepages, use timetables
|
|
1256
|
-
return
|
|
1257
|
-
|
|
1258
|
-
convertTimetableToTimetablePage(timetable, config)
|
|
1259
|
-
)
|
|
1231
|
+
return timetables.map((timetable) =>
|
|
1232
|
+
convertTimetableToTimetablePage(timetable, config)
|
|
1260
1233
|
);
|
|
1261
1234
|
}
|
|
1262
1235
|
|
|
1263
|
-
const routes =
|
|
1236
|
+
const routes = getRoutes();
|
|
1264
1237
|
|
|
1265
1238
|
// Otherwise, use timetable pages defined in `timetable_pages.txt`.
|
|
1266
|
-
return
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
);
|
|
1239
|
+
return timetablePages.map((timetablePage) => {
|
|
1240
|
+
timetablePage.timetables = sortBy(
|
|
1241
|
+
timetables.filter(
|
|
1242
|
+
(timetable) =>
|
|
1243
|
+
timetable.timetable_page_id === timetablePage.timetable_page_id
|
|
1244
|
+
),
|
|
1245
|
+
'timetable_sequence'
|
|
1246
|
+
);
|
|
1275
1247
|
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1248
|
+
// Add routes for each timetable.
|
|
1249
|
+
for (const timetable of timetablePage.timetables) {
|
|
1250
|
+
timetable.routes = routes.filter((route) =>
|
|
1251
|
+
timetable.route_ids.includes(route.route_id)
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1282
1254
|
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
);
|
|
1255
|
+
return timetablePage;
|
|
1256
|
+
});
|
|
1286
1257
|
}
|
|
1287
1258
|
|
|
1288
1259
|
/*
|
|
1289
1260
|
* Get a timetable_page by id.
|
|
1290
1261
|
*/
|
|
1291
|
-
const getTimetablePageById =
|
|
1262
|
+
const getTimetablePageById = (timetablePageId, config) => {
|
|
1292
1263
|
// Check if there are any timetable pages defined in `timetable_pages.txt`.
|
|
1293
|
-
const timetablePages =
|
|
1264
|
+
const timetablePages = getTimetablePages({
|
|
1294
1265
|
timetable_page_id: timetablePageId,
|
|
1295
1266
|
});
|
|
1296
1267
|
|
|
1297
|
-
const timetables = mergeTimetablesWithSameId(
|
|
1268
|
+
const timetables = mergeTimetablesWithSameId(getTimetables());
|
|
1298
1269
|
|
|
1299
1270
|
if (timetablePages.length > 1) {
|
|
1300
1271
|
throw new Error(
|
|
@@ -1313,13 +1284,11 @@ const getTimetablePageById = async (timetablePageId, config) => {
|
|
|
1313
1284
|
);
|
|
1314
1285
|
|
|
1315
1286
|
// Add routes for each timetable
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
timetable.
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
})
|
|
1322
|
-
);
|
|
1287
|
+
for (const timetable of timetablePage.timetables) {
|
|
1288
|
+
timetable.routes = getRoutes({
|
|
1289
|
+
route_id: timetable.route_ids,
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1323
1292
|
|
|
1324
1293
|
return timetablePage;
|
|
1325
1294
|
}
|
|
@@ -1356,11 +1325,11 @@ const getTimetablePageById = async (timetablePageId, config) => {
|
|
|
1356
1325
|
|
|
1357
1326
|
const routeId = parts.join('|');
|
|
1358
1327
|
|
|
1359
|
-
const routes =
|
|
1328
|
+
const routes = getRoutes({
|
|
1360
1329
|
route_id: routeId,
|
|
1361
1330
|
});
|
|
1362
1331
|
|
|
1363
|
-
const trips =
|
|
1332
|
+
const trips = getTrips(
|
|
1364
1333
|
{
|
|
1365
1334
|
route_id: routeId,
|
|
1366
1335
|
direction_id: directionId,
|
|
@@ -1376,12 +1345,12 @@ const getTimetablePageById = async (timetablePageId, config) => {
|
|
|
1376
1345
|
}
|
|
1377
1346
|
|
|
1378
1347
|
if (/^[01]*$/.test(calendarCode)) {
|
|
1379
|
-
calendars =
|
|
1348
|
+
calendars = getCalendars({
|
|
1380
1349
|
...calendarCodeToCalendar(calendarCode),
|
|
1381
1350
|
});
|
|
1382
1351
|
} else {
|
|
1383
1352
|
serviceId = calendarCode;
|
|
1384
|
-
calendarDates =
|
|
1353
|
+
calendarDates = getCalendarDates({
|
|
1385
1354
|
exception_type: 1,
|
|
1386
1355
|
service_id: serviceId,
|
|
1387
1356
|
});
|
|
@@ -1462,10 +1431,10 @@ export function setDefaultConfig(initialConfig) {
|
|
|
1462
1431
|
/*
|
|
1463
1432
|
* Get a timetable page by id.
|
|
1464
1433
|
*/
|
|
1465
|
-
export
|
|
1466
|
-
const timetablePage =
|
|
1434
|
+
export function getFormattedTimetablePage(timetablePageId, config) {
|
|
1435
|
+
const timetablePage = getTimetablePageById(timetablePageId, config);
|
|
1467
1436
|
|
|
1468
|
-
timetablePage.consolidatedTimetables =
|
|
1437
|
+
timetablePage.consolidatedTimetables = formatTimetables(
|
|
1469
1438
|
timetablePage.timetables,
|
|
1470
1439
|
config
|
|
1471
1440
|
);
|
|
@@ -1481,7 +1450,7 @@ export async function getFormattedTimetablePage(timetablePageId, config) {
|
|
|
1481
1450
|
flatMap(timetablePage.consolidatedTimetables, 'route_ids')
|
|
1482
1451
|
);
|
|
1483
1452
|
|
|
1484
|
-
const timetableRoutes =
|
|
1453
|
+
const timetableRoutes = getRoutes(
|
|
1485
1454
|
{
|
|
1486
1455
|
route_id: timetablePage.route_ids,
|
|
1487
1456
|
},
|
|
@@ -1502,21 +1471,17 @@ export async function getFormattedTimetablePage(timetablePageId, config) {
|
|
|
1502
1471
|
}
|
|
1503
1472
|
|
|
1504
1473
|
// Get `direction_name` for each timetable.
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
timetable
|
|
1510
|
-
);
|
|
1511
|
-
}
|
|
1474
|
+
for (const timetable of timetablePage.consolidatedTimetables) {
|
|
1475
|
+
if (isNullOrEmpty(timetable.direction_name)) {
|
|
1476
|
+
timetable.direction_name = getDirectionHeadsignFromTimetable(timetable);
|
|
1477
|
+
}
|
|
1512
1478
|
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
);
|
|
1479
|
+
if (!timetable.routes) {
|
|
1480
|
+
timetable.routes = getRoutes({
|
|
1481
|
+
route_id: timetable.route_ids,
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1520
1485
|
|
|
1521
1486
|
return timetablePage;
|
|
1522
1487
|
}
|
|
@@ -1564,7 +1529,7 @@ export function generateTimetableHTML(timetablePage, config) {
|
|
|
1564
1529
|
/*
|
|
1565
1530
|
* Generate the CSV timetable for a timetable page.
|
|
1566
1531
|
*/
|
|
1567
|
-
export
|
|
1532
|
+
export function generateTimetableCSV(timetable) {
|
|
1568
1533
|
// Generate horizontal orientation, then transpose if vertical is needed.
|
|
1569
1534
|
const lines = [];
|
|
1570
1535
|
|
|
@@ -1606,15 +1571,15 @@ export async function generateTimetableCSV(timetable) {
|
|
|
1606
1571
|
/*
|
|
1607
1572
|
* Generate the HTML for the agency overview page.
|
|
1608
1573
|
*/
|
|
1609
|
-
export
|
|
1610
|
-
const agencies =
|
|
1574
|
+
export function generateOverviewHTML(timetablePages, config) {
|
|
1575
|
+
const agencies = getAgencies();
|
|
1611
1576
|
if (agencies.length === 0) {
|
|
1612
1577
|
throw new Error('No agencies found');
|
|
1613
1578
|
}
|
|
1614
1579
|
|
|
1615
1580
|
let geojson;
|
|
1616
1581
|
if (config.showMap) {
|
|
1617
|
-
geojson =
|
|
1582
|
+
geojson = getAgencyGeoJSON(config);
|
|
1618
1583
|
}
|
|
1619
1584
|
|
|
1620
1585
|
// Sort timetables for display, first numerically then alphabetically.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gtfs-to-html",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Build human readable transit timetables as HTML, PDF or CSV from GTFS",
|
|
6
6
|
"keywords": [
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"copy-dir": "^1.3.0",
|
|
43
43
|
"csv-stringify": "^6.2.3",
|
|
44
44
|
"express": "^4.18.2",
|
|
45
|
-
"gtfs": "^
|
|
45
|
+
"gtfs": "^4.0.1",
|
|
46
46
|
"js-beautify": "^1.14.7",
|
|
47
47
|
"lodash-es": "^4.17.21",
|
|
48
48
|
"moment": "^2.29.4",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"yoctocolors": "^1.0.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"eslint": "^8.
|
|
62
|
+
"eslint": "^8.31.0",
|
|
63
63
|
"eslint-config-prettier": "^8.5.0",
|
|
64
64
|
"eslint-config-xo": "^0.43.1",
|
|
65
65
|
"husky": "^8.0.2",
|