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/index.js
CHANGED
|
@@ -9,8 +9,6 @@ import path from "path";
|
|
|
9
9
|
import { mkdir as mkdir2, writeFile } from "fs/promises";
|
|
10
10
|
import { openDb as openDb2, importGtfs } from "gtfs";
|
|
11
11
|
import sanitize2 from "sanitize-filename";
|
|
12
|
-
import Timer from "timer-machine";
|
|
13
|
-
import untildify2 from "untildify";
|
|
14
12
|
|
|
15
13
|
// src/lib/file-utils.ts
|
|
16
14
|
import { dirname, join, resolve } from "path";
|
|
@@ -25,14 +23,15 @@ import {
|
|
|
25
23
|
readFile,
|
|
26
24
|
rm
|
|
27
25
|
} from "fs/promises";
|
|
26
|
+
import { homedir } from "os";
|
|
28
27
|
import * as _ from "lodash-es";
|
|
28
|
+
import { uniqBy as uniqBy2 } from "lodash-es";
|
|
29
29
|
import archiver from "archiver";
|
|
30
30
|
import beautify from "js-beautify";
|
|
31
31
|
import sanitizeHtml from "sanitize-html";
|
|
32
32
|
import { renderFile } from "pug";
|
|
33
33
|
import puppeteer from "puppeteer";
|
|
34
34
|
import sanitize from "sanitize-filename";
|
|
35
|
-
import untildify from "untildify";
|
|
36
35
|
import { marked } from "marked";
|
|
37
36
|
|
|
38
37
|
// src/lib/formatters.ts
|
|
@@ -61,16 +60,7 @@ function fromGTFSTime(timeString) {
|
|
|
61
60
|
function toGTFSTime(time) {
|
|
62
61
|
return time.format("HH:mm:ss");
|
|
63
62
|
}
|
|
64
|
-
function fromGTFSDate(gtfsDate) {
|
|
65
|
-
return moment(gtfsDate, "YYYYMMDD");
|
|
66
|
-
}
|
|
67
|
-
function toGTFSDate(date) {
|
|
68
|
-
return moment(date).format("YYYYMMDD");
|
|
69
|
-
}
|
|
70
63
|
function calendarToCalendarCode(c) {
|
|
71
|
-
if (c.service_id) {
|
|
72
|
-
return c.service_id;
|
|
73
|
-
}
|
|
74
64
|
return `${c.monday}${c.tuesday}${c.wednesday}${c.thursday}${c.friday}${c.saturday}${c.sunday}`;
|
|
75
65
|
}
|
|
76
66
|
function calendarCodeToCalendar(code) {
|
|
@@ -111,13 +101,13 @@ import {
|
|
|
111
101
|
find,
|
|
112
102
|
findLast,
|
|
113
103
|
first,
|
|
114
|
-
flatMap
|
|
115
|
-
flattenDeep,
|
|
104
|
+
flatMap,
|
|
116
105
|
flow,
|
|
117
106
|
groupBy,
|
|
118
107
|
head,
|
|
119
108
|
last,
|
|
120
109
|
maxBy,
|
|
110
|
+
orderBy,
|
|
121
111
|
partialRight,
|
|
122
112
|
reduce,
|
|
123
113
|
size,
|
|
@@ -151,7 +141,6 @@ import toposort from "toposort";
|
|
|
151
141
|
|
|
152
142
|
// src/lib/geojson-utils.ts
|
|
153
143
|
import { getShapesAsGeoJSON, getStopsAsGeoJSON } from "gtfs";
|
|
154
|
-
import { flatMap } from "lodash-es";
|
|
155
144
|
import simplify from "@turf/simplify";
|
|
156
145
|
import { featureCollection, round } from "@turf/helpers";
|
|
157
146
|
|
|
@@ -313,7 +302,7 @@ function progressBar(formatString, barTotal, config) {
|
|
|
313
302
|
}
|
|
314
303
|
|
|
315
304
|
// src/lib/geojson-utils.ts
|
|
316
|
-
var mergeGeojson = (...geojsons) => featureCollection(flatMap(
|
|
305
|
+
var mergeGeojson = (...geojsons) => featureCollection(geojsons.flatMap((geojson) => geojson.features));
|
|
317
306
|
var truncateGeoJSONDecimals = (geojson, config) => {
|
|
318
307
|
for (const feature of geojson.features) {
|
|
319
308
|
if (feature.geometry.coordinates) {
|
|
@@ -504,7 +493,7 @@ function formatTripNameForCSV(trip, timetable) {
|
|
|
504
493
|
// package.json
|
|
505
494
|
var package_default = {
|
|
506
495
|
name: "gtfs-to-html",
|
|
507
|
-
version: "2.
|
|
496
|
+
version: "2.11.0",
|
|
508
497
|
private: false,
|
|
509
498
|
description: "Build human readable transit timetables as HTML, PDF or CSV from GTFS",
|
|
510
499
|
keywords: [
|
|
@@ -557,24 +546,22 @@ var package_default = {
|
|
|
557
546
|
"cli-table": "^0.3.11",
|
|
558
547
|
"csv-stringify": "^6.6.0",
|
|
559
548
|
express: "^5.1.0",
|
|
560
|
-
gtfs: "^4.
|
|
549
|
+
gtfs: "^4.18.0",
|
|
561
550
|
"gtfs-realtime-pbf-js-module": "^1.0.0",
|
|
562
551
|
"js-beautify": "^1.15.4",
|
|
563
552
|
"lodash-es": "^4.17.21",
|
|
564
|
-
marked: "^16.
|
|
553
|
+
marked: "^16.3.0",
|
|
565
554
|
moment: "^2.30.1",
|
|
566
555
|
pbf: "^4.0.1",
|
|
567
556
|
"pretty-error": "^4.0.0",
|
|
568
557
|
pug: "^3.0.3",
|
|
569
|
-
puppeteer: "^24.
|
|
558
|
+
puppeteer: "^24.21.0",
|
|
570
559
|
"sanitize-filename": "^1.6.3",
|
|
571
560
|
"sanitize-html": "^2.17.0",
|
|
572
561
|
sqlstring: "^2.3.3",
|
|
573
|
-
"timer-machine": "^1.1.0",
|
|
574
562
|
toposort: "^2.0.2",
|
|
575
|
-
untildify: "^5.0.0",
|
|
576
563
|
yargs: "^18.0.0",
|
|
577
|
-
yoctocolors: "^2.1.
|
|
564
|
+
yoctocolors: "^2.1.2"
|
|
578
565
|
},
|
|
579
566
|
devDependencies: {
|
|
580
567
|
"@types/archiver": "^6.0.3",
|
|
@@ -586,10 +573,11 @@ var package_default = {
|
|
|
586
573
|
"@types/node": "^22",
|
|
587
574
|
"@types/pug": "^2.0.10",
|
|
588
575
|
"@types/sanitize-html": "^2.16.0",
|
|
589
|
-
"@types/
|
|
576
|
+
"@types/sqlstring": "^2.3.2",
|
|
577
|
+
"@types/toposort": "^2.0.7",
|
|
590
578
|
"@types/yargs": "^17.0.33",
|
|
591
579
|
husky: "^9.1.7",
|
|
592
|
-
"lint-staged": "^16.1.
|
|
580
|
+
"lint-staged": "^16.1.6",
|
|
593
581
|
prettier: "^3.6.2",
|
|
594
582
|
tsup: "^8.5.0",
|
|
595
583
|
typescript: "^5.9.2"
|
|
@@ -645,7 +633,7 @@ var findCommonStopId = (trips, config) => {
|
|
|
645
633
|
return null;
|
|
646
634
|
}
|
|
647
635
|
const commonStoptime = longestTripStoptimes.find((stoptime, idx) => {
|
|
648
|
-
if (idx === 0 && stoptime.stop_id === last(longestTripStoptimes)
|
|
636
|
+
if (idx === 0 && stoptime.stop_id === last(longestTripStoptimes)?.stop_id) {
|
|
649
637
|
return false;
|
|
650
638
|
}
|
|
651
639
|
if (isNullOrEmpty(stoptime.arrival_time)) {
|
|
@@ -698,11 +686,7 @@ var sortTrips = (trips, config) => {
|
|
|
698
686
|
trip.stoptimes[trip.stoptimes.length - 1].departure_time
|
|
699
687
|
);
|
|
700
688
|
}
|
|
701
|
-
sortedTrips = sortBy(
|
|
702
|
-
trips,
|
|
703
|
-
["firstStoptime", "lastStoptime"],
|
|
704
|
-
["asc", "asc"]
|
|
705
|
-
);
|
|
689
|
+
sortedTrips = sortBy(trips, ["firstStoptime", "lastStoptime"]);
|
|
706
690
|
} else if (config.sortingAlgorithm === "end") {
|
|
707
691
|
for (const trip of trips) {
|
|
708
692
|
if (trip.stoptimes.length === 0) {
|
|
@@ -713,11 +697,7 @@ var sortTrips = (trips, config) => {
|
|
|
713
697
|
trip.stoptimes[trip.stoptimes.length - 1].departure_time
|
|
714
698
|
);
|
|
715
699
|
}
|
|
716
|
-
sortedTrips = sortBy(
|
|
717
|
-
trips,
|
|
718
|
-
["lastStoptime", "firstStoptime"],
|
|
719
|
-
["asc", "asc"]
|
|
720
|
-
);
|
|
700
|
+
sortedTrips = sortBy(trips, ["lastStoptime", "firstStoptime"]);
|
|
721
701
|
} else if (config.sortingAlgorithm === "first") {
|
|
722
702
|
const longestTripStoptimes = getLongestTripStoptimes(trips, config);
|
|
723
703
|
const firstStopId = first(longestTripStoptimes).stop_id;
|
|
@@ -744,8 +724,8 @@ var getCalendarDatesForTimetable = (timetable, config) => {
|
|
|
744
724
|
[],
|
|
745
725
|
[["date", "ASC"]]
|
|
746
726
|
);
|
|
747
|
-
const start =
|
|
748
|
-
const end =
|
|
727
|
+
const start = moment2(timetable.start_date, "YYYYMMDD");
|
|
728
|
+
const end = moment2(timetable.end_date, "YYYYMMDD");
|
|
749
729
|
const excludedDates = /* @__PURE__ */ new Set();
|
|
750
730
|
const includedDates = /* @__PURE__ */ new Set();
|
|
751
731
|
for (const calendarDate of calendarDates) {
|
|
@@ -870,28 +850,70 @@ var getTimetableNotesForTimetable = (timetable, config) => {
|
|
|
870
850
|
}));
|
|
871
851
|
return sortBy(formattedNotes, "symbol");
|
|
872
852
|
};
|
|
873
|
-
var
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
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);
|
|
880
872
|
return {
|
|
881
|
-
|
|
882
|
-
timetable_page_label: timetable.timetable_label,
|
|
883
|
-
timetables: [timetable],
|
|
873
|
+
...timetablePage,
|
|
884
874
|
filename
|
|
885
875
|
};
|
|
886
876
|
};
|
|
887
|
-
var
|
|
888
|
-
|
|
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,
|
|
889
911
|
route_ids: [route.route_id],
|
|
890
|
-
direction_id:
|
|
891
|
-
direction_name:
|
|
912
|
+
direction_id: directionId === null ? null : directionId,
|
|
913
|
+
direction_name: tripHeadsign === null ? null : tripHeadsign,
|
|
892
914
|
routes: [route],
|
|
893
915
|
include_exceptions: calendarDates && calendarDates.length > 0 ? 1 : 0,
|
|
894
|
-
|
|
916
|
+
service_ids: serviceIds,
|
|
895
917
|
service_notes: null,
|
|
896
918
|
timetable_label: null,
|
|
897
919
|
start_time: null,
|
|
@@ -899,88 +921,83 @@ var convertRouteToTimetablePage = (route, direction, calendars, calendarDates, c
|
|
|
899
921
|
orientation: null,
|
|
900
922
|
timetable_sequence: null,
|
|
901
923
|
show_trip_continuation: null,
|
|
902
|
-
start_date:
|
|
903
|
-
end_date:
|
|
924
|
+
start_date: startDate,
|
|
925
|
+
end_date: endDate,
|
|
926
|
+
...days2
|
|
904
927
|
};
|
|
905
|
-
if (calendars && calendars.length > 0) {
|
|
906
|
-
Object.assign(timetable, getDaysFromCalendars(calendars));
|
|
907
|
-
timetable.start_date = toGTFSDate(
|
|
908
|
-
moment2.min(
|
|
909
|
-
calendars.map((calendar) => fromGTFSDate(calendar.start_date))
|
|
910
|
-
)
|
|
911
|
-
);
|
|
912
|
-
timetable.end_date = toGTFSDate(
|
|
913
|
-
moment2.max(calendars.map((calendar) => fromGTFSDate(calendar.end_date)))
|
|
914
|
-
);
|
|
915
|
-
}
|
|
916
|
-
timetable.timetable_id = formatTimetableId(timetable);
|
|
917
|
-
return convertTimetableToTimetablePage(timetable, config);
|
|
918
928
|
};
|
|
919
929
|
var convertRoutesToTimetablePages = (config) => {
|
|
920
|
-
const db = openDb(config);
|
|
921
930
|
const routes = getRoutes();
|
|
922
|
-
|
|
923
|
-
const
|
|
924
|
-
|
|
925
|
-
whereClauses.push(
|
|
926
|
-
`start_date <= ${sqlString.escape(toGTFSDate(moment2(config.endDate)))}`
|
|
927
|
-
);
|
|
928
|
-
}
|
|
929
|
-
if (config.startDate) {
|
|
930
|
-
whereClauses.push(
|
|
931
|
-
`end_date >= ${sqlString.escape(toGTFSDate(moment2(config.startDate)))}`
|
|
932
|
-
);
|
|
933
|
-
}
|
|
934
|
-
if (whereClauses.length > 0) {
|
|
935
|
-
whereClause = `WHERE ${whereClauses.join(" AND ")}`;
|
|
936
|
-
}
|
|
937
|
-
const calendars = db.prepare(`SELECT * FROM calendar ${whereClause}`).all();
|
|
938
|
-
const serviceIds = calendars.map((calendar) => calendar.service_id);
|
|
939
|
-
const calendarDates = db.prepare(
|
|
940
|
-
`SELECT * FROM calendar_dates WHERE exception_type = 1 AND service_id NOT IN (${serviceIds.map((serviceId) => `'${serviceId}'`).join(", ")})`
|
|
941
|
-
).all();
|
|
942
|
-
const timetablePages = routes.map((route) => {
|
|
931
|
+
const timetablePages = [];
|
|
932
|
+
const { calendars, calendarDates } = getCalendarsFromConfig(config);
|
|
933
|
+
for (const route of routes) {
|
|
943
934
|
const trips = getTrips(
|
|
944
935
|
{
|
|
945
936
|
route_id: route.route_id
|
|
946
937
|
},
|
|
947
938
|
["trip_headsign", "direction_id", "trip_id", "service_id"]
|
|
948
939
|
);
|
|
949
|
-
const
|
|
950
|
-
|
|
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);
|
|
951
946
|
const calendarDateGroups = groupBy(calendarDates, "service_id");
|
|
952
|
-
|
|
953
|
-
|
|
947
|
+
const timetables = [];
|
|
948
|
+
for (const uniqueTripDirection of uniqueTripDirections) {
|
|
949
|
+
for (const calendars2 of Object.values(calendarGroups)) {
|
|
954
950
|
const tripsForCalendars = trips.filter(
|
|
955
951
|
(trip) => some(calendars2, { service_id: trip.service_id })
|
|
956
952
|
);
|
|
957
953
|
if (tripsForCalendars.length > 0) {
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
954
|
+
timetables.push(
|
|
955
|
+
createTimetable({
|
|
956
|
+
route,
|
|
957
|
+
directionId: uniqueTripDirection.direction_id,
|
|
958
|
+
tripHeadsign: uniqueTripDirection.trip_headsign,
|
|
959
|
+
calendars: calendars2
|
|
960
|
+
})
|
|
964
961
|
);
|
|
965
962
|
}
|
|
966
|
-
}
|
|
967
|
-
Object.values(calendarDateGroups)
|
|
963
|
+
}
|
|
964
|
+
for (const calendarDates2 of Object.values(calendarDateGroups)) {
|
|
968
965
|
const tripsForCalendarDates = trips.filter(
|
|
969
966
|
(trip) => some(calendarDates2, { service_id: trip.service_id })
|
|
970
967
|
);
|
|
971
968
|
if (tripsForCalendarDates.length > 0) {
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
969
|
+
timetables.push(
|
|
970
|
+
createTimetable({
|
|
971
|
+
route,
|
|
972
|
+
directionId: uniqueTripDirection.direction_id,
|
|
973
|
+
tripHeadsign: uniqueTripDirection.trip_headsign,
|
|
974
|
+
calendarDates: calendarDates2
|
|
975
|
+
})
|
|
978
976
|
);
|
|
979
977
|
}
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
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;
|
|
984
1001
|
};
|
|
985
1002
|
var generateTripsByFrequencies = (trip, frequencies, config) => {
|
|
986
1003
|
const formattedFrequencies = frequencies.map(
|
|
@@ -1003,7 +1020,7 @@ var generateTripsByFrequencies = (trip, frequencies, config) => {
|
|
|
1003
1020
|
return trips;
|
|
1004
1021
|
};
|
|
1005
1022
|
var duplicateStopsForDifferentArrivalDeparture = (stopIds, timetable, config) => {
|
|
1006
|
-
if (config.showArrivalOnDifference === null) {
|
|
1023
|
+
if (config.showArrivalOnDifference === null || config.showArrivalOnDifference === void 0) {
|
|
1007
1024
|
return stopIds;
|
|
1008
1025
|
}
|
|
1009
1026
|
for (const trip of timetable.orderedTrips) {
|
|
@@ -1125,7 +1142,7 @@ var getStopsForTimetable = (timetable, config) => {
|
|
|
1125
1142
|
}
|
|
1126
1143
|
return stop;
|
|
1127
1144
|
});
|
|
1128
|
-
if (
|
|
1145
|
+
if (config.showStopCity) {
|
|
1129
1146
|
const stopAttributes = getStopAttributes({
|
|
1130
1147
|
stop_id: orderedStopIds
|
|
1131
1148
|
});
|
|
@@ -1140,6 +1157,39 @@ var getStopsForTimetable = (timetable, config) => {
|
|
|
1140
1157
|
}
|
|
1141
1158
|
return orderedStops;
|
|
1142
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
|
+
};
|
|
1143
1193
|
var getCalendarsFromTimetable = (timetable) => {
|
|
1144
1194
|
const db = openDb();
|
|
1145
1195
|
let whereClause = "";
|
|
@@ -1289,13 +1339,17 @@ var filterTrips = (timetable) => {
|
|
|
1289
1339
|
}
|
|
1290
1340
|
trip.stoptimes = combinedStoptimes;
|
|
1291
1341
|
}
|
|
1292
|
-
const timetableStopIds = new Set(
|
|
1342
|
+
const timetableStopIds = new Set(
|
|
1343
|
+
timetable.stops.map((stop) => stop.stop_id)
|
|
1344
|
+
);
|
|
1293
1345
|
for (const trip of filteredTrips) {
|
|
1294
1346
|
trip.stoptimes = trip.stoptimes.filter(
|
|
1295
1347
|
(stoptime) => timetableStopIds.has(stoptime.stop_id)
|
|
1296
1348
|
);
|
|
1297
1349
|
}
|
|
1298
|
-
filteredTrips = filteredTrips.filter(
|
|
1350
|
+
filteredTrips = filteredTrips.filter(
|
|
1351
|
+
(trip) => trip.stoptimes.length > 1
|
|
1352
|
+
);
|
|
1299
1353
|
return filteredTrips;
|
|
1300
1354
|
};
|
|
1301
1355
|
var getTripsForTimetable = (timetable, calendars, config) => {
|
|
@@ -1446,6 +1500,15 @@ var formatTimetables = (timetables, config) => {
|
|
|
1446
1500
|
};
|
|
1447
1501
|
function getTimetablePagesForAgency(config) {
|
|
1448
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
|
+
});
|
|
1449
1512
|
if (timetables.length === 0) {
|
|
1450
1513
|
return convertRoutesToTimetablePages(config);
|
|
1451
1514
|
}
|
|
@@ -1455,68 +1518,33 @@ function getTimetablePagesForAgency(config) {
|
|
|
1455
1518
|
[["timetable_page_id", "ASC"]]
|
|
1456
1519
|
);
|
|
1457
1520
|
if (timetablePages.length === 0) {
|
|
1458
|
-
return
|
|
1459
|
-
(timetable) =>
|
|
1521
|
+
return formattedTimetables.map(
|
|
1522
|
+
(timetable) => createTimetablePage({
|
|
1523
|
+
timetablePageId: timetable.timetable_id,
|
|
1524
|
+
timetables: [timetable],
|
|
1525
|
+
config
|
|
1526
|
+
})
|
|
1460
1527
|
);
|
|
1461
1528
|
}
|
|
1462
|
-
const routes = getRoutes();
|
|
1463
1529
|
return timetablePages.map((timetablePage) => {
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
);
|
|
1474
|
-
}
|
|
1475
|
-
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
|
+
};
|
|
1476
1539
|
});
|
|
1477
1540
|
}
|
|
1478
|
-
var
|
|
1479
|
-
const timetablePages = getTimetablePages({
|
|
1480
|
-
timetable_page_id: timetablePageId
|
|
1481
|
-
});
|
|
1482
|
-
const timetables = mergeTimetablesWithSameId(getTimetables());
|
|
1483
|
-
if (timetablePages.length > 1) {
|
|
1484
|
-
throw new Error(
|
|
1485
|
-
`Multiple timetable_pages found for timetable_page_id=${timetablePageId}`
|
|
1486
|
-
);
|
|
1487
|
-
}
|
|
1488
|
-
if (timetablePages.length === 1) {
|
|
1489
|
-
const timetablePage = timetablePages[0];
|
|
1490
|
-
timetablePage.timetables = sortBy(
|
|
1491
|
-
timetables.filter(
|
|
1492
|
-
(timetable) => timetable.timetable_page_id === timetablePageId
|
|
1493
|
-
),
|
|
1494
|
-
"timetable_sequence"
|
|
1495
|
-
);
|
|
1496
|
-
for (const timetable of timetablePage.timetables) {
|
|
1497
|
-
timetable.routes = getRoutes({
|
|
1498
|
-
route_id: timetable.route_ids
|
|
1499
|
-
});
|
|
1500
|
-
}
|
|
1501
|
-
return timetablePage;
|
|
1502
|
-
}
|
|
1503
|
-
if (timetables.length > 0) {
|
|
1504
|
-
const timetablePageTimetables = timetables.filter(
|
|
1505
|
-
(timetable) => timetable.timetable_id === timetablePageId
|
|
1506
|
-
);
|
|
1507
|
-
if (timetablePageTimetables.length === 0) {
|
|
1508
|
-
throw new Error(
|
|
1509
|
-
`No timetable found for timetable_page_id=${timetablePageId}`
|
|
1510
|
-
);
|
|
1511
|
-
}
|
|
1512
|
-
return convertTimetableToTimetablePage(timetablePageTimetables[0], config);
|
|
1513
|
-
}
|
|
1541
|
+
var getDataForTimetablePageById = (timetablePageId) => {
|
|
1514
1542
|
let calendarCode;
|
|
1515
1543
|
let calendars;
|
|
1516
1544
|
let calendarDates;
|
|
1517
1545
|
let serviceId;
|
|
1518
1546
|
let directionId = "";
|
|
1519
|
-
const parts = timetablePageId
|
|
1547
|
+
const parts = timetablePageId?.split("|") ?? [];
|
|
1520
1548
|
if (parts.length > 2) {
|
|
1521
1549
|
directionId = Number.parseInt(parts.pop(), 10);
|
|
1522
1550
|
calendarCode = parts.pop();
|
|
@@ -1535,13 +1563,13 @@ var getTimetablePageById = (timetablePageId, config) => {
|
|
|
1535
1563
|
},
|
|
1536
1564
|
["trip_headsign", "direction_id"]
|
|
1537
1565
|
);
|
|
1538
|
-
const
|
|
1539
|
-
if (
|
|
1566
|
+
const uniqueTripDirections = uniqBy(trips, (trip) => trip.direction_id);
|
|
1567
|
+
if (uniqueTripDirections.length === 0) {
|
|
1540
1568
|
throw new Error(
|
|
1541
1569
|
`No trips found for timetable_page_id=${timetablePageId} route_id=${routeId} direction_id=${directionId}`
|
|
1542
1570
|
);
|
|
1543
1571
|
}
|
|
1544
|
-
if (/^[01]*$/.test(calendarCode)) {
|
|
1572
|
+
if (/^[01]*$/.test(calendarCode ?? "")) {
|
|
1545
1573
|
calendars = getCalendars({
|
|
1546
1574
|
...calendarCodeToCalendar(calendarCode)
|
|
1547
1575
|
});
|
|
@@ -1552,13 +1580,129 @@ var getTimetablePageById = (timetablePageId, config) => {
|
|
|
1552
1580
|
service_id: serviceId
|
|
1553
1581
|
});
|
|
1554
1582
|
}
|
|
1555
|
-
return
|
|
1556
|
-
routes[0],
|
|
1557
|
-
directions[0],
|
|
1583
|
+
return {
|
|
1558
1584
|
calendars,
|
|
1559
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],
|
|
1560
1704
|
config
|
|
1561
|
-
);
|
|
1705
|
+
});
|
|
1562
1706
|
};
|
|
1563
1707
|
function setDefaultConfig(initialConfig) {
|
|
1564
1708
|
const defaults = {
|
|
@@ -1579,6 +1723,7 @@ function setDefaultConfig(initialConfig) {
|
|
|
1579
1723
|
defaultOrientation: "vertical",
|
|
1580
1724
|
interpolatedStopSymbol: "\u2022",
|
|
1581
1725
|
interpolatedStopText: "Estimated time of arrival",
|
|
1726
|
+
groupTimetablesIntoPages: true,
|
|
1582
1727
|
gtfsToHtmlVersion: version,
|
|
1583
1728
|
linkStopUrls: false,
|
|
1584
1729
|
mapStyleUrl: "https://tiles.openfreemap.org/styles/positron",
|
|
@@ -1635,12 +1780,6 @@ function getFormattedTimetablePage(timetablePageId, config) {
|
|
|
1635
1780
|
timetablePageId,
|
|
1636
1781
|
config
|
|
1637
1782
|
);
|
|
1638
|
-
const timetableRoutes = getRoutes(
|
|
1639
|
-
{
|
|
1640
|
-
route_id: timetablePage.route_ids
|
|
1641
|
-
},
|
|
1642
|
-
["agency_id"]
|
|
1643
|
-
);
|
|
1644
1783
|
const consolidatedTimetables = formatTimetables(
|
|
1645
1784
|
timetablePage.timetables,
|
|
1646
1785
|
config
|
|
@@ -1656,7 +1795,7 @@ function getFormattedTimetablePage(timetablePageId, config) {
|
|
|
1656
1795
|
}
|
|
1657
1796
|
}
|
|
1658
1797
|
const uniqueRoutes = uniqBy(
|
|
1659
|
-
|
|
1798
|
+
flatMap(consolidatedTimetables, (timetable) => timetable.routes),
|
|
1660
1799
|
"route_id"
|
|
1661
1800
|
);
|
|
1662
1801
|
const formattedTimetablePage = {
|
|
@@ -1667,7 +1806,7 @@ function getFormattedTimetablePage(timetablePageId, config) {
|
|
|
1667
1806
|
consolidatedTimetables.map((timetable) => timetable.dayList)
|
|
1668
1807
|
),
|
|
1669
1808
|
route_ids: uniqueRoutes.map((route) => route.route_id),
|
|
1670
|
-
agency_ids: uniq(compact(
|
|
1809
|
+
agency_ids: uniq(compact(uniqueRoutes.map((route) => route.agency_id))),
|
|
1671
1810
|
filename: timetablePage.filename ?? `${timetablePage.timetable_page_id}.html`,
|
|
1672
1811
|
timetable_page_label: timetablePage.timetable_page_label ?? formatListForDisplay(uniqueRoutes.map((route) => formatRouteName(route)))
|
|
1673
1812
|
};
|
|
@@ -1925,12 +2064,14 @@ function formatFrequency(frequency, config) {
|
|
|
1925
2064
|
frequency.headway_min = Math.round(headway.asMinutes());
|
|
1926
2065
|
return frequency;
|
|
1927
2066
|
}
|
|
1928
|
-
function formatTimetableId(
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
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}`;
|
|
1934
2075
|
}
|
|
1935
2076
|
return timetableId;
|
|
1936
2077
|
}
|
|
@@ -2083,10 +2224,18 @@ function formatTimetableLabel(timetable) {
|
|
|
2083
2224
|
return timetableLabel;
|
|
2084
2225
|
}
|
|
2085
2226
|
var formatRouteName = (route) => {
|
|
2086
|
-
if (route.route_long_name
|
|
2087
|
-
return `Route ${route.
|
|
2227
|
+
if (route.route_long_name) {
|
|
2228
|
+
return `Route ${route.route_long_name}`;
|
|
2088
2229
|
}
|
|
2089
|
-
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";
|
|
2090
2239
|
};
|
|
2091
2240
|
var formatListForDisplay = (list) => {
|
|
2092
2241
|
return new Intl.ListFormat("en-US", {
|
|
@@ -2109,6 +2258,7 @@ function mergeTimetablesWithSameId(timetables) {
|
|
|
2109
2258
|
}
|
|
2110
2259
|
|
|
2111
2260
|
// src/lib/file-utils.ts
|
|
2261
|
+
var homeDirectory = homedir();
|
|
2112
2262
|
function getPathToViewsFolder(config) {
|
|
2113
2263
|
if (config.templatePath) {
|
|
2114
2264
|
return untildify(config.templatePath);
|
|
@@ -2193,15 +2343,34 @@ function zipFolder(outputPath) {
|
|
|
2193
2343
|
archive.finalize();
|
|
2194
2344
|
});
|
|
2195
2345
|
}
|
|
2196
|
-
function
|
|
2197
|
-
|
|
2346
|
+
function generateTimetablePageFileName(timetablePage, config) {
|
|
2347
|
+
if (timetablePage.filename) {
|
|
2348
|
+
return sanitize(timetablePage.filename);
|
|
2349
|
+
}
|
|
2350
|
+
if (config.groupTimetablesIntoPages === true && uniqBy2(timetablePage.timetables, "route_id").length === 1) {
|
|
2351
|
+
const route = timetablePage.timetables[0].routes[0];
|
|
2352
|
+
return sanitize(`${formatRouteNameForFilename(route).toLowerCase()}.html`);
|
|
2353
|
+
}
|
|
2354
|
+
const timetable = timetablePage.timetables[0];
|
|
2355
|
+
let filename = timetable.timetable_id ?? "";
|
|
2356
|
+
for (const route of timetable.routes) {
|
|
2357
|
+
filename += `_${formatRouteNameForFilename(route)}`;
|
|
2358
|
+
}
|
|
2359
|
+
if (!isNullOrEmpty(timetable.direction_id)) {
|
|
2360
|
+
filename += `_${timetable.direction_id}`;
|
|
2361
|
+
}
|
|
2362
|
+
filename += `_${formatDays(timetable, config).replace(/\s/g, "")}.html`;
|
|
2363
|
+
return sanitize(filename.toLowerCase());
|
|
2364
|
+
}
|
|
2365
|
+
function generateCSVFileName(timetable, config) {
|
|
2366
|
+
let filename = timetable.timetable_id ?? "";
|
|
2198
2367
|
for (const route of timetable.routes) {
|
|
2199
|
-
filename +=
|
|
2368
|
+
filename += `_${formatRouteNameForFilename(route)}`;
|
|
2200
2369
|
}
|
|
2201
2370
|
if (!isNullOrEmpty(timetable.direction_id)) {
|
|
2202
2371
|
filename += `_${timetable.direction_id}`;
|
|
2203
2372
|
}
|
|
2204
|
-
filename += `_${formatDays(timetable, config).replace(/\s/g, "")}
|
|
2373
|
+
filename += `_${formatDays(timetable, config).replace(/\s/g, "")}.csv`;
|
|
2205
2374
|
return sanitize(filename).toLowerCase();
|
|
2206
2375
|
}
|
|
2207
2376
|
function generateFolderName(timetablePage) {
|
|
@@ -2241,16 +2410,18 @@ async function renderPdf(htmlPath) {
|
|
|
2241
2410
|
});
|
|
2242
2411
|
await browser.close();
|
|
2243
2412
|
}
|
|
2413
|
+
function untildify(pathWithTilde) {
|
|
2414
|
+
return homeDirectory ? pathWithTilde.replace(/^~(?=$|\/|\\)/, homeDirectory) : pathWithTilde;
|
|
2415
|
+
}
|
|
2244
2416
|
|
|
2245
2417
|
// src/lib/gtfs-to-html.ts
|
|
2246
2418
|
var gtfsToHtml = async (initialConfig) => {
|
|
2247
2419
|
const config = setDefaultConfig(initialConfig);
|
|
2248
|
-
const
|
|
2420
|
+
const startTime = process.hrtime.bigint();
|
|
2249
2421
|
const agencyKey = config.agencies.map(
|
|
2250
2422
|
(agency) => agency.agencyKey ?? agency.agency_key ?? "unknown"
|
|
2251
2423
|
).join("-");
|
|
2252
|
-
const outputPath = config.outputPath ?
|
|
2253
|
-
timer.start();
|
|
2424
|
+
const outputPath = config.outputPath ? untildify(config.outputPath) : path.join(process.cwd(), "html", sanitize2(agencyKey));
|
|
2254
2425
|
await prepDirectory(outputPath, config);
|
|
2255
2426
|
try {
|
|
2256
2427
|
openDb2(config);
|
|
@@ -2296,9 +2467,11 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2296
2467
|
config
|
|
2297
2468
|
);
|
|
2298
2469
|
for (const timetable of timetablePage.timetables) {
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2470
|
+
if (timetable.warnings) {
|
|
2471
|
+
for (const warning of timetable.warnings) {
|
|
2472
|
+
stats.warnings.push(warning);
|
|
2473
|
+
bar?.interrupt(warning);
|
|
2474
|
+
}
|
|
2302
2475
|
}
|
|
2303
2476
|
}
|
|
2304
2477
|
if (timetablePage.consolidatedTimetables.length === 0) {
|
|
@@ -2321,7 +2494,7 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2321
2494
|
const csvPath = path.join(
|
|
2322
2495
|
outputPath,
|
|
2323
2496
|
datePath,
|
|
2324
|
-
|
|
2497
|
+
generateCSVFileName(timetable, config)
|
|
2325
2498
|
);
|
|
2326
2499
|
await writeFile(csvPath, csv);
|
|
2327
2500
|
}
|
|
@@ -2367,11 +2540,11 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2367
2540
|
`${agencyKey}: ${config.outputFormat.toUpperCase()} timetables created at ${fullOutputPath}`
|
|
2368
2541
|
);
|
|
2369
2542
|
logStats(config)(stats);
|
|
2370
|
-
const
|
|
2543
|
+
const endTime = process.hrtime.bigint();
|
|
2544
|
+
const elapsedSeconds = Number(endTime - startTime) / 1e9;
|
|
2371
2545
|
log(config)(
|
|
2372
|
-
`${agencyKey}: ${config.outputFormat.toUpperCase()} timetable generation required ${
|
|
2546
|
+
`${agencyKey}: ${config.outputFormat.toUpperCase()} timetable generation required ${elapsedSeconds.toFixed(1)} seconds`
|
|
2373
2547
|
);
|
|
2374
|
-
timer.stop();
|
|
2375
2548
|
return fullOutputPath;
|
|
2376
2549
|
};
|
|
2377
2550
|
var gtfs_to_html_default = gtfsToHtml;
|