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/bin/gtfs-to-html.js
CHANGED
|
@@ -23,14 +23,16 @@ import {
|
|
|
23
23
|
readFile,
|
|
24
24
|
rm
|
|
25
25
|
} from "fs/promises";
|
|
26
|
+
import { homedir } from "os";
|
|
27
|
+
import { findPackageJSON } from "module";
|
|
26
28
|
import * as _ from "lodash-es";
|
|
29
|
+
import { uniqBy as uniqBy2 } from "lodash-es";
|
|
27
30
|
import archiver from "archiver";
|
|
28
31
|
import beautify from "js-beautify";
|
|
29
32
|
import sanitizeHtml from "sanitize-html";
|
|
30
33
|
import { renderFile } from "pug";
|
|
31
34
|
import puppeteer from "puppeteer";
|
|
32
35
|
import sanitize from "sanitize-filename";
|
|
33
|
-
import untildify from "untildify";
|
|
34
36
|
import { marked } from "marked";
|
|
35
37
|
|
|
36
38
|
// src/lib/formatters.ts
|
|
@@ -59,16 +61,7 @@ function fromGTFSTime(timeString) {
|
|
|
59
61
|
function toGTFSTime(time) {
|
|
60
62
|
return time.format("HH:mm:ss");
|
|
61
63
|
}
|
|
62
|
-
function fromGTFSDate(gtfsDate) {
|
|
63
|
-
return moment(gtfsDate, "YYYYMMDD");
|
|
64
|
-
}
|
|
65
|
-
function toGTFSDate(date) {
|
|
66
|
-
return moment(date).format("YYYYMMDD");
|
|
67
|
-
}
|
|
68
64
|
function calendarToCalendarCode(c) {
|
|
69
|
-
if (c.service_id) {
|
|
70
|
-
return c.service_id;
|
|
71
|
-
}
|
|
72
65
|
return `${c.monday}${c.tuesday}${c.wednesday}${c.thursday}${c.friday}${c.saturday}${c.sunday}`;
|
|
73
66
|
}
|
|
74
67
|
function calendarCodeToCalendar(code) {
|
|
@@ -109,13 +102,13 @@ import {
|
|
|
109
102
|
find,
|
|
110
103
|
findLast,
|
|
111
104
|
first,
|
|
112
|
-
flatMap
|
|
113
|
-
flattenDeep,
|
|
105
|
+
flatMap,
|
|
114
106
|
flow,
|
|
115
107
|
groupBy,
|
|
116
108
|
head,
|
|
117
109
|
last,
|
|
118
110
|
maxBy,
|
|
111
|
+
orderBy,
|
|
119
112
|
partialRight,
|
|
120
113
|
reduce,
|
|
121
114
|
size,
|
|
@@ -149,7 +142,6 @@ import toposort from "toposort";
|
|
|
149
142
|
|
|
150
143
|
// src/lib/geojson-utils.ts
|
|
151
144
|
import { getShapesAsGeoJSON, getStopsAsGeoJSON } from "gtfs";
|
|
152
|
-
import { flatMap } from "lodash-es";
|
|
153
145
|
import simplify from "@turf/simplify";
|
|
154
146
|
import { featureCollection, round } from "@turf/helpers";
|
|
155
147
|
|
|
@@ -311,7 +303,7 @@ function progressBar(formatString, barTotal, config) {
|
|
|
311
303
|
}
|
|
312
304
|
|
|
313
305
|
// src/lib/geojson-utils.ts
|
|
314
|
-
var mergeGeojson = (...geojsons) => featureCollection(flatMap(
|
|
306
|
+
var mergeGeojson = (...geojsons) => featureCollection(geojsons.flatMap((geojson) => geojson.features));
|
|
315
307
|
var truncateGeoJSONDecimals = (geojson, config) => {
|
|
316
308
|
for (const feature of geojson.features) {
|
|
317
309
|
if (feature.geometry.coordinates) {
|
|
@@ -502,7 +494,7 @@ function formatTripNameForCSV(trip, timetable) {
|
|
|
502
494
|
// package.json
|
|
503
495
|
var package_default = {
|
|
504
496
|
name: "gtfs-to-html",
|
|
505
|
-
version: "2.
|
|
497
|
+
version: "2.11.1",
|
|
506
498
|
private: false,
|
|
507
499
|
description: "Build human readable transit timetables as HTML, PDF or CSV from GTFS",
|
|
508
500
|
keywords: [
|
|
@@ -555,24 +547,22 @@ var package_default = {
|
|
|
555
547
|
"cli-table": "^0.3.11",
|
|
556
548
|
"csv-stringify": "^6.6.0",
|
|
557
549
|
express: "^5.1.0",
|
|
558
|
-
gtfs: "^4.
|
|
550
|
+
gtfs: "^4.18.0",
|
|
559
551
|
"gtfs-realtime-pbf-js-module": "^1.0.0",
|
|
560
552
|
"js-beautify": "^1.15.4",
|
|
561
553
|
"lodash-es": "^4.17.21",
|
|
562
|
-
marked: "^16.
|
|
554
|
+
marked: "^16.3.0",
|
|
563
555
|
moment: "^2.30.1",
|
|
564
556
|
pbf: "^4.0.1",
|
|
565
557
|
"pretty-error": "^4.0.0",
|
|
566
558
|
pug: "^3.0.3",
|
|
567
|
-
puppeteer: "^24.
|
|
559
|
+
puppeteer: "^24.21.0",
|
|
568
560
|
"sanitize-filename": "^1.6.3",
|
|
569
561
|
"sanitize-html": "^2.17.0",
|
|
570
562
|
sqlstring: "^2.3.3",
|
|
571
|
-
"timer-machine": "^1.1.0",
|
|
572
563
|
toposort: "^2.0.2",
|
|
573
|
-
untildify: "^5.0.0",
|
|
574
564
|
yargs: "^18.0.0",
|
|
575
|
-
yoctocolors: "^2.1.
|
|
565
|
+
yoctocolors: "^2.1.2"
|
|
576
566
|
},
|
|
577
567
|
devDependencies: {
|
|
578
568
|
"@types/archiver": "^6.0.3",
|
|
@@ -584,16 +574,17 @@ var package_default = {
|
|
|
584
574
|
"@types/node": "^22",
|
|
585
575
|
"@types/pug": "^2.0.10",
|
|
586
576
|
"@types/sanitize-html": "^2.16.0",
|
|
587
|
-
"@types/
|
|
577
|
+
"@types/sqlstring": "^2.3.2",
|
|
578
|
+
"@types/toposort": "^2.0.7",
|
|
588
579
|
"@types/yargs": "^17.0.33",
|
|
589
580
|
husky: "^9.1.7",
|
|
590
|
-
"lint-staged": "^16.1.
|
|
581
|
+
"lint-staged": "^16.1.6",
|
|
591
582
|
prettier: "^3.6.2",
|
|
592
583
|
tsup: "^8.5.0",
|
|
593
584
|
typescript: "^5.9.2"
|
|
594
585
|
},
|
|
595
586
|
engines: {
|
|
596
|
-
node: ">=
|
|
587
|
+
node: ">= 22"
|
|
597
588
|
},
|
|
598
589
|
"release-it": {
|
|
599
590
|
github: {
|
|
@@ -643,7 +634,7 @@ var findCommonStopId = (trips, config) => {
|
|
|
643
634
|
return null;
|
|
644
635
|
}
|
|
645
636
|
const commonStoptime = longestTripStoptimes.find((stoptime, idx) => {
|
|
646
|
-
if (idx === 0 && stoptime.stop_id === last(longestTripStoptimes)
|
|
637
|
+
if (idx === 0 && stoptime.stop_id === last(longestTripStoptimes)?.stop_id) {
|
|
647
638
|
return false;
|
|
648
639
|
}
|
|
649
640
|
if (isNullOrEmpty(stoptime.arrival_time)) {
|
|
@@ -696,11 +687,7 @@ var sortTrips = (trips, config) => {
|
|
|
696
687
|
trip.stoptimes[trip.stoptimes.length - 1].departure_time
|
|
697
688
|
);
|
|
698
689
|
}
|
|
699
|
-
sortedTrips = sortBy(
|
|
700
|
-
trips,
|
|
701
|
-
["firstStoptime", "lastStoptime"],
|
|
702
|
-
["asc", "asc"]
|
|
703
|
-
);
|
|
690
|
+
sortedTrips = sortBy(trips, ["firstStoptime", "lastStoptime"]);
|
|
704
691
|
} else if (config.sortingAlgorithm === "end") {
|
|
705
692
|
for (const trip of trips) {
|
|
706
693
|
if (trip.stoptimes.length === 0) {
|
|
@@ -711,11 +698,7 @@ var sortTrips = (trips, config) => {
|
|
|
711
698
|
trip.stoptimes[trip.stoptimes.length - 1].departure_time
|
|
712
699
|
);
|
|
713
700
|
}
|
|
714
|
-
sortedTrips = sortBy(
|
|
715
|
-
trips,
|
|
716
|
-
["lastStoptime", "firstStoptime"],
|
|
717
|
-
["asc", "asc"]
|
|
718
|
-
);
|
|
701
|
+
sortedTrips = sortBy(trips, ["lastStoptime", "firstStoptime"]);
|
|
719
702
|
} else if (config.sortingAlgorithm === "first") {
|
|
720
703
|
const longestTripStoptimes = getLongestTripStoptimes(trips, config);
|
|
721
704
|
const firstStopId = first(longestTripStoptimes).stop_id;
|
|
@@ -742,8 +725,8 @@ var getCalendarDatesForTimetable = (timetable, config) => {
|
|
|
742
725
|
[],
|
|
743
726
|
[["date", "ASC"]]
|
|
744
727
|
);
|
|
745
|
-
const start =
|
|
746
|
-
const end =
|
|
728
|
+
const start = moment2(timetable.start_date, "YYYYMMDD");
|
|
729
|
+
const end = moment2(timetable.end_date, "YYYYMMDD");
|
|
747
730
|
const excludedDates = /* @__PURE__ */ new Set();
|
|
748
731
|
const includedDates = /* @__PURE__ */ new Set();
|
|
749
732
|
for (const calendarDate of calendarDates) {
|
|
@@ -868,28 +851,70 @@ var getTimetableNotesForTimetable = (timetable, config) => {
|
|
|
868
851
|
}));
|
|
869
852
|
return sortBy(formattedNotes, "symbol");
|
|
870
853
|
};
|
|
871
|
-
var
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
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);
|
|
878
873
|
return {
|
|
879
|
-
|
|
880
|
-
timetable_page_label: timetable.timetable_label,
|
|
881
|
-
timetables: [timetable],
|
|
874
|
+
...timetablePage,
|
|
882
875
|
filename
|
|
883
876
|
};
|
|
884
877
|
};
|
|
885
|
-
var
|
|
886
|
-
|
|
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,
|
|
887
912
|
route_ids: [route.route_id],
|
|
888
|
-
direction_id:
|
|
889
|
-
direction_name:
|
|
913
|
+
direction_id: directionId === null ? null : directionId,
|
|
914
|
+
direction_name: tripHeadsign === null ? null : tripHeadsign,
|
|
890
915
|
routes: [route],
|
|
891
916
|
include_exceptions: calendarDates && calendarDates.length > 0 ? 1 : 0,
|
|
892
|
-
|
|
917
|
+
service_ids: serviceIds,
|
|
893
918
|
service_notes: null,
|
|
894
919
|
timetable_label: null,
|
|
895
920
|
start_time: null,
|
|
@@ -897,88 +922,83 @@ var convertRouteToTimetablePage = (route, direction, calendars, calendarDates, c
|
|
|
897
922
|
orientation: null,
|
|
898
923
|
timetable_sequence: null,
|
|
899
924
|
show_trip_continuation: null,
|
|
900
|
-
start_date:
|
|
901
|
-
end_date:
|
|
925
|
+
start_date: startDate,
|
|
926
|
+
end_date: endDate,
|
|
927
|
+
...days2
|
|
902
928
|
};
|
|
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
929
|
};
|
|
917
930
|
var convertRoutesToTimetablePages = (config) => {
|
|
918
|
-
const db = openDb(config);
|
|
919
931
|
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) => {
|
|
932
|
+
const timetablePages = [];
|
|
933
|
+
const { calendars, calendarDates } = getCalendarsFromConfig(config);
|
|
934
|
+
for (const route of routes) {
|
|
941
935
|
const trips = getTrips(
|
|
942
936
|
{
|
|
943
937
|
route_id: route.route_id
|
|
944
938
|
},
|
|
945
939
|
["trip_headsign", "direction_id", "trip_id", "service_id"]
|
|
946
940
|
);
|
|
947
|
-
const
|
|
948
|
-
|
|
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);
|
|
949
947
|
const calendarDateGroups = groupBy(calendarDates, "service_id");
|
|
950
|
-
|
|
951
|
-
|
|
948
|
+
const timetables = [];
|
|
949
|
+
for (const uniqueTripDirection of uniqueTripDirections) {
|
|
950
|
+
for (const calendars2 of Object.values(calendarGroups)) {
|
|
952
951
|
const tripsForCalendars = trips.filter(
|
|
953
952
|
(trip) => some(calendars2, { service_id: trip.service_id })
|
|
954
953
|
);
|
|
955
954
|
if (tripsForCalendars.length > 0) {
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
955
|
+
timetables.push(
|
|
956
|
+
createTimetable({
|
|
957
|
+
route,
|
|
958
|
+
directionId: uniqueTripDirection.direction_id,
|
|
959
|
+
tripHeadsign: uniqueTripDirection.trip_headsign,
|
|
960
|
+
calendars: calendars2
|
|
961
|
+
})
|
|
962
962
|
);
|
|
963
963
|
}
|
|
964
|
-
}
|
|
965
|
-
Object.values(calendarDateGroups)
|
|
964
|
+
}
|
|
965
|
+
for (const calendarDates2 of Object.values(calendarDateGroups)) {
|
|
966
966
|
const tripsForCalendarDates = trips.filter(
|
|
967
967
|
(trip) => some(calendarDates2, { service_id: trip.service_id })
|
|
968
968
|
);
|
|
969
969
|
if (tripsForCalendarDates.length > 0) {
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
970
|
+
timetables.push(
|
|
971
|
+
createTimetable({
|
|
972
|
+
route,
|
|
973
|
+
directionId: uniqueTripDirection.direction_id,
|
|
974
|
+
tripHeadsign: uniqueTripDirection.trip_headsign,
|
|
975
|
+
calendarDates: calendarDates2
|
|
976
|
+
})
|
|
976
977
|
);
|
|
977
978
|
}
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
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;
|
|
982
1002
|
};
|
|
983
1003
|
var generateTripsByFrequencies = (trip, frequencies, config) => {
|
|
984
1004
|
const formattedFrequencies = frequencies.map(
|
|
@@ -1001,7 +1021,7 @@ var generateTripsByFrequencies = (trip, frequencies, config) => {
|
|
|
1001
1021
|
return trips;
|
|
1002
1022
|
};
|
|
1003
1023
|
var duplicateStopsForDifferentArrivalDeparture = (stopIds, timetable, config) => {
|
|
1004
|
-
if (config.showArrivalOnDifference === null) {
|
|
1024
|
+
if (config.showArrivalOnDifference === null || config.showArrivalOnDifference === void 0) {
|
|
1005
1025
|
return stopIds;
|
|
1006
1026
|
}
|
|
1007
1027
|
for (const trip of timetable.orderedTrips) {
|
|
@@ -1123,7 +1143,7 @@ var getStopsForTimetable = (timetable, config) => {
|
|
|
1123
1143
|
}
|
|
1124
1144
|
return stop;
|
|
1125
1145
|
});
|
|
1126
|
-
if (
|
|
1146
|
+
if (config.showStopCity) {
|
|
1127
1147
|
const stopAttributes = getStopAttributes({
|
|
1128
1148
|
stop_id: orderedStopIds
|
|
1129
1149
|
});
|
|
@@ -1138,6 +1158,39 @@ var getStopsForTimetable = (timetable, config) => {
|
|
|
1138
1158
|
}
|
|
1139
1159
|
return orderedStops;
|
|
1140
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
|
+
};
|
|
1141
1194
|
var getCalendarsFromTimetable = (timetable) => {
|
|
1142
1195
|
const db = openDb();
|
|
1143
1196
|
let whereClause = "";
|
|
@@ -1287,13 +1340,17 @@ var filterTrips = (timetable) => {
|
|
|
1287
1340
|
}
|
|
1288
1341
|
trip.stoptimes = combinedStoptimes;
|
|
1289
1342
|
}
|
|
1290
|
-
const timetableStopIds = new Set(
|
|
1343
|
+
const timetableStopIds = new Set(
|
|
1344
|
+
timetable.stops.map((stop) => stop.stop_id)
|
|
1345
|
+
);
|
|
1291
1346
|
for (const trip of filteredTrips) {
|
|
1292
1347
|
trip.stoptimes = trip.stoptimes.filter(
|
|
1293
1348
|
(stoptime) => timetableStopIds.has(stoptime.stop_id)
|
|
1294
1349
|
);
|
|
1295
1350
|
}
|
|
1296
|
-
filteredTrips = filteredTrips.filter(
|
|
1351
|
+
filteredTrips = filteredTrips.filter(
|
|
1352
|
+
(trip) => trip.stoptimes.length > 1
|
|
1353
|
+
);
|
|
1297
1354
|
return filteredTrips;
|
|
1298
1355
|
};
|
|
1299
1356
|
var getTripsForTimetable = (timetable, calendars, config) => {
|
|
@@ -1444,6 +1501,15 @@ var formatTimetables = (timetables, config) => {
|
|
|
1444
1501
|
};
|
|
1445
1502
|
function getTimetablePagesForAgency(config) {
|
|
1446
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
|
+
});
|
|
1447
1513
|
if (timetables.length === 0) {
|
|
1448
1514
|
return convertRoutesToTimetablePages(config);
|
|
1449
1515
|
}
|
|
@@ -1453,68 +1519,33 @@ function getTimetablePagesForAgency(config) {
|
|
|
1453
1519
|
[["timetable_page_id", "ASC"]]
|
|
1454
1520
|
);
|
|
1455
1521
|
if (timetablePages.length === 0) {
|
|
1456
|
-
return
|
|
1457
|
-
(timetable) =>
|
|
1522
|
+
return formattedTimetables.map(
|
|
1523
|
+
(timetable) => createTimetablePage({
|
|
1524
|
+
timetablePageId: timetable.timetable_id,
|
|
1525
|
+
timetables: [timetable],
|
|
1526
|
+
config
|
|
1527
|
+
})
|
|
1458
1528
|
);
|
|
1459
1529
|
}
|
|
1460
|
-
const routes = getRoutes();
|
|
1461
1530
|
return timetablePages.map((timetablePage) => {
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
);
|
|
1472
|
-
}
|
|
1473
|
-
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
|
+
};
|
|
1474
1540
|
});
|
|
1475
1541
|
}
|
|
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
|
-
}
|
|
1542
|
+
var getDataForTimetablePageById = (timetablePageId) => {
|
|
1512
1543
|
let calendarCode;
|
|
1513
1544
|
let calendars;
|
|
1514
1545
|
let calendarDates;
|
|
1515
1546
|
let serviceId;
|
|
1516
1547
|
let directionId = "";
|
|
1517
|
-
const parts = timetablePageId
|
|
1548
|
+
const parts = timetablePageId?.split("|") ?? [];
|
|
1518
1549
|
if (parts.length > 2) {
|
|
1519
1550
|
directionId = Number.parseInt(parts.pop(), 10);
|
|
1520
1551
|
calendarCode = parts.pop();
|
|
@@ -1533,13 +1564,13 @@ var getTimetablePageById = (timetablePageId, config) => {
|
|
|
1533
1564
|
},
|
|
1534
1565
|
["trip_headsign", "direction_id"]
|
|
1535
1566
|
);
|
|
1536
|
-
const
|
|
1537
|
-
if (
|
|
1567
|
+
const uniqueTripDirections = uniqBy(trips, (trip) => trip.direction_id);
|
|
1568
|
+
if (uniqueTripDirections.length === 0) {
|
|
1538
1569
|
throw new Error(
|
|
1539
1570
|
`No trips found for timetable_page_id=${timetablePageId} route_id=${routeId} direction_id=${directionId}`
|
|
1540
1571
|
);
|
|
1541
1572
|
}
|
|
1542
|
-
if (/^[01]*$/.test(calendarCode)) {
|
|
1573
|
+
if (/^[01]*$/.test(calendarCode ?? "")) {
|
|
1543
1574
|
calendars = getCalendars({
|
|
1544
1575
|
...calendarCodeToCalendar(calendarCode)
|
|
1545
1576
|
});
|
|
@@ -1550,13 +1581,129 @@ var getTimetablePageById = (timetablePageId, config) => {
|
|
|
1550
1581
|
service_id: serviceId
|
|
1551
1582
|
});
|
|
1552
1583
|
}
|
|
1553
|
-
return
|
|
1554
|
-
routes[0],
|
|
1555
|
-
directions[0],
|
|
1584
|
+
return {
|
|
1556
1585
|
calendars,
|
|
1557
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],
|
|
1558
1705
|
config
|
|
1559
|
-
);
|
|
1706
|
+
});
|
|
1560
1707
|
};
|
|
1561
1708
|
function setDefaultConfig(initialConfig) {
|
|
1562
1709
|
const defaults = {
|
|
@@ -1577,6 +1724,7 @@ function setDefaultConfig(initialConfig) {
|
|
|
1577
1724
|
defaultOrientation: "vertical",
|
|
1578
1725
|
interpolatedStopSymbol: "\u2022",
|
|
1579
1726
|
interpolatedStopText: "Estimated time of arrival",
|
|
1727
|
+
groupTimetablesIntoPages: true,
|
|
1580
1728
|
gtfsToHtmlVersion: version,
|
|
1581
1729
|
linkStopUrls: false,
|
|
1582
1730
|
mapStyleUrl: "https://tiles.openfreemap.org/styles/positron",
|
|
@@ -1633,12 +1781,6 @@ function getFormattedTimetablePage(timetablePageId, config) {
|
|
|
1633
1781
|
timetablePageId,
|
|
1634
1782
|
config
|
|
1635
1783
|
);
|
|
1636
|
-
const timetableRoutes = getRoutes(
|
|
1637
|
-
{
|
|
1638
|
-
route_id: timetablePage.route_ids
|
|
1639
|
-
},
|
|
1640
|
-
["agency_id"]
|
|
1641
|
-
);
|
|
1642
1784
|
const consolidatedTimetables = formatTimetables(
|
|
1643
1785
|
timetablePage.timetables,
|
|
1644
1786
|
config
|
|
@@ -1654,7 +1796,7 @@ function getFormattedTimetablePage(timetablePageId, config) {
|
|
|
1654
1796
|
}
|
|
1655
1797
|
}
|
|
1656
1798
|
const uniqueRoutes = uniqBy(
|
|
1657
|
-
|
|
1799
|
+
flatMap(consolidatedTimetables, (timetable) => timetable.routes),
|
|
1658
1800
|
"route_id"
|
|
1659
1801
|
);
|
|
1660
1802
|
const formattedTimetablePage = {
|
|
@@ -1665,7 +1807,7 @@ function getFormattedTimetablePage(timetablePageId, config) {
|
|
|
1665
1807
|
consolidatedTimetables.map((timetable) => timetable.dayList)
|
|
1666
1808
|
),
|
|
1667
1809
|
route_ids: uniqueRoutes.map((route) => route.route_id),
|
|
1668
|
-
agency_ids: uniq(compact(
|
|
1810
|
+
agency_ids: uniq(compact(uniqueRoutes.map((route) => route.agency_id))),
|
|
1669
1811
|
filename: timetablePage.filename ?? `${timetablePage.timetable_page_id}.html`,
|
|
1670
1812
|
timetable_page_label: timetablePage.timetable_page_label ?? formatListForDisplay(uniqueRoutes.map((route) => formatRouteName(route)))
|
|
1671
1813
|
};
|
|
@@ -1923,12 +2065,14 @@ function formatFrequency(frequency, config) {
|
|
|
1923
2065
|
frequency.headway_min = Math.round(headway.asMinutes());
|
|
1924
2066
|
return frequency;
|
|
1925
2067
|
}
|
|
1926
|
-
function formatTimetableId(
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
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}`;
|
|
1932
2076
|
}
|
|
1933
2077
|
return timetableId;
|
|
1934
2078
|
}
|
|
@@ -2081,10 +2225,18 @@ function formatTimetableLabel(timetable) {
|
|
|
2081
2225
|
return timetableLabel;
|
|
2082
2226
|
}
|
|
2083
2227
|
var formatRouteName = (route) => {
|
|
2084
|
-
if (route.route_long_name
|
|
2085
|
-
return `Route ${route.
|
|
2228
|
+
if (route.route_long_name) {
|
|
2229
|
+
return `Route ${route.route_long_name}`;
|
|
2086
2230
|
}
|
|
2087
|
-
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";
|
|
2088
2240
|
};
|
|
2089
2241
|
var formatListForDisplay = (list) => {
|
|
2090
2242
|
return new Intl.ListFormat("en-US", {
|
|
@@ -2107,6 +2259,7 @@ function mergeTimetablesWithSameId(timetables) {
|
|
|
2107
2259
|
}
|
|
2108
2260
|
|
|
2109
2261
|
// src/lib/file-utils.ts
|
|
2262
|
+
var homeDirectory = homedir();
|
|
2110
2263
|
async function getConfig(argv2) {
|
|
2111
2264
|
let data;
|
|
2112
2265
|
let config;
|
|
@@ -2188,18 +2341,32 @@ async function copyStaticAssets(config, outputPath) {
|
|
|
2188
2341
|
}
|
|
2189
2342
|
if (config.hasGtfsRealtimeVehiclePositions || config.hasGtfsRealtimeTripUpdates || config.hasGtfsRealtimeAlerts) {
|
|
2190
2343
|
await copyFile(
|
|
2191
|
-
|
|
2344
|
+
join(
|
|
2345
|
+
dirname(findPackageJSON("pbf", import.meta.url)),
|
|
2346
|
+
"dist/pbf.js"
|
|
2347
|
+
),
|
|
2192
2348
|
join(outputPath, "js/pbf.js")
|
|
2193
2349
|
);
|
|
2194
2350
|
await copyFile(
|
|
2195
|
-
|
|
2351
|
+
join(
|
|
2352
|
+
dirname(
|
|
2353
|
+
findPackageJSON(
|
|
2354
|
+
"gtfs-realtime-pbf-js-module",
|
|
2355
|
+
import.meta.url
|
|
2356
|
+
)
|
|
2357
|
+
),
|
|
2358
|
+
"gtfs-realtime.browser.proto.js"
|
|
2359
|
+
),
|
|
2196
2360
|
join(outputPath, "js/gtfs-realtime.browser.proto.js")
|
|
2197
2361
|
);
|
|
2198
2362
|
}
|
|
2199
2363
|
if (config.hasGtfsRealtimeAlerts) {
|
|
2200
2364
|
await copyFile(
|
|
2201
|
-
|
|
2202
|
-
|
|
2365
|
+
join(
|
|
2366
|
+
dirname(findPackageJSON("anchorme", import.meta.url)),
|
|
2367
|
+
"dist/browser/anchorme.min.js"
|
|
2368
|
+
),
|
|
2369
|
+
join(outputPath, "js/anchorme.min.js")
|
|
2203
2370
|
);
|
|
2204
2371
|
}
|
|
2205
2372
|
}
|
|
@@ -2216,15 +2383,34 @@ function zipFolder(outputPath) {
|
|
|
2216
2383
|
archive.finalize();
|
|
2217
2384
|
});
|
|
2218
2385
|
}
|
|
2219
|
-
function
|
|
2220
|
-
|
|
2386
|
+
function generateTimetablePageFileName(timetablePage, config) {
|
|
2387
|
+
if (timetablePage.filename) {
|
|
2388
|
+
return sanitize(timetablePage.filename);
|
|
2389
|
+
}
|
|
2390
|
+
if (config.groupTimetablesIntoPages === true && uniqBy2(timetablePage.timetables, "route_id").length === 1) {
|
|
2391
|
+
const route = timetablePage.timetables[0].routes[0];
|
|
2392
|
+
return sanitize(`${formatRouteNameForFilename(route).toLowerCase()}.html`);
|
|
2393
|
+
}
|
|
2394
|
+
const timetable = timetablePage.timetables[0];
|
|
2395
|
+
let filename = timetable.timetable_id ?? "";
|
|
2396
|
+
for (const route of timetable.routes) {
|
|
2397
|
+
filename += `_${formatRouteNameForFilename(route)}`;
|
|
2398
|
+
}
|
|
2399
|
+
if (!isNullOrEmpty(timetable.direction_id)) {
|
|
2400
|
+
filename += `_${timetable.direction_id}`;
|
|
2401
|
+
}
|
|
2402
|
+
filename += `_${formatDays(timetable, config).replace(/\s/g, "")}.html`;
|
|
2403
|
+
return sanitize(filename.toLowerCase());
|
|
2404
|
+
}
|
|
2405
|
+
function generateCSVFileName(timetable, config) {
|
|
2406
|
+
let filename = timetable.timetable_id ?? "";
|
|
2221
2407
|
for (const route of timetable.routes) {
|
|
2222
|
-
filename +=
|
|
2408
|
+
filename += `_${formatRouteNameForFilename(route)}`;
|
|
2223
2409
|
}
|
|
2224
2410
|
if (!isNullOrEmpty(timetable.direction_id)) {
|
|
2225
2411
|
filename += `_${timetable.direction_id}`;
|
|
2226
2412
|
}
|
|
2227
|
-
filename += `_${formatDays(timetable, config).replace(/\s/g, "")}
|
|
2413
|
+
filename += `_${formatDays(timetable, config).replace(/\s/g, "")}.csv`;
|
|
2228
2414
|
return sanitize(filename).toLowerCase();
|
|
2229
2415
|
}
|
|
2230
2416
|
function generateFolderName(timetablePage) {
|
|
@@ -2264,22 +2450,22 @@ async function renderPdf(htmlPath) {
|
|
|
2264
2450
|
});
|
|
2265
2451
|
await browser.close();
|
|
2266
2452
|
}
|
|
2453
|
+
function untildify(pathWithTilde) {
|
|
2454
|
+
return homeDirectory ? pathWithTilde.replace(/^~(?=$|\/|\\)/, homeDirectory) : pathWithTilde;
|
|
2455
|
+
}
|
|
2267
2456
|
|
|
2268
2457
|
// src/lib/gtfs-to-html.ts
|
|
2269
2458
|
import path from "path";
|
|
2270
2459
|
import { mkdir as mkdir2, writeFile } from "fs/promises";
|
|
2271
2460
|
import { openDb as openDb2, importGtfs } from "gtfs";
|
|
2272
2461
|
import sanitize2 from "sanitize-filename";
|
|
2273
|
-
import Timer from "timer-machine";
|
|
2274
|
-
import untildify2 from "untildify";
|
|
2275
2462
|
var gtfsToHtml = async (initialConfig) => {
|
|
2276
2463
|
const config = setDefaultConfig(initialConfig);
|
|
2277
|
-
const
|
|
2464
|
+
const startTime = process.hrtime.bigint();
|
|
2278
2465
|
const agencyKey = config.agencies.map(
|
|
2279
2466
|
(agency) => agency.agencyKey ?? agency.agency_key ?? "unknown"
|
|
2280
2467
|
).join("-");
|
|
2281
|
-
const outputPath = config.outputPath ?
|
|
2282
|
-
timer.start();
|
|
2468
|
+
const outputPath = config.outputPath ? untildify(config.outputPath) : path.join(process.cwd(), "html", sanitize2(agencyKey));
|
|
2283
2469
|
await prepDirectory(outputPath, config);
|
|
2284
2470
|
try {
|
|
2285
2471
|
openDb2(config);
|
|
@@ -2325,9 +2511,11 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2325
2511
|
config
|
|
2326
2512
|
);
|
|
2327
2513
|
for (const timetable of timetablePage.timetables) {
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2514
|
+
if (timetable.warnings) {
|
|
2515
|
+
for (const warning of timetable.warnings) {
|
|
2516
|
+
stats.warnings.push(warning);
|
|
2517
|
+
bar?.interrupt(warning);
|
|
2518
|
+
}
|
|
2331
2519
|
}
|
|
2332
2520
|
}
|
|
2333
2521
|
if (timetablePage.consolidatedTimetables.length === 0) {
|
|
@@ -2350,7 +2538,7 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2350
2538
|
const csvPath = path.join(
|
|
2351
2539
|
outputPath,
|
|
2352
2540
|
datePath,
|
|
2353
|
-
|
|
2541
|
+
generateCSVFileName(timetable, config)
|
|
2354
2542
|
);
|
|
2355
2543
|
await writeFile(csvPath, csv);
|
|
2356
2544
|
}
|
|
@@ -2396,11 +2584,11 @@ var gtfsToHtml = async (initialConfig) => {
|
|
|
2396
2584
|
`${agencyKey}: ${config.outputFormat.toUpperCase()} timetables created at ${fullOutputPath}`
|
|
2397
2585
|
);
|
|
2398
2586
|
logStats(config)(stats);
|
|
2399
|
-
const
|
|
2587
|
+
const endTime = process.hrtime.bigint();
|
|
2588
|
+
const elapsedSeconds = Number(endTime - startTime) / 1e9;
|
|
2400
2589
|
log(config)(
|
|
2401
|
-
`${agencyKey}: ${config.outputFormat.toUpperCase()} timetable generation required ${
|
|
2590
|
+
`${agencyKey}: ${config.outputFormat.toUpperCase()} timetable generation required ${elapsedSeconds.toFixed(1)} seconds`
|
|
2402
2591
|
);
|
|
2403
|
-
timer.stop();
|
|
2404
2592
|
return fullOutputPath;
|
|
2405
2593
|
};
|
|
2406
2594
|
var gtfs_to_html_default = gtfsToHtml;
|