gtfs-to-html 2.10.17 → 2.11.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/dist/app/index.js +365 -200
- package/dist/app/index.js.map +1 -1
- package/dist/bin/gtfs-to-html.js +379 -206
- package/dist/bin/gtfs-to-html.js.map +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.js +379 -206
- package/dist/index.js.map +1 -1
- package/package.json +8 -9
- package/views/default/overview.pug +3 -3
package/dist/bin/gtfs-to-html.js
CHANGED
|
@@ -23,14 +23,15 @@ import {
|
|
|
23
23
|
readFile,
|
|
24
24
|
rm
|
|
25
25
|
} from "fs/promises";
|
|
26
|
+
import { homedir } from "os";
|
|
26
27
|
import * as _ from "lodash-es";
|
|
28
|
+
import { uniqBy as uniqBy2 } from "lodash-es";
|
|
27
29
|
import archiver from "archiver";
|
|
28
30
|
import beautify from "js-beautify";
|
|
29
31
|
import sanitizeHtml from "sanitize-html";
|
|
30
32
|
import { renderFile } from "pug";
|
|
31
33
|
import puppeteer from "puppeteer";
|
|
32
34
|
import sanitize from "sanitize-filename";
|
|
33
|
-
import untildify from "untildify";
|
|
34
35
|
import { marked } from "marked";
|
|
35
36
|
|
|
36
37
|
// src/lib/formatters.ts
|
|
@@ -59,16 +60,7 @@ function fromGTFSTime(timeString) {
|
|
|
59
60
|
function toGTFSTime(time) {
|
|
60
61
|
return time.format("HH:mm:ss");
|
|
61
62
|
}
|
|
62
|
-
function fromGTFSDate(gtfsDate) {
|
|
63
|
-
return moment(gtfsDate, "YYYYMMDD");
|
|
64
|
-
}
|
|
65
|
-
function toGTFSDate(date) {
|
|
66
|
-
return moment(date).format("YYYYMMDD");
|
|
67
|
-
}
|
|
68
63
|
function calendarToCalendarCode(c) {
|
|
69
|
-
if (c.service_id) {
|
|
70
|
-
return c.service_id;
|
|
71
|
-
}
|
|
72
64
|
return `${c.monday}${c.tuesday}${c.wednesday}${c.thursday}${c.friday}${c.saturday}${c.sunday}`;
|
|
73
65
|
}
|
|
74
66
|
function calendarCodeToCalendar(code) {
|
|
@@ -109,13 +101,13 @@ import {
|
|
|
109
101
|
find,
|
|
110
102
|
findLast,
|
|
111
103
|
first,
|
|
112
|
-
flatMap
|
|
113
|
-
flattenDeep,
|
|
104
|
+
flatMap,
|
|
114
105
|
flow,
|
|
115
106
|
groupBy,
|
|
116
107
|
head,
|
|
117
108
|
last,
|
|
118
109
|
maxBy,
|
|
110
|
+
orderBy,
|
|
119
111
|
partialRight,
|
|
120
112
|
reduce,
|
|
121
113
|
size,
|
|
@@ -149,7 +141,6 @@ import toposort from "toposort";
|
|
|
149
141
|
|
|
150
142
|
// src/lib/geojson-utils.ts
|
|
151
143
|
import { getShapesAsGeoJSON, getStopsAsGeoJSON } from "gtfs";
|
|
152
|
-
import { flatMap } from "lodash-es";
|
|
153
144
|
import simplify from "@turf/simplify";
|
|
154
145
|
import { featureCollection, round } from "@turf/helpers";
|
|
155
146
|
|
|
@@ -311,7 +302,7 @@ function progressBar(formatString, barTotal, config) {
|
|
|
311
302
|
}
|
|
312
303
|
|
|
313
304
|
// src/lib/geojson-utils.ts
|
|
314
|
-
var mergeGeojson = (...geojsons) => featureCollection(flatMap(
|
|
305
|
+
var mergeGeojson = (...geojsons) => featureCollection(geojsons.flatMap((geojson) => geojson.features));
|
|
315
306
|
var truncateGeoJSONDecimals = (geojson, config) => {
|
|
316
307
|
for (const feature of geojson.features) {
|
|
317
308
|
if (feature.geometry.coordinates) {
|
|
@@ -502,7 +493,7 @@ function formatTripNameForCSV(trip, timetable) {
|
|
|
502
493
|
// package.json
|
|
503
494
|
var package_default = {
|
|
504
495
|
name: "gtfs-to-html",
|
|
505
|
-
version: "2.
|
|
496
|
+
version: "2.11.0",
|
|
506
497
|
private: false,
|
|
507
498
|
description: "Build human readable transit timetables as HTML, PDF or CSV from GTFS",
|
|
508
499
|
keywords: [
|
|
@@ -555,24 +546,22 @@ var package_default = {
|
|
|
555
546
|
"cli-table": "^0.3.11",
|
|
556
547
|
"csv-stringify": "^6.6.0",
|
|
557
548
|
express: "^5.1.0",
|
|
558
|
-
gtfs: "^4.
|
|
549
|
+
gtfs: "^4.18.0",
|
|
559
550
|
"gtfs-realtime-pbf-js-module": "^1.0.0",
|
|
560
551
|
"js-beautify": "^1.15.4",
|
|
561
552
|
"lodash-es": "^4.17.21",
|
|
562
|
-
marked: "^16.
|
|
553
|
+
marked: "^16.3.0",
|
|
563
554
|
moment: "^2.30.1",
|
|
564
555
|
pbf: "^4.0.1",
|
|
565
556
|
"pretty-error": "^4.0.0",
|
|
566
557
|
pug: "^3.0.3",
|
|
567
|
-
puppeteer: "^24.
|
|
558
|
+
puppeteer: "^24.21.0",
|
|
568
559
|
"sanitize-filename": "^1.6.3",
|
|
569
560
|
"sanitize-html": "^2.17.0",
|
|
570
561
|
sqlstring: "^2.3.3",
|
|
571
|
-
"timer-machine": "^1.1.0",
|
|
572
562
|
toposort: "^2.0.2",
|
|
573
|
-
untildify: "^5.0.0",
|
|
574
563
|
yargs: "^18.0.0",
|
|
575
|
-
yoctocolors: "^2.1.
|
|
564
|
+
yoctocolors: "^2.1.2"
|
|
576
565
|
},
|
|
577
566
|
devDependencies: {
|
|
578
567
|
"@types/archiver": "^6.0.3",
|
|
@@ -584,10 +573,11 @@ var package_default = {
|
|
|
584
573
|
"@types/node": "^22",
|
|
585
574
|
"@types/pug": "^2.0.10",
|
|
586
575
|
"@types/sanitize-html": "^2.16.0",
|
|
587
|
-
"@types/
|
|
576
|
+
"@types/sqlstring": "^2.3.2",
|
|
577
|
+
"@types/toposort": "^2.0.7",
|
|
588
578
|
"@types/yargs": "^17.0.33",
|
|
589
579
|
husky: "^9.1.7",
|
|
590
|
-
"lint-staged": "^16.1.
|
|
580
|
+
"lint-staged": "^16.1.6",
|
|
591
581
|
prettier: "^3.6.2",
|
|
592
582
|
tsup: "^8.5.0",
|
|
593
583
|
typescript: "^5.9.2"
|
|
@@ -643,7 +633,7 @@ var findCommonStopId = (trips, config) => {
|
|
|
643
633
|
return null;
|
|
644
634
|
}
|
|
645
635
|
const commonStoptime = longestTripStoptimes.find((stoptime, idx) => {
|
|
646
|
-
if (idx === 0 && stoptime.stop_id === last(longestTripStoptimes)
|
|
636
|
+
if (idx === 0 && stoptime.stop_id === last(longestTripStoptimes)?.stop_id) {
|
|
647
637
|
return false;
|
|
648
638
|
}
|
|
649
639
|
if (isNullOrEmpty(stoptime.arrival_time)) {
|
|
@@ -696,11 +686,7 @@ var sortTrips = (trips, config) => {
|
|
|
696
686
|
trip.stoptimes[trip.stoptimes.length - 1].departure_time
|
|
697
687
|
);
|
|
698
688
|
}
|
|
699
|
-
sortedTrips = sortBy(
|
|
700
|
-
trips,
|
|
701
|
-
["firstStoptime", "lastStoptime"],
|
|
702
|
-
["asc", "asc"]
|
|
703
|
-
);
|
|
689
|
+
sortedTrips = sortBy(trips, ["firstStoptime", "lastStoptime"]);
|
|
704
690
|
} else if (config.sortingAlgorithm === "end") {
|
|
705
691
|
for (const trip of trips) {
|
|
706
692
|
if (trip.stoptimes.length === 0) {
|
|
@@ -711,11 +697,7 @@ var sortTrips = (trips, config) => {
|
|
|
711
697
|
trip.stoptimes[trip.stoptimes.length - 1].departure_time
|
|
712
698
|
);
|
|
713
699
|
}
|
|
714
|
-
sortedTrips = sortBy(
|
|
715
|
-
trips,
|
|
716
|
-
["lastStoptime", "firstStoptime"],
|
|
717
|
-
["asc", "asc"]
|
|
718
|
-
);
|
|
700
|
+
sortedTrips = sortBy(trips, ["lastStoptime", "firstStoptime"]);
|
|
719
701
|
} else if (config.sortingAlgorithm === "first") {
|
|
720
702
|
const longestTripStoptimes = getLongestTripStoptimes(trips, config);
|
|
721
703
|
const firstStopId = first(longestTripStoptimes).stop_id;
|
|
@@ -742,8 +724,8 @@ var getCalendarDatesForTimetable = (timetable, config) => {
|
|
|
742
724
|
[],
|
|
743
725
|
[["date", "ASC"]]
|
|
744
726
|
);
|
|
745
|
-
const start =
|
|
746
|
-
const end =
|
|
727
|
+
const start = moment2(timetable.start_date, "YYYYMMDD");
|
|
728
|
+
const end = moment2(timetable.end_date, "YYYYMMDD");
|
|
747
729
|
const excludedDates = /* @__PURE__ */ new Set();
|
|
748
730
|
const includedDates = /* @__PURE__ */ new Set();
|
|
749
731
|
for (const calendarDate of calendarDates) {
|
|
@@ -868,28 +850,70 @@ var getTimetableNotesForTimetable = (timetable, config) => {
|
|
|
868
850
|
}));
|
|
869
851
|
return sortBy(formattedNotes, "symbol");
|
|
870
852
|
};
|
|
871
|
-
var
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
853
|
+
var createTimetablePage = ({
|
|
854
|
+
timetablePageId,
|
|
855
|
+
timetables,
|
|
856
|
+
config
|
|
857
|
+
}) => {
|
|
858
|
+
const updatedTimetables = timetables.map((timetable) => {
|
|
859
|
+
if (!timetable.routes) {
|
|
860
|
+
timetable.routes = getRoutes({
|
|
861
|
+
route_id: timetable.route_ids
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
return timetable;
|
|
865
|
+
});
|
|
866
|
+
const timetablePage = {
|
|
867
|
+
timetable_page_id: timetablePageId,
|
|
868
|
+
timetables: updatedTimetables,
|
|
869
|
+
routes: updatedTimetables.flatMap((timetable) => timetable.routes)
|
|
870
|
+
};
|
|
871
|
+
const filename = generateTimetablePageFileName(timetablePage, config);
|
|
878
872
|
return {
|
|
879
|
-
|
|
880
|
-
timetable_page_label: timetable.timetable_label,
|
|
881
|
-
timetables: [timetable],
|
|
873
|
+
...timetablePage,
|
|
882
874
|
filename
|
|
883
875
|
};
|
|
884
876
|
};
|
|
885
|
-
var
|
|
886
|
-
|
|
877
|
+
var createTimetable = ({
|
|
878
|
+
route,
|
|
879
|
+
directionId,
|
|
880
|
+
tripHeadsign,
|
|
881
|
+
calendars,
|
|
882
|
+
calendarDates
|
|
883
|
+
}) => {
|
|
884
|
+
const serviceIds = uniq([
|
|
885
|
+
...calendars?.map((calendar) => calendar.service_id) ?? [],
|
|
886
|
+
...calendarDates?.map((calendarDate) => calendarDate.service_id) ?? []
|
|
887
|
+
]);
|
|
888
|
+
const days2 = {};
|
|
889
|
+
let startDate = null;
|
|
890
|
+
let endDate = null;
|
|
891
|
+
if (calendars && calendars.length > 0) {
|
|
892
|
+
Object.assign(days2, getDaysFromCalendars(calendars));
|
|
893
|
+
startDate = parseInt(
|
|
894
|
+
moment2.min(
|
|
895
|
+
calendars.map((calendar) => moment2(calendar.start_date, "YYYYMMDD"))
|
|
896
|
+
).format("YYYYMMDD"),
|
|
897
|
+
10
|
|
898
|
+
);
|
|
899
|
+
endDate = parseInt(
|
|
900
|
+
moment2.max(calendars.map((calendar) => moment2(calendar.end_date, "YYYYMMDD"))).format("YYYYMMDD"),
|
|
901
|
+
10
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
const timetableId = formatTimetableId({
|
|
905
|
+
routeIds: [route.route_id],
|
|
906
|
+
directionId,
|
|
907
|
+
days: days2
|
|
908
|
+
});
|
|
909
|
+
return {
|
|
910
|
+
timetable_id: timetableId,
|
|
887
911
|
route_ids: [route.route_id],
|
|
888
|
-
direction_id:
|
|
889
|
-
direction_name:
|
|
912
|
+
direction_id: directionId === null ? null : directionId,
|
|
913
|
+
direction_name: tripHeadsign === null ? null : tripHeadsign,
|
|
890
914
|
routes: [route],
|
|
891
915
|
include_exceptions: calendarDates && calendarDates.length > 0 ? 1 : 0,
|
|
892
|
-
|
|
916
|
+
service_ids: serviceIds,
|
|
893
917
|
service_notes: null,
|
|
894
918
|
timetable_label: null,
|
|
895
919
|
start_time: null,
|
|
@@ -897,88 +921,83 @@ var convertRouteToTimetablePage = (route, direction, calendars, calendarDates, c
|
|
|
897
921
|
orientation: null,
|
|
898
922
|
timetable_sequence: null,
|
|
899
923
|
show_trip_continuation: null,
|
|
900
|
-
start_date:
|
|
901
|
-
end_date:
|
|
924
|
+
start_date: startDate,
|
|
925
|
+
end_date: endDate,
|
|
926
|
+
...days2
|
|
902
927
|
};
|
|
903
|
-
if (calendars && calendars.length > 0) {
|
|
904
|
-
Object.assign(timetable, getDaysFromCalendars(calendars));
|
|
905
|
-
timetable.start_date = toGTFSDate(
|
|
906
|
-
moment2.min(
|
|
907
|
-
calendars.map((calendar) => fromGTFSDate(calendar.start_date))
|
|
908
|
-
)
|
|
909
|
-
);
|
|
910
|
-
timetable.end_date = toGTFSDate(
|
|
911
|
-
moment2.max(calendars.map((calendar) => fromGTFSDate(calendar.end_date)))
|
|
912
|
-
);
|
|
913
|
-
}
|
|
914
|
-
timetable.timetable_id = formatTimetableId(timetable);
|
|
915
|
-
return convertTimetableToTimetablePage(timetable, config);
|
|
916
928
|
};
|
|
917
929
|
var convertRoutesToTimetablePages = (config) => {
|
|
918
|
-
const db = openDb(config);
|
|
919
930
|
const routes = getRoutes();
|
|
920
|
-
|
|
921
|
-
const
|
|
922
|
-
|
|
923
|
-
whereClauses.push(
|
|
924
|
-
`start_date <= ${sqlString.escape(toGTFSDate(moment2(config.endDate)))}`
|
|
925
|
-
);
|
|
926
|
-
}
|
|
927
|
-
if (config.startDate) {
|
|
928
|
-
whereClauses.push(
|
|
929
|
-
`end_date >= ${sqlString.escape(toGTFSDate(moment2(config.startDate)))}`
|
|
930
|
-
);
|
|
931
|
-
}
|
|
932
|
-
if (whereClauses.length > 0) {
|
|
933
|
-
whereClause = `WHERE ${whereClauses.join(" AND ")}`;
|
|
934
|
-
}
|
|
935
|
-
const calendars = db.prepare(`SELECT * FROM calendar ${whereClause}`).all();
|
|
936
|
-
const serviceIds = calendars.map((calendar) => calendar.service_id);
|
|
937
|
-
const calendarDates = db.prepare(
|
|
938
|
-
`SELECT * FROM calendar_dates WHERE exception_type = 1 AND service_id NOT IN (${serviceIds.map((serviceId) => `'${serviceId}'`).join(", ")})`
|
|
939
|
-
).all();
|
|
940
|
-
const timetablePages = routes.map((route) => {
|
|
931
|
+
const timetablePages = [];
|
|
932
|
+
const { calendars, calendarDates } = getCalendarsFromConfig(config);
|
|
933
|
+
for (const route of routes) {
|
|
941
934
|
const trips = getTrips(
|
|
942
935
|
{
|
|
943
936
|
route_id: route.route_id
|
|
944
937
|
},
|
|
945
938
|
["trip_headsign", "direction_id", "trip_id", "service_id"]
|
|
946
939
|
);
|
|
947
|
-
const
|
|
948
|
-
|
|
940
|
+
const uniqueTripDirections = orderBy(
|
|
941
|
+
uniqBy(trips, (trip) => trip.direction_id),
|
|
942
|
+
"direction_id"
|
|
943
|
+
);
|
|
944
|
+
const sortedCalendars = orderBy(calendars, calendarToCalendarCode, "desc");
|
|
945
|
+
const calendarGroups = groupBy(sortedCalendars, calendarToCalendarCode);
|
|
949
946
|
const calendarDateGroups = groupBy(calendarDates, "service_id");
|
|
950
|
-
|
|
951
|
-
|
|
947
|
+
const timetables = [];
|
|
948
|
+
for (const uniqueTripDirection of uniqueTripDirections) {
|
|
949
|
+
for (const calendars2 of Object.values(calendarGroups)) {
|
|
952
950
|
const tripsForCalendars = trips.filter(
|
|
953
951
|
(trip) => some(calendars2, { service_id: trip.service_id })
|
|
954
952
|
);
|
|
955
953
|
if (tripsForCalendars.length > 0) {
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
954
|
+
timetables.push(
|
|
955
|
+
createTimetable({
|
|
956
|
+
route,
|
|
957
|
+
directionId: uniqueTripDirection.direction_id,
|
|
958
|
+
tripHeadsign: uniqueTripDirection.trip_headsign,
|
|
959
|
+
calendars: calendars2
|
|
960
|
+
})
|
|
962
961
|
);
|
|
963
962
|
}
|
|
964
|
-
}
|
|
965
|
-
Object.values(calendarDateGroups)
|
|
963
|
+
}
|
|
964
|
+
for (const calendarDates2 of Object.values(calendarDateGroups)) {
|
|
966
965
|
const tripsForCalendarDates = trips.filter(
|
|
967
966
|
(trip) => some(calendarDates2, { service_id: trip.service_id })
|
|
968
967
|
);
|
|
969
968
|
if (tripsForCalendarDates.length > 0) {
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
969
|
+
timetables.push(
|
|
970
|
+
createTimetable({
|
|
971
|
+
route,
|
|
972
|
+
directionId: uniqueTripDirection.direction_id,
|
|
973
|
+
tripHeadsign: uniqueTripDirection.trip_headsign,
|
|
974
|
+
calendarDates: calendarDates2
|
|
975
|
+
})
|
|
976
976
|
);
|
|
977
977
|
}
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
if (config.groupTimetablesIntoPages === true) {
|
|
981
|
+
timetablePages.push(
|
|
982
|
+
createTimetablePage({
|
|
983
|
+
timetablePageId: `route_${route.route_short_name ?? route.route_long_name}`,
|
|
984
|
+
timetables,
|
|
985
|
+
config
|
|
986
|
+
})
|
|
987
|
+
);
|
|
988
|
+
} else {
|
|
989
|
+
for (const timetable of timetables) {
|
|
990
|
+
timetablePages.push(
|
|
991
|
+
createTimetablePage({
|
|
992
|
+
timetablePageId: timetable.timetable_id,
|
|
993
|
+
timetables: [timetable],
|
|
994
|
+
config
|
|
995
|
+
})
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
return timetablePages;
|
|
982
1001
|
};
|
|
983
1002
|
var generateTripsByFrequencies = (trip, frequencies, config) => {
|
|
984
1003
|
const formattedFrequencies = frequencies.map(
|
|
@@ -1001,7 +1020,7 @@ var generateTripsByFrequencies = (trip, frequencies, config) => {
|
|
|
1001
1020
|
return trips;
|
|
1002
1021
|
};
|
|
1003
1022
|
var duplicateStopsForDifferentArrivalDeparture = (stopIds, timetable, config) => {
|
|
1004
|
-
if (config.showArrivalOnDifference === null) {
|
|
1023
|
+
if (config.showArrivalOnDifference === null || config.showArrivalOnDifference === void 0) {
|
|
1005
1024
|
return stopIds;
|
|
1006
1025
|
}
|
|
1007
1026
|
for (const trip of timetable.orderedTrips) {
|
|
@@ -1123,7 +1142,7 @@ var getStopsForTimetable = (timetable, config) => {
|
|
|
1123
1142
|
}
|
|
1124
1143
|
return stop;
|
|
1125
1144
|
});
|
|
1126
|
-
if (
|
|
1145
|
+
if (config.showStopCity) {
|
|
1127
1146
|
const stopAttributes = getStopAttributes({
|
|
1128
1147
|
stop_id: orderedStopIds
|
|
1129
1148
|
});
|
|
@@ -1138,6 +1157,39 @@ var getStopsForTimetable = (timetable, config) => {
|
|
|
1138
1157
|
}
|
|
1139
1158
|
return orderedStops;
|
|
1140
1159
|
};
|
|
1160
|
+
var getCalendarsFromConfig = (config) => {
|
|
1161
|
+
const db = openDb();
|
|
1162
|
+
let whereClause = "";
|
|
1163
|
+
const whereClauses = [];
|
|
1164
|
+
if (config.endDate) {
|
|
1165
|
+
if (!moment2(config.endDate).isValid()) {
|
|
1166
|
+
throw new Error(`Invalid endDate=${config.endDate} in config.json`);
|
|
1167
|
+
}
|
|
1168
|
+
whereClauses.push(
|
|
1169
|
+
`start_date <= ${sqlString.escape(moment2(config.endDate).format("YYYYMMDD"))}`
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
if (config.startDate) {
|
|
1173
|
+
if (!moment2(config.startDate).isValid()) {
|
|
1174
|
+
throw new Error(`Invalid startDate=${config.startDate} in config.json`);
|
|
1175
|
+
}
|
|
1176
|
+
whereClauses.push(
|
|
1177
|
+
`end_date >= ${sqlString.escape(moment2(config.startDate).format("YYYYMMDD"))}`
|
|
1178
|
+
);
|
|
1179
|
+
}
|
|
1180
|
+
if (whereClauses.length > 0) {
|
|
1181
|
+
whereClause = `WHERE ${whereClauses.join(" AND ")}`;
|
|
1182
|
+
}
|
|
1183
|
+
const calendars = db.prepare(`SELECT * FROM calendar ${whereClause}`).all();
|
|
1184
|
+
const serviceIds = calendars.map((calendar) => calendar.service_id);
|
|
1185
|
+
const calendarDates = db.prepare(
|
|
1186
|
+
`SELECT * FROM calendar_dates WHERE exception_type = 1 AND service_id NOT IN (${serviceIds.map((serviceId) => `'${serviceId}'`).join(", ")})`
|
|
1187
|
+
).all();
|
|
1188
|
+
return {
|
|
1189
|
+
calendars,
|
|
1190
|
+
calendarDates
|
|
1191
|
+
};
|
|
1192
|
+
};
|
|
1141
1193
|
var getCalendarsFromTimetable = (timetable) => {
|
|
1142
1194
|
const db = openDb();
|
|
1143
1195
|
let whereClause = "";
|
|
@@ -1287,13 +1339,17 @@ var filterTrips = (timetable) => {
|
|
|
1287
1339
|
}
|
|
1288
1340
|
trip.stoptimes = combinedStoptimes;
|
|
1289
1341
|
}
|
|
1290
|
-
const timetableStopIds = new Set(
|
|
1342
|
+
const timetableStopIds = new Set(
|
|
1343
|
+
timetable.stops.map((stop) => stop.stop_id)
|
|
1344
|
+
);
|
|
1291
1345
|
for (const trip of filteredTrips) {
|
|
1292
1346
|
trip.stoptimes = trip.stoptimes.filter(
|
|
1293
1347
|
(stoptime) => timetableStopIds.has(stoptime.stop_id)
|
|
1294
1348
|
);
|
|
1295
1349
|
}
|
|
1296
|
-
filteredTrips = filteredTrips.filter(
|
|
1350
|
+
filteredTrips = filteredTrips.filter(
|
|
1351
|
+
(trip) => trip.stoptimes.length > 1
|
|
1352
|
+
);
|
|
1297
1353
|
return filteredTrips;
|
|
1298
1354
|
};
|
|
1299
1355
|
var getTripsForTimetable = (timetable, calendars, config) => {
|
|
@@ -1444,6 +1500,15 @@ var formatTimetables = (timetables, config) => {
|
|
|
1444
1500
|
};
|
|
1445
1501
|
function getTimetablePagesForAgency(config) {
|
|
1446
1502
|
const timetables = mergeTimetablesWithSameId(getTimetables());
|
|
1503
|
+
const routes = getRoutes();
|
|
1504
|
+
const formattedTimetables = timetables.map((timetable) => {
|
|
1505
|
+
return {
|
|
1506
|
+
...timetable,
|
|
1507
|
+
routes: routes.filter(
|
|
1508
|
+
(route) => timetable.route_ids.includes(route.route_id)
|
|
1509
|
+
)
|
|
1510
|
+
};
|
|
1511
|
+
});
|
|
1447
1512
|
if (timetables.length === 0) {
|
|
1448
1513
|
return convertRoutesToTimetablePages(config);
|
|
1449
1514
|
}
|
|
@@ -1453,68 +1518,33 @@ function getTimetablePagesForAgency(config) {
|
|
|
1453
1518
|
[["timetable_page_id", "ASC"]]
|
|
1454
1519
|
);
|
|
1455
1520
|
if (timetablePages.length === 0) {
|
|
1456
|
-
return
|
|
1457
|
-
(timetable) =>
|
|
1521
|
+
return formattedTimetables.map(
|
|
1522
|
+
(timetable) => createTimetablePage({
|
|
1523
|
+
timetablePageId: timetable.timetable_id,
|
|
1524
|
+
timetables: [timetable],
|
|
1525
|
+
config
|
|
1526
|
+
})
|
|
1458
1527
|
);
|
|
1459
1528
|
}
|
|
1460
|
-
const routes = getRoutes();
|
|
1461
1529
|
return timetablePages.map((timetablePage) => {
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
);
|
|
1472
|
-
}
|
|
1473
|
-
return timetablePage;
|
|
1530
|
+
return {
|
|
1531
|
+
...timetablePage,
|
|
1532
|
+
timetables: sortBy(
|
|
1533
|
+
formattedTimetables.filter(
|
|
1534
|
+
(timetable) => timetable.timetable_page_id === timetablePage.timetable_page_id
|
|
1535
|
+
),
|
|
1536
|
+
"timetable_sequence"
|
|
1537
|
+
)
|
|
1538
|
+
};
|
|
1474
1539
|
});
|
|
1475
1540
|
}
|
|
1476
|
-
var
|
|
1477
|
-
const timetablePages = getTimetablePages({
|
|
1478
|
-
timetable_page_id: timetablePageId
|
|
1479
|
-
});
|
|
1480
|
-
const timetables = mergeTimetablesWithSameId(getTimetables());
|
|
1481
|
-
if (timetablePages.length > 1) {
|
|
1482
|
-
throw new Error(
|
|
1483
|
-
`Multiple timetable_pages found for timetable_page_id=${timetablePageId}`
|
|
1484
|
-
);
|
|
1485
|
-
}
|
|
1486
|
-
if (timetablePages.length === 1) {
|
|
1487
|
-
const timetablePage = timetablePages[0];
|
|
1488
|
-
timetablePage.timetables = sortBy(
|
|
1489
|
-
timetables.filter(
|
|
1490
|
-
(timetable) => timetable.timetable_page_id === timetablePageId
|
|
1491
|
-
),
|
|
1492
|
-
"timetable_sequence"
|
|
1493
|
-
);
|
|
1494
|
-
for (const timetable of timetablePage.timetables) {
|
|
1495
|
-
timetable.routes = getRoutes({
|
|
1496
|
-
route_id: timetable.route_ids
|
|
1497
|
-
});
|
|
1498
|
-
}
|
|
1499
|
-
return timetablePage;
|
|
1500
|
-
}
|
|
1501
|
-
if (timetables.length > 0) {
|
|
1502
|
-
const timetablePageTimetables = timetables.filter(
|
|
1503
|
-
(timetable) => timetable.timetable_id === timetablePageId
|
|
1504
|
-
);
|
|
1505
|
-
if (timetablePageTimetables.length === 0) {
|
|
1506
|
-
throw new Error(
|
|
1507
|
-
`No timetable found for timetable_page_id=${timetablePageId}`
|
|
1508
|
-
);
|
|
1509
|
-
}
|
|
1510
|
-
return convertTimetableToTimetablePage(timetablePageTimetables[0], config);
|
|
1511
|
-
}
|
|
1541
|
+
var getDataForTimetablePageById = (timetablePageId) => {
|
|
1512
1542
|
let calendarCode;
|
|
1513
1543
|
let calendars;
|
|
1514
1544
|
let calendarDates;
|
|
1515
1545
|
let serviceId;
|
|
1516
1546
|
let directionId = "";
|
|
1517
|
-
const parts = timetablePageId
|
|
1547
|
+
const parts = timetablePageId?.split("|") ?? [];
|
|
1518
1548
|
if (parts.length > 2) {
|
|
1519
1549
|
directionId = Number.parseInt(parts.pop(), 10);
|
|
1520
1550
|
calendarCode = parts.pop();
|
|
@@ -1533,13 +1563,13 @@ var getTimetablePageById = (timetablePageId, config) => {
|
|
|
1533
1563
|
},
|
|
1534
1564
|
["trip_headsign", "direction_id"]
|
|
1535
1565
|
);
|
|
1536
|
-
const
|
|
1537
|
-
if (
|
|
1566
|
+
const uniqueTripDirections = uniqBy(trips, (trip) => trip.direction_id);
|
|
1567
|
+
if (uniqueTripDirections.length === 0) {
|
|
1538
1568
|
throw new Error(
|
|
1539
1569
|
`No trips found for timetable_page_id=${timetablePageId} route_id=${routeId} direction_id=${directionId}`
|
|
1540
1570
|
);
|
|
1541
1571
|
}
|
|
1542
|
-
if (/^[01]*$/.test(calendarCode)) {
|
|
1572
|
+
if (/^[01]*$/.test(calendarCode ?? "")) {
|
|
1543
1573
|
calendars = getCalendars({
|
|
1544
1574
|
...calendarCodeToCalendar(calendarCode)
|
|
1545
1575
|
});
|
|
@@ -1550,13 +1580,129 @@ var getTimetablePageById = (timetablePageId, config) => {
|
|
|
1550
1580
|
service_id: serviceId
|
|
1551
1581
|
});
|
|
1552
1582
|
}
|
|
1553
|
-
return
|
|
1554
|
-
routes[0],
|
|
1555
|
-
directions[0],
|
|
1583
|
+
return {
|
|
1556
1584
|
calendars,
|
|
1557
1585
|
calendarDates,
|
|
1586
|
+
route: routes[0],
|
|
1587
|
+
directionId: uniqueTripDirections[0].direction_id,
|
|
1588
|
+
tripHeadsign: uniqueTripDirections[0].trip_headsign
|
|
1589
|
+
};
|
|
1590
|
+
};
|
|
1591
|
+
var getTimetablePageById = (timetablePageId, config) => {
|
|
1592
|
+
const timetablePages = getTimetablePages({
|
|
1593
|
+
timetable_page_id: timetablePageId
|
|
1594
|
+
});
|
|
1595
|
+
const timetables = mergeTimetablesWithSameId(getTimetables());
|
|
1596
|
+
if (timetablePages.length > 1) {
|
|
1597
|
+
throw new Error(
|
|
1598
|
+
`Multiple timetable_pages found for timetable_page_id=${timetablePageId}`
|
|
1599
|
+
);
|
|
1600
|
+
}
|
|
1601
|
+
if (timetablePages.length === 1) {
|
|
1602
|
+
const timetablePage = timetablePages[0];
|
|
1603
|
+
timetablePage.timetables = sortBy(
|
|
1604
|
+
timetables.filter(
|
|
1605
|
+
(timetable2) => timetable2.timetable_page_id === timetablePageId
|
|
1606
|
+
),
|
|
1607
|
+
"timetable_sequence"
|
|
1608
|
+
);
|
|
1609
|
+
for (const timetable2 of timetablePage.timetables) {
|
|
1610
|
+
timetable2.routes = getRoutes({
|
|
1611
|
+
route_id: timetable2.route_ids
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
return timetablePage;
|
|
1615
|
+
}
|
|
1616
|
+
if (timetables.length > 0) {
|
|
1617
|
+
const timetablePageTimetables = timetables.filter(
|
|
1618
|
+
(timetable2) => timetable2.timetable_id === timetablePageId
|
|
1619
|
+
);
|
|
1620
|
+
if (timetablePageTimetables.length === 0) {
|
|
1621
|
+
throw new Error(
|
|
1622
|
+
`No timetable found for timetable_page_id=${timetablePageId}`
|
|
1623
|
+
);
|
|
1624
|
+
}
|
|
1625
|
+
return createTimetablePage({
|
|
1626
|
+
timetablePageId,
|
|
1627
|
+
timetables: [timetablePageTimetables[0]],
|
|
1628
|
+
config
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
if (timetablePageId.startsWith("route_")) {
|
|
1632
|
+
const routes = getRoutes({
|
|
1633
|
+
route_short_name: timetablePageId.split("_")[1]
|
|
1634
|
+
});
|
|
1635
|
+
if (routes.length === 0) {
|
|
1636
|
+
throw new Error(
|
|
1637
|
+
`No route found for timetable_page_id=${timetablePageId}`
|
|
1638
|
+
);
|
|
1639
|
+
}
|
|
1640
|
+
const { calendars: calendars2, calendarDates: calendarDates2 } = getCalendarsFromConfig(config);
|
|
1641
|
+
const trips = getTrips(
|
|
1642
|
+
{
|
|
1643
|
+
route_id: routes[0].route_id
|
|
1644
|
+
},
|
|
1645
|
+
["trip_headsign", "direction_id", "trip_id", "service_id"]
|
|
1646
|
+
);
|
|
1647
|
+
const uniqueTripDirections = orderBy(
|
|
1648
|
+
uniqBy(trips, (trip) => trip.direction_id),
|
|
1649
|
+
"direction_id"
|
|
1650
|
+
);
|
|
1651
|
+
const sortedCalendars = orderBy(calendars2, calendarToCalendarCode, "desc");
|
|
1652
|
+
const calendarGroups = groupBy(sortedCalendars, calendarToCalendarCode);
|
|
1653
|
+
const calendarDateGroups = groupBy(calendarDates2, "service_id");
|
|
1654
|
+
const timetables2 = [];
|
|
1655
|
+
for (const uniqueTripDirection of uniqueTripDirections) {
|
|
1656
|
+
for (const calendars3 of Object.values(calendarGroups)) {
|
|
1657
|
+
const tripsForCalendars = trips.filter(
|
|
1658
|
+
(trip) => some(calendars3, { service_id: trip.service_id })
|
|
1659
|
+
);
|
|
1660
|
+
if (tripsForCalendars.length > 0) {
|
|
1661
|
+
timetables2.push(
|
|
1662
|
+
createTimetable({
|
|
1663
|
+
route: routes[0],
|
|
1664
|
+
directionId: uniqueTripDirection.direction_id,
|
|
1665
|
+
tripHeadsign: uniqueTripDirection.trip_headsign,
|
|
1666
|
+
calendars: calendars3
|
|
1667
|
+
})
|
|
1668
|
+
);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
for (const calendarDates3 of Object.values(calendarDateGroups)) {
|
|
1672
|
+
const tripsForCalendarDates = trips.filter(
|
|
1673
|
+
(trip) => some(calendarDates3, { service_id: trip.service_id })
|
|
1674
|
+
);
|
|
1675
|
+
if (tripsForCalendarDates.length > 0) {
|
|
1676
|
+
timetables2.push(
|
|
1677
|
+
createTimetable({
|
|
1678
|
+
route: routes[0],
|
|
1679
|
+
directionId: uniqueTripDirection.direction_id,
|
|
1680
|
+
tripHeadsign: uniqueTripDirection.trip_headsign,
|
|
1681
|
+
calendarDates: calendarDates3
|
|
1682
|
+
})
|
|
1683
|
+
);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
return createTimetablePage({
|
|
1688
|
+
timetablePageId,
|
|
1689
|
+
timetables: timetables2,
|
|
1690
|
+
config
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
1693
|
+
const { calendars, calendarDates, route, directionId, tripHeadsign } = getDataForTimetablePageById(timetablePageId);
|
|
1694
|
+
const timetable = createTimetable({
|
|
1695
|
+
route,
|
|
1696
|
+
directionId,
|
|
1697
|
+
tripHeadsign,
|
|
1698
|
+
calendars,
|
|
1699
|
+
calendarDates
|
|
1700
|
+
});
|
|
1701
|
+
return createTimetablePage({
|
|
1702
|
+
timetablePageId,
|
|
1703
|
+
timetables: [timetable],
|
|
1558
1704
|
config
|
|
1559
|
-
);
|
|
1705
|
+
});
|
|
1560
1706
|
};
|
|
1561
1707
|
function setDefaultConfig(initialConfig) {
|
|
1562
1708
|
const defaults = {
|
|
@@ -1577,6 +1723,7 @@ function setDefaultConfig(initialConfig) {
|
|
|
1577
1723
|
defaultOrientation: "vertical",
|
|
1578
1724
|
interpolatedStopSymbol: "\u2022",
|
|
1579
1725
|
interpolatedStopText: "Estimated time of arrival",
|
|
1726
|
+
groupTimetablesIntoPages: true,
|
|
1580
1727
|
gtfsToHtmlVersion: version,
|
|
1581
1728
|
linkStopUrls: false,
|
|
1582
1729
|
mapStyleUrl: "https://tiles.openfreemap.org/styles/positron",
|
|
@@ -1633,12 +1780,6 @@ function getFormattedTimetablePage(timetablePageId, config) {
|
|
|
1633
1780
|
timetablePageId,
|
|
1634
1781
|
config
|
|
1635
1782
|
);
|
|
1636
|
-
const timetableRoutes = getRoutes(
|
|
1637
|
-
{
|
|
1638
|
-
route_id: timetablePage.route_ids
|
|
1639
|
-
},
|
|
1640
|
-
["agency_id"]
|
|
1641
|
-
);
|
|
1642
1783
|
const consolidatedTimetables = formatTimetables(
|
|
1643
1784
|
timetablePage.timetables,
|
|
1644
1785
|
config
|
|
@@ -1654,7 +1795,7 @@ function getFormattedTimetablePage(timetablePageId, config) {
|
|
|
1654
1795
|
}
|
|
1655
1796
|
}
|
|
1656
1797
|
const uniqueRoutes = uniqBy(
|
|
1657
|
-
|
|
1798
|
+
flatMap(consolidatedTimetables, (timetable) => timetable.routes),
|
|
1658
1799
|
"route_id"
|
|
1659
1800
|
);
|
|
1660
1801
|
const formattedTimetablePage = {
|
|
@@ -1665,7 +1806,7 @@ function getFormattedTimetablePage(timetablePageId, config) {
|
|
|
1665
1806
|
consolidatedTimetables.map((timetable) => timetable.dayList)
|
|
1666
1807
|
),
|
|
1667
1808
|
route_ids: uniqueRoutes.map((route) => route.route_id),
|
|
1668
|
-
agency_ids: uniq(compact(
|
|
1809
|
+
agency_ids: uniq(compact(uniqueRoutes.map((route) => route.agency_id))),
|
|
1669
1810
|
filename: timetablePage.filename ?? `${timetablePage.timetable_page_id}.html`,
|
|
1670
1811
|
timetable_page_label: timetablePage.timetable_page_label ?? formatListForDisplay(uniqueRoutes.map((route) => formatRouteName(route)))
|
|
1671
1812
|
};
|
|
@@ -1923,12 +2064,14 @@ function formatFrequency(frequency, config) {
|
|
|
1923
2064
|
frequency.headway_min = Math.round(headway.asMinutes());
|
|
1924
2065
|
return frequency;
|
|
1925
2066
|
}
|
|
1926
|
-
function formatTimetableId(
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
2067
|
+
function formatTimetableId({
|
|
2068
|
+
routeIds,
|
|
2069
|
+
directionId,
|
|
2070
|
+
days: days2
|
|
2071
|
+
}) {
|
|
2072
|
+
let timetableId = `${routeIds.join("_")}|${calendarToCalendarCode(days2)}`;
|
|
2073
|
+
if (!isNullOrEmpty(directionId)) {
|
|
2074
|
+
timetableId += `|${directionId}`;
|
|
1932
2075
|
}
|
|
1933
2076
|
return timetableId;
|
|
1934
2077
|
}
|
|
@@ -2081,10 +2224,18 @@ function formatTimetableLabel(timetable) {
|
|
|
2081
2224
|
return timetableLabel;
|
|
2082
2225
|
}
|
|
2083
2226
|
var formatRouteName = (route) => {
|
|
2084
|
-
if (route.route_long_name
|
|
2085
|
-
return `Route ${route.
|
|
2227
|
+
if (route.route_long_name) {
|
|
2228
|
+
return `Route ${route.route_long_name}`;
|
|
2086
2229
|
}
|
|
2087
|
-
return route.
|
|
2230
|
+
return route.route_short_name ?? "Unknown";
|
|
2231
|
+
};
|
|
2232
|
+
var formatRouteNameForFilename = (route) => {
|
|
2233
|
+
if (route.route_short_name) {
|
|
2234
|
+
return route.route_short_name.replace(/\s/g, "-");
|
|
2235
|
+
} else if (route.route_long_name) {
|
|
2236
|
+
return route.route_long_name.replace(/\s/g, "-");
|
|
2237
|
+
}
|
|
2238
|
+
return "Unknown";
|
|
2088
2239
|
};
|
|
2089
2240
|
var formatListForDisplay = (list) => {
|
|
2090
2241
|
return new Intl.ListFormat("en-US", {
|
|
@@ -2107,6 +2258,7 @@ function mergeTimetablesWithSameId(timetables) {
|
|
|
2107
2258
|
}
|
|
2108
2259
|
|
|
2109
2260
|
// src/lib/file-utils.ts
|
|
2261
|
+
var homeDirectory = homedir();
|
|
2110
2262
|
async function getConfig(argv2) {
|
|
2111
2263
|
let data;
|
|
2112
2264
|
let config;
|
|
@@ -2216,15 +2368,34 @@ function zipFolder(outputPath) {
|
|
|
2216
2368
|
archive.finalize();
|
|
2217
2369
|
});
|
|
2218
2370
|
}
|
|
2219
|
-
function
|
|
2220
|
-
|
|
2371
|
+
function generateTimetablePageFileName(timetablePage, config) {
|
|
2372
|
+
if (timetablePage.filename) {
|
|
2373
|
+
return sanitize(timetablePage.filename);
|
|
2374
|
+
}
|
|
2375
|
+
if (config.groupTimetablesIntoPages === true && uniqBy2(timetablePage.timetables, "route_id").length === 1) {
|
|
2376
|
+
const route = timetablePage.timetables[0].routes[0];
|
|
2377
|
+
return sanitize(`${formatRouteNameForFilename(route).toLowerCase()}.html`);
|
|
2378
|
+
}
|
|
2379
|
+
const timetable = timetablePage.timetables[0];
|
|
2380
|
+
let filename = timetable.timetable_id ?? "";
|
|
2381
|
+
for (const route of timetable.routes) {
|
|
2382
|
+
filename += `_${formatRouteNameForFilename(route)}`;
|
|
2383
|
+
}
|
|
2384
|
+
if (!isNullOrEmpty(timetable.direction_id)) {
|
|
2385
|
+
filename += `_${timetable.direction_id}`;
|
|
2386
|
+
}
|
|
2387
|
+
filename += `_${formatDays(timetable, config).replace(/\s/g, "")}.html`;
|
|
2388
|
+
return sanitize(filename.toLowerCase());
|
|
2389
|
+
}
|
|
2390
|
+
function generateCSVFileName(timetable, config) {
|
|
2391
|
+
let filename = timetable.timetable_id ?? "";
|
|
2221
2392
|
for (const route of timetable.routes) {
|
|
2222
|
-
filename +=
|
|
2393
|
+
filename += `_${formatRouteNameForFilename(route)}`;
|
|
2223
2394
|
}
|
|
2224
2395
|
if (!isNullOrEmpty(timetable.direction_id)) {
|
|
2225
2396
|
filename += `_${timetable.direction_id}`;
|
|
2226
2397
|
}
|
|
2227
|
-
filename += `_${formatDays(timetable, config).replace(/\s/g, "")}
|
|
2398
|
+
filename += `_${formatDays(timetable, config).replace(/\s/g, "")}.csv`;
|
|
2228
2399
|
return sanitize(filename).toLowerCase();
|
|
2229
2400
|
}
|
|
2230
2401
|
function generateFolderName(timetablePage) {
|
|
@@ -2264,22 +2435,22 @@ async function renderPdf(htmlPath) {
|
|
|
2264
2435
|
});
|
|
2265
2436
|
await browser.close();
|
|
2266
2437
|
}
|
|
2438
|
+
function untildify(pathWithTilde) {
|
|
2439
|
+
return homeDirectory ? pathWithTilde.replace(/^~(?=$|\/|\\)/, homeDirectory) : pathWithTilde;
|
|
2440
|
+
}
|
|
2267
2441
|
|
|
2268
2442
|
// src/lib/gtfs-to-html.ts
|
|
2269
2443
|
import path from "path";
|
|
2270
2444
|
import { mkdir as mkdir2, writeFile } from "fs/promises";
|
|
2271
2445
|
import { openDb as openDb2, importGtfs } from "gtfs";
|
|
2272
2446
|
import sanitize2 from "sanitize-filename";
|
|
2273
|
-
import Timer from "timer-machine";
|
|
2274
|
-
import untildify2 from "untildify";
|
|
2275
2447
|
var gtfsToHtml = async (initialConfig) => {
|
|
2276
2448
|
const config = setDefaultConfig(initialConfig);
|
|
2277
|
-
const
|
|
2449
|
+
const startTime = process.hrtime.bigint();
|
|
2278
2450
|
const agencyKey = config.agencies.map(
|
|
2279
2451
|
(agency) => agency.agencyKey ?? agency.agency_key ?? "unknown"
|
|
2280
2452
|
).join("-");
|
|
2281
|
-
const outputPath = config.outputPath ?
|
|
2282
|
-
timer.start();
|
|
2453
|
+
const outputPath = config.outputPath ? untildify(config.outputPath) : path.join(process.cwd(), "html", sanitize2(agencyKey));
|
|
2283
2454
|
await prepDirectory(outputPath, config);
|
|
2284
2455
|
try {
|
|
2285
2456
|
openDb2(config);
|
|
@@ -2325,9 +2496,11 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2325
2496
|
config
|
|
2326
2497
|
);
|
|
2327
2498
|
for (const timetable of timetablePage.timetables) {
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2499
|
+
if (timetable.warnings) {
|
|
2500
|
+
for (const warning of timetable.warnings) {
|
|
2501
|
+
stats.warnings.push(warning);
|
|
2502
|
+
bar?.interrupt(warning);
|
|
2503
|
+
}
|
|
2331
2504
|
}
|
|
2332
2505
|
}
|
|
2333
2506
|
if (timetablePage.consolidatedTimetables.length === 0) {
|
|
@@ -2350,7 +2523,7 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2350
2523
|
const csvPath = path.join(
|
|
2351
2524
|
outputPath,
|
|
2352
2525
|
datePath,
|
|
2353
|
-
|
|
2526
|
+
generateCSVFileName(timetable, config)
|
|
2354
2527
|
);
|
|
2355
2528
|
await writeFile(csvPath, csv);
|
|
2356
2529
|
}
|
|
@@ -2396,11 +2569,11 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2396
2569
|
`${agencyKey}: ${config.outputFormat.toUpperCase()} timetables created at ${fullOutputPath}`
|
|
2397
2570
|
);
|
|
2398
2571
|
logStats(config)(stats);
|
|
2399
|
-
const
|
|
2572
|
+
const endTime = process.hrtime.bigint();
|
|
2573
|
+
const elapsedSeconds = Number(endTime - startTime) / 1e9;
|
|
2400
2574
|
log(config)(
|
|
2401
|
-
`${agencyKey}: ${config.outputFormat.toUpperCase()} timetable generation required ${
|
|
2575
|
+
`${agencyKey}: ${config.outputFormat.toUpperCase()} timetable generation required ${elapsedSeconds.toFixed(1)} seconds`
|
|
2402
2576
|
);
|
|
2403
|
-
timer.stop();
|
|
2404
2577
|
return fullOutputPath;
|
|
2405
2578
|
};
|
|
2406
2579
|
var gtfs_to_html_default = gtfsToHtml;
|