gtfs-to-html 2.10.17 → 2.11.1
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 +367 -201
- package/dist/app/index.js.map +1 -1
- package/dist/bin/gtfs-to-html.js +399 -211
- package/dist/bin/gtfs-to-html.js.map +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.js +399 -211
- package/dist/index.js.map +1 -1
- package/package.json +9 -10
- 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,16 @@ import {
|
|
|
25
23
|
readFile,
|
|
26
24
|
rm
|
|
27
25
|
} from "fs/promises";
|
|
26
|
+
import { homedir } from "os";
|
|
27
|
+
import { findPackageJSON } from "module";
|
|
28
28
|
import * as _ from "lodash-es";
|
|
29
|
+
import { uniqBy as uniqBy2 } from "lodash-es";
|
|
29
30
|
import archiver from "archiver";
|
|
30
31
|
import beautify from "js-beautify";
|
|
31
32
|
import sanitizeHtml from "sanitize-html";
|
|
32
33
|
import { renderFile } from "pug";
|
|
33
34
|
import puppeteer from "puppeteer";
|
|
34
35
|
import sanitize from "sanitize-filename";
|
|
35
|
-
import untildify from "untildify";
|
|
36
36
|
import { marked } from "marked";
|
|
37
37
|
|
|
38
38
|
// src/lib/formatters.ts
|
|
@@ -61,16 +61,7 @@ function fromGTFSTime(timeString) {
|
|
|
61
61
|
function toGTFSTime(time) {
|
|
62
62
|
return time.format("HH:mm:ss");
|
|
63
63
|
}
|
|
64
|
-
function fromGTFSDate(gtfsDate) {
|
|
65
|
-
return moment(gtfsDate, "YYYYMMDD");
|
|
66
|
-
}
|
|
67
|
-
function toGTFSDate(date) {
|
|
68
|
-
return moment(date).format("YYYYMMDD");
|
|
69
|
-
}
|
|
70
64
|
function calendarToCalendarCode(c) {
|
|
71
|
-
if (c.service_id) {
|
|
72
|
-
return c.service_id;
|
|
73
|
-
}
|
|
74
65
|
return `${c.monday}${c.tuesday}${c.wednesday}${c.thursday}${c.friday}${c.saturday}${c.sunday}`;
|
|
75
66
|
}
|
|
76
67
|
function calendarCodeToCalendar(code) {
|
|
@@ -111,13 +102,13 @@ import {
|
|
|
111
102
|
find,
|
|
112
103
|
findLast,
|
|
113
104
|
first,
|
|
114
|
-
flatMap
|
|
115
|
-
flattenDeep,
|
|
105
|
+
flatMap,
|
|
116
106
|
flow,
|
|
117
107
|
groupBy,
|
|
118
108
|
head,
|
|
119
109
|
last,
|
|
120
110
|
maxBy,
|
|
111
|
+
orderBy,
|
|
121
112
|
partialRight,
|
|
122
113
|
reduce,
|
|
123
114
|
size,
|
|
@@ -151,7 +142,6 @@ import toposort from "toposort";
|
|
|
151
142
|
|
|
152
143
|
// src/lib/geojson-utils.ts
|
|
153
144
|
import { getShapesAsGeoJSON, getStopsAsGeoJSON } from "gtfs";
|
|
154
|
-
import { flatMap } from "lodash-es";
|
|
155
145
|
import simplify from "@turf/simplify";
|
|
156
146
|
import { featureCollection, round } from "@turf/helpers";
|
|
157
147
|
|
|
@@ -313,7 +303,7 @@ function progressBar(formatString, barTotal, config) {
|
|
|
313
303
|
}
|
|
314
304
|
|
|
315
305
|
// src/lib/geojson-utils.ts
|
|
316
|
-
var mergeGeojson = (...geojsons) => featureCollection(flatMap(
|
|
306
|
+
var mergeGeojson = (...geojsons) => featureCollection(geojsons.flatMap((geojson) => geojson.features));
|
|
317
307
|
var truncateGeoJSONDecimals = (geojson, config) => {
|
|
318
308
|
for (const feature of geojson.features) {
|
|
319
309
|
if (feature.geometry.coordinates) {
|
|
@@ -504,7 +494,7 @@ function formatTripNameForCSV(trip, timetable) {
|
|
|
504
494
|
// package.json
|
|
505
495
|
var package_default = {
|
|
506
496
|
name: "gtfs-to-html",
|
|
507
|
-
version: "2.
|
|
497
|
+
version: "2.11.1",
|
|
508
498
|
private: false,
|
|
509
499
|
description: "Build human readable transit timetables as HTML, PDF or CSV from GTFS",
|
|
510
500
|
keywords: [
|
|
@@ -557,24 +547,22 @@ var package_default = {
|
|
|
557
547
|
"cli-table": "^0.3.11",
|
|
558
548
|
"csv-stringify": "^6.6.0",
|
|
559
549
|
express: "^5.1.0",
|
|
560
|
-
gtfs: "^4.
|
|
550
|
+
gtfs: "^4.18.0",
|
|
561
551
|
"gtfs-realtime-pbf-js-module": "^1.0.0",
|
|
562
552
|
"js-beautify": "^1.15.4",
|
|
563
553
|
"lodash-es": "^4.17.21",
|
|
564
|
-
marked: "^16.
|
|
554
|
+
marked: "^16.3.0",
|
|
565
555
|
moment: "^2.30.1",
|
|
566
556
|
pbf: "^4.0.1",
|
|
567
557
|
"pretty-error": "^4.0.0",
|
|
568
558
|
pug: "^3.0.3",
|
|
569
|
-
puppeteer: "^24.
|
|
559
|
+
puppeteer: "^24.21.0",
|
|
570
560
|
"sanitize-filename": "^1.6.3",
|
|
571
561
|
"sanitize-html": "^2.17.0",
|
|
572
562
|
sqlstring: "^2.3.3",
|
|
573
|
-
"timer-machine": "^1.1.0",
|
|
574
563
|
toposort: "^2.0.2",
|
|
575
|
-
untildify: "^5.0.0",
|
|
576
564
|
yargs: "^18.0.0",
|
|
577
|
-
yoctocolors: "^2.1.
|
|
565
|
+
yoctocolors: "^2.1.2"
|
|
578
566
|
},
|
|
579
567
|
devDependencies: {
|
|
580
568
|
"@types/archiver": "^6.0.3",
|
|
@@ -586,16 +574,17 @@ var package_default = {
|
|
|
586
574
|
"@types/node": "^22",
|
|
587
575
|
"@types/pug": "^2.0.10",
|
|
588
576
|
"@types/sanitize-html": "^2.16.0",
|
|
589
|
-
"@types/
|
|
577
|
+
"@types/sqlstring": "^2.3.2",
|
|
578
|
+
"@types/toposort": "^2.0.7",
|
|
590
579
|
"@types/yargs": "^17.0.33",
|
|
591
580
|
husky: "^9.1.7",
|
|
592
|
-
"lint-staged": "^16.1.
|
|
581
|
+
"lint-staged": "^16.1.6",
|
|
593
582
|
prettier: "^3.6.2",
|
|
594
583
|
tsup: "^8.5.0",
|
|
595
584
|
typescript: "^5.9.2"
|
|
596
585
|
},
|
|
597
586
|
engines: {
|
|
598
|
-
node: ">=
|
|
587
|
+
node: ">= 22"
|
|
599
588
|
},
|
|
600
589
|
"release-it": {
|
|
601
590
|
github: {
|
|
@@ -645,7 +634,7 @@ var findCommonStopId = (trips, config) => {
|
|
|
645
634
|
return null;
|
|
646
635
|
}
|
|
647
636
|
const commonStoptime = longestTripStoptimes.find((stoptime, idx) => {
|
|
648
|
-
if (idx === 0 && stoptime.stop_id === last(longestTripStoptimes)
|
|
637
|
+
if (idx === 0 && stoptime.stop_id === last(longestTripStoptimes)?.stop_id) {
|
|
649
638
|
return false;
|
|
650
639
|
}
|
|
651
640
|
if (isNullOrEmpty(stoptime.arrival_time)) {
|
|
@@ -698,11 +687,7 @@ var sortTrips = (trips, config) => {
|
|
|
698
687
|
trip.stoptimes[trip.stoptimes.length - 1].departure_time
|
|
699
688
|
);
|
|
700
689
|
}
|
|
701
|
-
sortedTrips = sortBy(
|
|
702
|
-
trips,
|
|
703
|
-
["firstStoptime", "lastStoptime"],
|
|
704
|
-
["asc", "asc"]
|
|
705
|
-
);
|
|
690
|
+
sortedTrips = sortBy(trips, ["firstStoptime", "lastStoptime"]);
|
|
706
691
|
} else if (config.sortingAlgorithm === "end") {
|
|
707
692
|
for (const trip of trips) {
|
|
708
693
|
if (trip.stoptimes.length === 0) {
|
|
@@ -713,11 +698,7 @@ var sortTrips = (trips, config) => {
|
|
|
713
698
|
trip.stoptimes[trip.stoptimes.length - 1].departure_time
|
|
714
699
|
);
|
|
715
700
|
}
|
|
716
|
-
sortedTrips = sortBy(
|
|
717
|
-
trips,
|
|
718
|
-
["lastStoptime", "firstStoptime"],
|
|
719
|
-
["asc", "asc"]
|
|
720
|
-
);
|
|
701
|
+
sortedTrips = sortBy(trips, ["lastStoptime", "firstStoptime"]);
|
|
721
702
|
} else if (config.sortingAlgorithm === "first") {
|
|
722
703
|
const longestTripStoptimes = getLongestTripStoptimes(trips, config);
|
|
723
704
|
const firstStopId = first(longestTripStoptimes).stop_id;
|
|
@@ -744,8 +725,8 @@ var getCalendarDatesForTimetable = (timetable, config) => {
|
|
|
744
725
|
[],
|
|
745
726
|
[["date", "ASC"]]
|
|
746
727
|
);
|
|
747
|
-
const start =
|
|
748
|
-
const end =
|
|
728
|
+
const start = moment2(timetable.start_date, "YYYYMMDD");
|
|
729
|
+
const end = moment2(timetable.end_date, "YYYYMMDD");
|
|
749
730
|
const excludedDates = /* @__PURE__ */ new Set();
|
|
750
731
|
const includedDates = /* @__PURE__ */ new Set();
|
|
751
732
|
for (const calendarDate of calendarDates) {
|
|
@@ -870,28 +851,70 @@ var getTimetableNotesForTimetable = (timetable, config) => {
|
|
|
870
851
|
}));
|
|
871
852
|
return sortBy(formattedNotes, "symbol");
|
|
872
853
|
};
|
|
873
|
-
var
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
854
|
+
var createTimetablePage = ({
|
|
855
|
+
timetablePageId,
|
|
856
|
+
timetables,
|
|
857
|
+
config
|
|
858
|
+
}) => {
|
|
859
|
+
const updatedTimetables = timetables.map((timetable) => {
|
|
860
|
+
if (!timetable.routes) {
|
|
861
|
+
timetable.routes = getRoutes({
|
|
862
|
+
route_id: timetable.route_ids
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
return timetable;
|
|
866
|
+
});
|
|
867
|
+
const timetablePage = {
|
|
868
|
+
timetable_page_id: timetablePageId,
|
|
869
|
+
timetables: updatedTimetables,
|
|
870
|
+
routes: updatedTimetables.flatMap((timetable) => timetable.routes)
|
|
871
|
+
};
|
|
872
|
+
const filename = generateTimetablePageFileName(timetablePage, config);
|
|
880
873
|
return {
|
|
881
|
-
|
|
882
|
-
timetable_page_label: timetable.timetable_label,
|
|
883
|
-
timetables: [timetable],
|
|
874
|
+
...timetablePage,
|
|
884
875
|
filename
|
|
885
876
|
};
|
|
886
877
|
};
|
|
887
|
-
var
|
|
888
|
-
|
|
878
|
+
var createTimetable = ({
|
|
879
|
+
route,
|
|
880
|
+
directionId,
|
|
881
|
+
tripHeadsign,
|
|
882
|
+
calendars,
|
|
883
|
+
calendarDates
|
|
884
|
+
}) => {
|
|
885
|
+
const serviceIds = uniq([
|
|
886
|
+
...calendars?.map((calendar) => calendar.service_id) ?? [],
|
|
887
|
+
...calendarDates?.map((calendarDate) => calendarDate.service_id) ?? []
|
|
888
|
+
]);
|
|
889
|
+
const days2 = {};
|
|
890
|
+
let startDate = null;
|
|
891
|
+
let endDate = null;
|
|
892
|
+
if (calendars && calendars.length > 0) {
|
|
893
|
+
Object.assign(days2, getDaysFromCalendars(calendars));
|
|
894
|
+
startDate = parseInt(
|
|
895
|
+
moment2.min(
|
|
896
|
+
calendars.map((calendar) => moment2(calendar.start_date, "YYYYMMDD"))
|
|
897
|
+
).format("YYYYMMDD"),
|
|
898
|
+
10
|
|
899
|
+
);
|
|
900
|
+
endDate = parseInt(
|
|
901
|
+
moment2.max(calendars.map((calendar) => moment2(calendar.end_date, "YYYYMMDD"))).format("YYYYMMDD"),
|
|
902
|
+
10
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
const timetableId = formatTimetableId({
|
|
906
|
+
routeIds: [route.route_id],
|
|
907
|
+
directionId,
|
|
908
|
+
days: days2
|
|
909
|
+
});
|
|
910
|
+
return {
|
|
911
|
+
timetable_id: timetableId,
|
|
889
912
|
route_ids: [route.route_id],
|
|
890
|
-
direction_id:
|
|
891
|
-
direction_name:
|
|
913
|
+
direction_id: directionId === null ? null : directionId,
|
|
914
|
+
direction_name: tripHeadsign === null ? null : tripHeadsign,
|
|
892
915
|
routes: [route],
|
|
893
916
|
include_exceptions: calendarDates && calendarDates.length > 0 ? 1 : 0,
|
|
894
|
-
|
|
917
|
+
service_ids: serviceIds,
|
|
895
918
|
service_notes: null,
|
|
896
919
|
timetable_label: null,
|
|
897
920
|
start_time: null,
|
|
@@ -899,88 +922,83 @@ var convertRouteToTimetablePage = (route, direction, calendars, calendarDates, c
|
|
|
899
922
|
orientation: null,
|
|
900
923
|
timetable_sequence: null,
|
|
901
924
|
show_trip_continuation: null,
|
|
902
|
-
start_date:
|
|
903
|
-
end_date:
|
|
925
|
+
start_date: startDate,
|
|
926
|
+
end_date: endDate,
|
|
927
|
+
...days2
|
|
904
928
|
};
|
|
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
929
|
};
|
|
919
930
|
var convertRoutesToTimetablePages = (config) => {
|
|
920
|
-
const db = openDb(config);
|
|
921
931
|
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) => {
|
|
932
|
+
const timetablePages = [];
|
|
933
|
+
const { calendars, calendarDates } = getCalendarsFromConfig(config);
|
|
934
|
+
for (const route of routes) {
|
|
943
935
|
const trips = getTrips(
|
|
944
936
|
{
|
|
945
937
|
route_id: route.route_id
|
|
946
938
|
},
|
|
947
939
|
["trip_headsign", "direction_id", "trip_id", "service_id"]
|
|
948
940
|
);
|
|
949
|
-
const
|
|
950
|
-
|
|
941
|
+
const uniqueTripDirections = orderBy(
|
|
942
|
+
uniqBy(trips, (trip) => trip.direction_id),
|
|
943
|
+
"direction_id"
|
|
944
|
+
);
|
|
945
|
+
const sortedCalendars = orderBy(calendars, calendarToCalendarCode, "desc");
|
|
946
|
+
const calendarGroups = groupBy(sortedCalendars, calendarToCalendarCode);
|
|
951
947
|
const calendarDateGroups = groupBy(calendarDates, "service_id");
|
|
952
|
-
|
|
953
|
-
|
|
948
|
+
const timetables = [];
|
|
949
|
+
for (const uniqueTripDirection of uniqueTripDirections) {
|
|
950
|
+
for (const calendars2 of Object.values(calendarGroups)) {
|
|
954
951
|
const tripsForCalendars = trips.filter(
|
|
955
952
|
(trip) => some(calendars2, { service_id: trip.service_id })
|
|
956
953
|
);
|
|
957
954
|
if (tripsForCalendars.length > 0) {
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
955
|
+
timetables.push(
|
|
956
|
+
createTimetable({
|
|
957
|
+
route,
|
|
958
|
+
directionId: uniqueTripDirection.direction_id,
|
|
959
|
+
tripHeadsign: uniqueTripDirection.trip_headsign,
|
|
960
|
+
calendars: calendars2
|
|
961
|
+
})
|
|
964
962
|
);
|
|
965
963
|
}
|
|
966
|
-
}
|
|
967
|
-
Object.values(calendarDateGroups)
|
|
964
|
+
}
|
|
965
|
+
for (const calendarDates2 of Object.values(calendarDateGroups)) {
|
|
968
966
|
const tripsForCalendarDates = trips.filter(
|
|
969
967
|
(trip) => some(calendarDates2, { service_id: trip.service_id })
|
|
970
968
|
);
|
|
971
969
|
if (tripsForCalendarDates.length > 0) {
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
970
|
+
timetables.push(
|
|
971
|
+
createTimetable({
|
|
972
|
+
route,
|
|
973
|
+
directionId: uniqueTripDirection.direction_id,
|
|
974
|
+
tripHeadsign: uniqueTripDirection.trip_headsign,
|
|
975
|
+
calendarDates: calendarDates2
|
|
976
|
+
})
|
|
978
977
|
);
|
|
979
978
|
}
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
if (config.groupTimetablesIntoPages === true) {
|
|
982
|
+
timetablePages.push(
|
|
983
|
+
createTimetablePage({
|
|
984
|
+
timetablePageId: `route_${route.route_short_name ?? route.route_long_name}`,
|
|
985
|
+
timetables,
|
|
986
|
+
config
|
|
987
|
+
})
|
|
988
|
+
);
|
|
989
|
+
} else {
|
|
990
|
+
for (const timetable of timetables) {
|
|
991
|
+
timetablePages.push(
|
|
992
|
+
createTimetablePage({
|
|
993
|
+
timetablePageId: timetable.timetable_id,
|
|
994
|
+
timetables: [timetable],
|
|
995
|
+
config
|
|
996
|
+
})
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return timetablePages;
|
|
984
1002
|
};
|
|
985
1003
|
var generateTripsByFrequencies = (trip, frequencies, config) => {
|
|
986
1004
|
const formattedFrequencies = frequencies.map(
|
|
@@ -1003,7 +1021,7 @@ var generateTripsByFrequencies = (trip, frequencies, config) => {
|
|
|
1003
1021
|
return trips;
|
|
1004
1022
|
};
|
|
1005
1023
|
var duplicateStopsForDifferentArrivalDeparture = (stopIds, timetable, config) => {
|
|
1006
|
-
if (config.showArrivalOnDifference === null) {
|
|
1024
|
+
if (config.showArrivalOnDifference === null || config.showArrivalOnDifference === void 0) {
|
|
1007
1025
|
return stopIds;
|
|
1008
1026
|
}
|
|
1009
1027
|
for (const trip of timetable.orderedTrips) {
|
|
@@ -1125,7 +1143,7 @@ var getStopsForTimetable = (timetable, config) => {
|
|
|
1125
1143
|
}
|
|
1126
1144
|
return stop;
|
|
1127
1145
|
});
|
|
1128
|
-
if (
|
|
1146
|
+
if (config.showStopCity) {
|
|
1129
1147
|
const stopAttributes = getStopAttributes({
|
|
1130
1148
|
stop_id: orderedStopIds
|
|
1131
1149
|
});
|
|
@@ -1140,6 +1158,39 @@ var getStopsForTimetable = (timetable, config) => {
|
|
|
1140
1158
|
}
|
|
1141
1159
|
return orderedStops;
|
|
1142
1160
|
};
|
|
1161
|
+
var getCalendarsFromConfig = (config) => {
|
|
1162
|
+
const db = openDb();
|
|
1163
|
+
let whereClause = "";
|
|
1164
|
+
const whereClauses = [];
|
|
1165
|
+
if (config.endDate) {
|
|
1166
|
+
if (!moment2(config.endDate).isValid()) {
|
|
1167
|
+
throw new Error(`Invalid endDate=${config.endDate} in config.json`);
|
|
1168
|
+
}
|
|
1169
|
+
whereClauses.push(
|
|
1170
|
+
`start_date <= ${sqlString.escape(moment2(config.endDate).format("YYYYMMDD"))}`
|
|
1171
|
+
);
|
|
1172
|
+
}
|
|
1173
|
+
if (config.startDate) {
|
|
1174
|
+
if (!moment2(config.startDate).isValid()) {
|
|
1175
|
+
throw new Error(`Invalid startDate=${config.startDate} in config.json`);
|
|
1176
|
+
}
|
|
1177
|
+
whereClauses.push(
|
|
1178
|
+
`end_date >= ${sqlString.escape(moment2(config.startDate).format("YYYYMMDD"))}`
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
if (whereClauses.length > 0) {
|
|
1182
|
+
whereClause = `WHERE ${whereClauses.join(" AND ")}`;
|
|
1183
|
+
}
|
|
1184
|
+
const calendars = db.prepare(`SELECT * FROM calendar ${whereClause}`).all();
|
|
1185
|
+
const serviceIds = calendars.map((calendar) => calendar.service_id);
|
|
1186
|
+
const calendarDates = db.prepare(
|
|
1187
|
+
`SELECT * FROM calendar_dates WHERE exception_type = 1 AND service_id NOT IN (${serviceIds.map((serviceId) => `'${serviceId}'`).join(", ")})`
|
|
1188
|
+
).all();
|
|
1189
|
+
return {
|
|
1190
|
+
calendars,
|
|
1191
|
+
calendarDates
|
|
1192
|
+
};
|
|
1193
|
+
};
|
|
1143
1194
|
var getCalendarsFromTimetable = (timetable) => {
|
|
1144
1195
|
const db = openDb();
|
|
1145
1196
|
let whereClause = "";
|
|
@@ -1289,13 +1340,17 @@ var filterTrips = (timetable) => {
|
|
|
1289
1340
|
}
|
|
1290
1341
|
trip.stoptimes = combinedStoptimes;
|
|
1291
1342
|
}
|
|
1292
|
-
const timetableStopIds = new Set(
|
|
1343
|
+
const timetableStopIds = new Set(
|
|
1344
|
+
timetable.stops.map((stop) => stop.stop_id)
|
|
1345
|
+
);
|
|
1293
1346
|
for (const trip of filteredTrips) {
|
|
1294
1347
|
trip.stoptimes = trip.stoptimes.filter(
|
|
1295
1348
|
(stoptime) => timetableStopIds.has(stoptime.stop_id)
|
|
1296
1349
|
);
|
|
1297
1350
|
}
|
|
1298
|
-
filteredTrips = filteredTrips.filter(
|
|
1351
|
+
filteredTrips = filteredTrips.filter(
|
|
1352
|
+
(trip) => trip.stoptimes.length > 1
|
|
1353
|
+
);
|
|
1299
1354
|
return filteredTrips;
|
|
1300
1355
|
};
|
|
1301
1356
|
var getTripsForTimetable = (timetable, calendars, config) => {
|
|
@@ -1446,6 +1501,15 @@ var formatTimetables = (timetables, config) => {
|
|
|
1446
1501
|
};
|
|
1447
1502
|
function getTimetablePagesForAgency(config) {
|
|
1448
1503
|
const timetables = mergeTimetablesWithSameId(getTimetables());
|
|
1504
|
+
const routes = getRoutes();
|
|
1505
|
+
const formattedTimetables = timetables.map((timetable) => {
|
|
1506
|
+
return {
|
|
1507
|
+
...timetable,
|
|
1508
|
+
routes: routes.filter(
|
|
1509
|
+
(route) => timetable.route_ids.includes(route.route_id)
|
|
1510
|
+
)
|
|
1511
|
+
};
|
|
1512
|
+
});
|
|
1449
1513
|
if (timetables.length === 0) {
|
|
1450
1514
|
return convertRoutesToTimetablePages(config);
|
|
1451
1515
|
}
|
|
@@ -1455,68 +1519,33 @@ function getTimetablePagesForAgency(config) {
|
|
|
1455
1519
|
[["timetable_page_id", "ASC"]]
|
|
1456
1520
|
);
|
|
1457
1521
|
if (timetablePages.length === 0) {
|
|
1458
|
-
return
|
|
1459
|
-
(timetable) =>
|
|
1522
|
+
return formattedTimetables.map(
|
|
1523
|
+
(timetable) => createTimetablePage({
|
|
1524
|
+
timetablePageId: timetable.timetable_id,
|
|
1525
|
+
timetables: [timetable],
|
|
1526
|
+
config
|
|
1527
|
+
})
|
|
1460
1528
|
);
|
|
1461
1529
|
}
|
|
1462
|
-
const routes = getRoutes();
|
|
1463
1530
|
return timetablePages.map((timetablePage) => {
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
);
|
|
1474
|
-
}
|
|
1475
|
-
return timetablePage;
|
|
1531
|
+
return {
|
|
1532
|
+
...timetablePage,
|
|
1533
|
+
timetables: sortBy(
|
|
1534
|
+
formattedTimetables.filter(
|
|
1535
|
+
(timetable) => timetable.timetable_page_id === timetablePage.timetable_page_id
|
|
1536
|
+
),
|
|
1537
|
+
"timetable_sequence"
|
|
1538
|
+
)
|
|
1539
|
+
};
|
|
1476
1540
|
});
|
|
1477
1541
|
}
|
|
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
|
-
}
|
|
1542
|
+
var getDataForTimetablePageById = (timetablePageId) => {
|
|
1514
1543
|
let calendarCode;
|
|
1515
1544
|
let calendars;
|
|
1516
1545
|
let calendarDates;
|
|
1517
1546
|
let serviceId;
|
|
1518
1547
|
let directionId = "";
|
|
1519
|
-
const parts = timetablePageId
|
|
1548
|
+
const parts = timetablePageId?.split("|") ?? [];
|
|
1520
1549
|
if (parts.length > 2) {
|
|
1521
1550
|
directionId = Number.parseInt(parts.pop(), 10);
|
|
1522
1551
|
calendarCode = parts.pop();
|
|
@@ -1535,13 +1564,13 @@ var getTimetablePageById = (timetablePageId, config) => {
|
|
|
1535
1564
|
},
|
|
1536
1565
|
["trip_headsign", "direction_id"]
|
|
1537
1566
|
);
|
|
1538
|
-
const
|
|
1539
|
-
if (
|
|
1567
|
+
const uniqueTripDirections = uniqBy(trips, (trip) => trip.direction_id);
|
|
1568
|
+
if (uniqueTripDirections.length === 0) {
|
|
1540
1569
|
throw new Error(
|
|
1541
1570
|
`No trips found for timetable_page_id=${timetablePageId} route_id=${routeId} direction_id=${directionId}`
|
|
1542
1571
|
);
|
|
1543
1572
|
}
|
|
1544
|
-
if (/^[01]*$/.test(calendarCode)) {
|
|
1573
|
+
if (/^[01]*$/.test(calendarCode ?? "")) {
|
|
1545
1574
|
calendars = getCalendars({
|
|
1546
1575
|
...calendarCodeToCalendar(calendarCode)
|
|
1547
1576
|
});
|
|
@@ -1552,13 +1581,129 @@ var getTimetablePageById = (timetablePageId, config) => {
|
|
|
1552
1581
|
service_id: serviceId
|
|
1553
1582
|
});
|
|
1554
1583
|
}
|
|
1555
|
-
return
|
|
1556
|
-
routes[0],
|
|
1557
|
-
directions[0],
|
|
1584
|
+
return {
|
|
1558
1585
|
calendars,
|
|
1559
1586
|
calendarDates,
|
|
1587
|
+
route: routes[0],
|
|
1588
|
+
directionId: uniqueTripDirections[0].direction_id,
|
|
1589
|
+
tripHeadsign: uniqueTripDirections[0].trip_headsign
|
|
1590
|
+
};
|
|
1591
|
+
};
|
|
1592
|
+
var getTimetablePageById = (timetablePageId, config) => {
|
|
1593
|
+
const timetablePages = getTimetablePages({
|
|
1594
|
+
timetable_page_id: timetablePageId
|
|
1595
|
+
});
|
|
1596
|
+
const timetables = mergeTimetablesWithSameId(getTimetables());
|
|
1597
|
+
if (timetablePages.length > 1) {
|
|
1598
|
+
throw new Error(
|
|
1599
|
+
`Multiple timetable_pages found for timetable_page_id=${timetablePageId}`
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1602
|
+
if (timetablePages.length === 1) {
|
|
1603
|
+
const timetablePage = timetablePages[0];
|
|
1604
|
+
timetablePage.timetables = sortBy(
|
|
1605
|
+
timetables.filter(
|
|
1606
|
+
(timetable2) => timetable2.timetable_page_id === timetablePageId
|
|
1607
|
+
),
|
|
1608
|
+
"timetable_sequence"
|
|
1609
|
+
);
|
|
1610
|
+
for (const timetable2 of timetablePage.timetables) {
|
|
1611
|
+
timetable2.routes = getRoutes({
|
|
1612
|
+
route_id: timetable2.route_ids
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
return timetablePage;
|
|
1616
|
+
}
|
|
1617
|
+
if (timetables.length > 0) {
|
|
1618
|
+
const timetablePageTimetables = timetables.filter(
|
|
1619
|
+
(timetable2) => timetable2.timetable_id === timetablePageId
|
|
1620
|
+
);
|
|
1621
|
+
if (timetablePageTimetables.length === 0) {
|
|
1622
|
+
throw new Error(
|
|
1623
|
+
`No timetable found for timetable_page_id=${timetablePageId}`
|
|
1624
|
+
);
|
|
1625
|
+
}
|
|
1626
|
+
return createTimetablePage({
|
|
1627
|
+
timetablePageId,
|
|
1628
|
+
timetables: [timetablePageTimetables[0]],
|
|
1629
|
+
config
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
if (timetablePageId.startsWith("route_")) {
|
|
1633
|
+
const routes = getRoutes({
|
|
1634
|
+
route_short_name: timetablePageId.split("_")[1]
|
|
1635
|
+
});
|
|
1636
|
+
if (routes.length === 0) {
|
|
1637
|
+
throw new Error(
|
|
1638
|
+
`No route found for timetable_page_id=${timetablePageId}`
|
|
1639
|
+
);
|
|
1640
|
+
}
|
|
1641
|
+
const { calendars: calendars2, calendarDates: calendarDates2 } = getCalendarsFromConfig(config);
|
|
1642
|
+
const trips = getTrips(
|
|
1643
|
+
{
|
|
1644
|
+
route_id: routes[0].route_id
|
|
1645
|
+
},
|
|
1646
|
+
["trip_headsign", "direction_id", "trip_id", "service_id"]
|
|
1647
|
+
);
|
|
1648
|
+
const uniqueTripDirections = orderBy(
|
|
1649
|
+
uniqBy(trips, (trip) => trip.direction_id),
|
|
1650
|
+
"direction_id"
|
|
1651
|
+
);
|
|
1652
|
+
const sortedCalendars = orderBy(calendars2, calendarToCalendarCode, "desc");
|
|
1653
|
+
const calendarGroups = groupBy(sortedCalendars, calendarToCalendarCode);
|
|
1654
|
+
const calendarDateGroups = groupBy(calendarDates2, "service_id");
|
|
1655
|
+
const timetables2 = [];
|
|
1656
|
+
for (const uniqueTripDirection of uniqueTripDirections) {
|
|
1657
|
+
for (const calendars3 of Object.values(calendarGroups)) {
|
|
1658
|
+
const tripsForCalendars = trips.filter(
|
|
1659
|
+
(trip) => some(calendars3, { service_id: trip.service_id })
|
|
1660
|
+
);
|
|
1661
|
+
if (tripsForCalendars.length > 0) {
|
|
1662
|
+
timetables2.push(
|
|
1663
|
+
createTimetable({
|
|
1664
|
+
route: routes[0],
|
|
1665
|
+
directionId: uniqueTripDirection.direction_id,
|
|
1666
|
+
tripHeadsign: uniqueTripDirection.trip_headsign,
|
|
1667
|
+
calendars: calendars3
|
|
1668
|
+
})
|
|
1669
|
+
);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
for (const calendarDates3 of Object.values(calendarDateGroups)) {
|
|
1673
|
+
const tripsForCalendarDates = trips.filter(
|
|
1674
|
+
(trip) => some(calendarDates3, { service_id: trip.service_id })
|
|
1675
|
+
);
|
|
1676
|
+
if (tripsForCalendarDates.length > 0) {
|
|
1677
|
+
timetables2.push(
|
|
1678
|
+
createTimetable({
|
|
1679
|
+
route: routes[0],
|
|
1680
|
+
directionId: uniqueTripDirection.direction_id,
|
|
1681
|
+
tripHeadsign: uniqueTripDirection.trip_headsign,
|
|
1682
|
+
calendarDates: calendarDates3
|
|
1683
|
+
})
|
|
1684
|
+
);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
return createTimetablePage({
|
|
1689
|
+
timetablePageId,
|
|
1690
|
+
timetables: timetables2,
|
|
1691
|
+
config
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
const { calendars, calendarDates, route, directionId, tripHeadsign } = getDataForTimetablePageById(timetablePageId);
|
|
1695
|
+
const timetable = createTimetable({
|
|
1696
|
+
route,
|
|
1697
|
+
directionId,
|
|
1698
|
+
tripHeadsign,
|
|
1699
|
+
calendars,
|
|
1700
|
+
calendarDates
|
|
1701
|
+
});
|
|
1702
|
+
return createTimetablePage({
|
|
1703
|
+
timetablePageId,
|
|
1704
|
+
timetables: [timetable],
|
|
1560
1705
|
config
|
|
1561
|
-
);
|
|
1706
|
+
});
|
|
1562
1707
|
};
|
|
1563
1708
|
function setDefaultConfig(initialConfig) {
|
|
1564
1709
|
const defaults = {
|
|
@@ -1579,6 +1724,7 @@ function setDefaultConfig(initialConfig) {
|
|
|
1579
1724
|
defaultOrientation: "vertical",
|
|
1580
1725
|
interpolatedStopSymbol: "\u2022",
|
|
1581
1726
|
interpolatedStopText: "Estimated time of arrival",
|
|
1727
|
+
groupTimetablesIntoPages: true,
|
|
1582
1728
|
gtfsToHtmlVersion: version,
|
|
1583
1729
|
linkStopUrls: false,
|
|
1584
1730
|
mapStyleUrl: "https://tiles.openfreemap.org/styles/positron",
|
|
@@ -1635,12 +1781,6 @@ function getFormattedTimetablePage(timetablePageId, config) {
|
|
|
1635
1781
|
timetablePageId,
|
|
1636
1782
|
config
|
|
1637
1783
|
);
|
|
1638
|
-
const timetableRoutes = getRoutes(
|
|
1639
|
-
{
|
|
1640
|
-
route_id: timetablePage.route_ids
|
|
1641
|
-
},
|
|
1642
|
-
["agency_id"]
|
|
1643
|
-
);
|
|
1644
1784
|
const consolidatedTimetables = formatTimetables(
|
|
1645
1785
|
timetablePage.timetables,
|
|
1646
1786
|
config
|
|
@@ -1656,7 +1796,7 @@ function getFormattedTimetablePage(timetablePageId, config) {
|
|
|
1656
1796
|
}
|
|
1657
1797
|
}
|
|
1658
1798
|
const uniqueRoutes = uniqBy(
|
|
1659
|
-
|
|
1799
|
+
flatMap(consolidatedTimetables, (timetable) => timetable.routes),
|
|
1660
1800
|
"route_id"
|
|
1661
1801
|
);
|
|
1662
1802
|
const formattedTimetablePage = {
|
|
@@ -1667,7 +1807,7 @@ function getFormattedTimetablePage(timetablePageId, config) {
|
|
|
1667
1807
|
consolidatedTimetables.map((timetable) => timetable.dayList)
|
|
1668
1808
|
),
|
|
1669
1809
|
route_ids: uniqueRoutes.map((route) => route.route_id),
|
|
1670
|
-
agency_ids: uniq(compact(
|
|
1810
|
+
agency_ids: uniq(compact(uniqueRoutes.map((route) => route.agency_id))),
|
|
1671
1811
|
filename: timetablePage.filename ?? `${timetablePage.timetable_page_id}.html`,
|
|
1672
1812
|
timetable_page_label: timetablePage.timetable_page_label ?? formatListForDisplay(uniqueRoutes.map((route) => formatRouteName(route)))
|
|
1673
1813
|
};
|
|
@@ -1925,12 +2065,14 @@ function formatFrequency(frequency, config) {
|
|
|
1925
2065
|
frequency.headway_min = Math.round(headway.asMinutes());
|
|
1926
2066
|
return frequency;
|
|
1927
2067
|
}
|
|
1928
|
-
function formatTimetableId(
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
2068
|
+
function formatTimetableId({
|
|
2069
|
+
routeIds,
|
|
2070
|
+
directionId,
|
|
2071
|
+
days: days2
|
|
2072
|
+
}) {
|
|
2073
|
+
let timetableId = `${routeIds.join("_")}|${calendarToCalendarCode(days2)}`;
|
|
2074
|
+
if (!isNullOrEmpty(directionId)) {
|
|
2075
|
+
timetableId += `|${directionId}`;
|
|
1934
2076
|
}
|
|
1935
2077
|
return timetableId;
|
|
1936
2078
|
}
|
|
@@ -2083,10 +2225,18 @@ function formatTimetableLabel(timetable) {
|
|
|
2083
2225
|
return timetableLabel;
|
|
2084
2226
|
}
|
|
2085
2227
|
var formatRouteName = (route) => {
|
|
2086
|
-
if (route.route_long_name
|
|
2087
|
-
return `Route ${route.
|
|
2228
|
+
if (route.route_long_name) {
|
|
2229
|
+
return `Route ${route.route_long_name}`;
|
|
2088
2230
|
}
|
|
2089
|
-
return route.
|
|
2231
|
+
return route.route_short_name ?? "Unknown";
|
|
2232
|
+
};
|
|
2233
|
+
var formatRouteNameForFilename = (route) => {
|
|
2234
|
+
if (route.route_short_name) {
|
|
2235
|
+
return route.route_short_name.replace(/\s/g, "-");
|
|
2236
|
+
} else if (route.route_long_name) {
|
|
2237
|
+
return route.route_long_name.replace(/\s/g, "-");
|
|
2238
|
+
}
|
|
2239
|
+
return "Unknown";
|
|
2090
2240
|
};
|
|
2091
2241
|
var formatListForDisplay = (list) => {
|
|
2092
2242
|
return new Intl.ListFormat("en-US", {
|
|
@@ -2109,6 +2259,7 @@ function mergeTimetablesWithSameId(timetables) {
|
|
|
2109
2259
|
}
|
|
2110
2260
|
|
|
2111
2261
|
// src/lib/file-utils.ts
|
|
2262
|
+
var homeDirectory = homedir();
|
|
2112
2263
|
function getPathToViewsFolder(config) {
|
|
2113
2264
|
if (config.templatePath) {
|
|
2114
2265
|
return untildify(config.templatePath);
|
|
@@ -2165,18 +2316,32 @@ async function copyStaticAssets(config, outputPath) {
|
|
|
2165
2316
|
}
|
|
2166
2317
|
if (config.hasGtfsRealtimeVehiclePositions || config.hasGtfsRealtimeTripUpdates || config.hasGtfsRealtimeAlerts) {
|
|
2167
2318
|
await copyFile(
|
|
2168
|
-
|
|
2319
|
+
join(
|
|
2320
|
+
dirname(findPackageJSON("pbf", import.meta.url)),
|
|
2321
|
+
"dist/pbf.js"
|
|
2322
|
+
),
|
|
2169
2323
|
join(outputPath, "js/pbf.js")
|
|
2170
2324
|
);
|
|
2171
2325
|
await copyFile(
|
|
2172
|
-
|
|
2326
|
+
join(
|
|
2327
|
+
dirname(
|
|
2328
|
+
findPackageJSON(
|
|
2329
|
+
"gtfs-realtime-pbf-js-module",
|
|
2330
|
+
import.meta.url
|
|
2331
|
+
)
|
|
2332
|
+
),
|
|
2333
|
+
"gtfs-realtime.browser.proto.js"
|
|
2334
|
+
),
|
|
2173
2335
|
join(outputPath, "js/gtfs-realtime.browser.proto.js")
|
|
2174
2336
|
);
|
|
2175
2337
|
}
|
|
2176
2338
|
if (config.hasGtfsRealtimeAlerts) {
|
|
2177
2339
|
await copyFile(
|
|
2178
|
-
|
|
2179
|
-
|
|
2340
|
+
join(
|
|
2341
|
+
dirname(findPackageJSON("anchorme", import.meta.url)),
|
|
2342
|
+
"dist/browser/anchorme.min.js"
|
|
2343
|
+
),
|
|
2344
|
+
join(outputPath, "js/anchorme.min.js")
|
|
2180
2345
|
);
|
|
2181
2346
|
}
|
|
2182
2347
|
}
|
|
@@ -2193,15 +2358,34 @@ function zipFolder(outputPath) {
|
|
|
2193
2358
|
archive.finalize();
|
|
2194
2359
|
});
|
|
2195
2360
|
}
|
|
2196
|
-
function
|
|
2197
|
-
|
|
2361
|
+
function generateTimetablePageFileName(timetablePage, config) {
|
|
2362
|
+
if (timetablePage.filename) {
|
|
2363
|
+
return sanitize(timetablePage.filename);
|
|
2364
|
+
}
|
|
2365
|
+
if (config.groupTimetablesIntoPages === true && uniqBy2(timetablePage.timetables, "route_id").length === 1) {
|
|
2366
|
+
const route = timetablePage.timetables[0].routes[0];
|
|
2367
|
+
return sanitize(`${formatRouteNameForFilename(route).toLowerCase()}.html`);
|
|
2368
|
+
}
|
|
2369
|
+
const timetable = timetablePage.timetables[0];
|
|
2370
|
+
let filename = timetable.timetable_id ?? "";
|
|
2371
|
+
for (const route of timetable.routes) {
|
|
2372
|
+
filename += `_${formatRouteNameForFilename(route)}`;
|
|
2373
|
+
}
|
|
2374
|
+
if (!isNullOrEmpty(timetable.direction_id)) {
|
|
2375
|
+
filename += `_${timetable.direction_id}`;
|
|
2376
|
+
}
|
|
2377
|
+
filename += `_${formatDays(timetable, config).replace(/\s/g, "")}.html`;
|
|
2378
|
+
return sanitize(filename.toLowerCase());
|
|
2379
|
+
}
|
|
2380
|
+
function generateCSVFileName(timetable, config) {
|
|
2381
|
+
let filename = timetable.timetable_id ?? "";
|
|
2198
2382
|
for (const route of timetable.routes) {
|
|
2199
|
-
filename +=
|
|
2383
|
+
filename += `_${formatRouteNameForFilename(route)}`;
|
|
2200
2384
|
}
|
|
2201
2385
|
if (!isNullOrEmpty(timetable.direction_id)) {
|
|
2202
2386
|
filename += `_${timetable.direction_id}`;
|
|
2203
2387
|
}
|
|
2204
|
-
filename += `_${formatDays(timetable, config).replace(/\s/g, "")}
|
|
2388
|
+
filename += `_${formatDays(timetable, config).replace(/\s/g, "")}.csv`;
|
|
2205
2389
|
return sanitize(filename).toLowerCase();
|
|
2206
2390
|
}
|
|
2207
2391
|
function generateFolderName(timetablePage) {
|
|
@@ -2241,16 +2425,18 @@ async function renderPdf(htmlPath) {
|
|
|
2241
2425
|
});
|
|
2242
2426
|
await browser.close();
|
|
2243
2427
|
}
|
|
2428
|
+
function untildify(pathWithTilde) {
|
|
2429
|
+
return homeDirectory ? pathWithTilde.replace(/^~(?=$|\/|\\)/, homeDirectory) : pathWithTilde;
|
|
2430
|
+
}
|
|
2244
2431
|
|
|
2245
2432
|
// src/lib/gtfs-to-html.ts
|
|
2246
2433
|
var gtfsToHtml = async (initialConfig) => {
|
|
2247
2434
|
const config = setDefaultConfig(initialConfig);
|
|
2248
|
-
const
|
|
2435
|
+
const startTime = process.hrtime.bigint();
|
|
2249
2436
|
const agencyKey = config.agencies.map(
|
|
2250
2437
|
(agency) => agency.agencyKey ?? agency.agency_key ?? "unknown"
|
|
2251
2438
|
).join("-");
|
|
2252
|
-
const outputPath = config.outputPath ?
|
|
2253
|
-
timer.start();
|
|
2439
|
+
const outputPath = config.outputPath ? untildify(config.outputPath) : path.join(process.cwd(), "html", sanitize2(agencyKey));
|
|
2254
2440
|
await prepDirectory(outputPath, config);
|
|
2255
2441
|
try {
|
|
2256
2442
|
openDb2(config);
|
|
@@ -2296,9 +2482,11 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2296
2482
|
config
|
|
2297
2483
|
);
|
|
2298
2484
|
for (const timetable of timetablePage.timetables) {
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2485
|
+
if (timetable.warnings) {
|
|
2486
|
+
for (const warning of timetable.warnings) {
|
|
2487
|
+
stats.warnings.push(warning);
|
|
2488
|
+
bar?.interrupt(warning);
|
|
2489
|
+
}
|
|
2302
2490
|
}
|
|
2303
2491
|
}
|
|
2304
2492
|
if (timetablePage.consolidatedTimetables.length === 0) {
|
|
@@ -2321,7 +2509,7 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2321
2509
|
const csvPath = path.join(
|
|
2322
2510
|
outputPath,
|
|
2323
2511
|
datePath,
|
|
2324
|
-
|
|
2512
|
+
generateCSVFileName(timetable, config)
|
|
2325
2513
|
);
|
|
2326
2514
|
await writeFile(csvPath, csv);
|
|
2327
2515
|
}
|
|
@@ -2367,11 +2555,11 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2367
2555
|
`${agencyKey}: ${config.outputFormat.toUpperCase()} timetables created at ${fullOutputPath}`
|
|
2368
2556
|
);
|
|
2369
2557
|
logStats(config)(stats);
|
|
2370
|
-
const
|
|
2558
|
+
const endTime = process.hrtime.bigint();
|
|
2559
|
+
const elapsedSeconds = Number(endTime - startTime) / 1e9;
|
|
2371
2560
|
log(config)(
|
|
2372
|
-
`${agencyKey}: ${config.outputFormat.toUpperCase()} timetable generation required ${
|
|
2561
|
+
`${agencyKey}: ${config.outputFormat.toUpperCase()} timetable generation required ${elapsedSeconds.toFixed(1)} seconds`
|
|
2373
2562
|
);
|
|
2374
|
-
timer.stop();
|
|
2375
2563
|
return fullOutputPath;
|
|
2376
2564
|
};
|
|
2377
2565
|
var gtfs_to_html_default = gtfsToHtml;
|