gtfs-to-html 2.12.7 → 2.12.9
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/README.md +3 -1
- package/config-sample.json +4 -4
- package/dist/app/index.js +82 -45
- package/dist/app/index.js.map +1 -1
- package/dist/bin/gtfs-to-html.js +82 -32
- package/dist/bin/gtfs-to-html.js.map +1 -1
- package/dist/frontend_libraries/anchorme.min.js +1 -0
- package/dist/frontend_libraries/gtfs-realtime.browser.proto.js +0 -0
- package/dist/frontend_libraries/maplibre-gl-geocoder.css +284 -0
- package/dist/frontend_libraries/maplibre-gl-geocoder.js +2786 -0
- package/dist/frontend_libraries/maplibre-gl.css +1 -0
- package/dist/frontend_libraries/maplibre-gl.js +59 -0
- package/dist/frontend_libraries/pbf.js +1 -0
- package/dist/index.js +82 -32
- package/dist/index.js.map +1 -1
- package/package.json +15 -16
package/README.md
CHANGED
|
@@ -61,11 +61,13 @@ Many transit agencies use `gtfs-to-html` to generate the schedule pages used on
|
|
|
61
61
|
|
|
62
62
|
| Agency | Location |
|
|
63
63
|
| ----------------------------------------------------------------------------- | ----------------------------------- |
|
|
64
|
+
| [Asheville Rides Transit (ART)](https://www.ridetheart.com) | Asheville, North Carolina |
|
|
64
65
|
| [Basin Transit](https://basin-transit.com) | Morongo Basin, California |
|
|
65
66
|
| [Bayway Transit](https://www.baywaytransit.org) | Panama City, Florida |
|
|
66
67
|
| [Brockton Area Transit Authority](https://ridebat.com) | Brockton, Massachusetts |
|
|
67
68
|
| [Cape Ann Transportation Authority](https://canntran.com) | Gloucester, Massachusetts |
|
|
68
69
|
| [Capital Transit](https://juneaucapitaltransit.org) | Juneau, Alaska |
|
|
70
|
+
| [Cascades East Transit](https://cascadeseasttransit.com) | Bend, Oregon |
|
|
69
71
|
| [Central Transit](https://centraltransit.org) | Ellensburg, Washington |
|
|
70
72
|
| [Citibus](https://citibus.com) | Lubbock, Texas |
|
|
71
73
|
| [Commute.org](https://commute.org) | San Mateo County, California |
|
|
@@ -77,7 +79,7 @@ Many transit agencies use `gtfs-to-html` to generate the schedule pages used on
|
|
|
77
79
|
| [Kings Area Rural Transit (KART)](https://www.kartbus.org) | Kings County, California |
|
|
78
80
|
| [Lowell Regional Transit Authority](https://lrta.com) | Lowell, Massachusetts |
|
|
79
81
|
| [Madera County Connection](https://mcctransit.com) | Madera County, California |
|
|
80
|
-
| [Marin Transit](https://marintransit.
|
|
82
|
+
| [Marin Transit](https://marintransit.gov) | Marin County, California |
|
|
81
83
|
| [Morongo Basin Transit Authority](https://mbtabus.com) | Morongo Basin, California |
|
|
82
84
|
| [Mountain Transit](https://mountaintransit.org) | Big Bear Valley, California |
|
|
83
85
|
| [Mountain View Community Shuttle](https://mvcommunityshuttle.com) | Mountain View, California |
|
package/config-sample.json
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
"agencies": [
|
|
3
3
|
{
|
|
4
4
|
"agencyKey": "marintransit",
|
|
5
|
-
"url": "https://marintransit.
|
|
5
|
+
"url": "https://marintransit.gov/data/google_transit.zip",
|
|
6
6
|
"realtimeAlerts": {
|
|
7
|
-
"url": "https://api.marintransit.
|
|
7
|
+
"url": "https://api.marintransit.gov/alerts"
|
|
8
8
|
},
|
|
9
9
|
"realtimeTripUpdates": {
|
|
10
|
-
"url": "https://api.marintransit.
|
|
10
|
+
"url": "https://api.marintransit.gov/tripupdates"
|
|
11
11
|
},
|
|
12
12
|
"realtimeVehiclePositions": {
|
|
13
|
-
"url": "https://api.marintransit.
|
|
13
|
+
"url": "https://api.marintransit.gov/vehiclepositions"
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
],
|
package/dist/app/index.js
CHANGED
|
@@ -188,7 +188,7 @@ import {
|
|
|
188
188
|
import { homedir } from "os";
|
|
189
189
|
import * as _ from "lodash-es";
|
|
190
190
|
import { uniqBy } from "lodash-es";
|
|
191
|
-
import
|
|
191
|
+
import { ZipArchive } from "archiver";
|
|
192
192
|
import beautify from "js-beautify";
|
|
193
193
|
import sanitizeHtml from "sanitize-html";
|
|
194
194
|
import { renderFile } from "pug";
|
|
@@ -330,6 +330,27 @@ function isGtfsToHtmlError(error) {
|
|
|
330
330
|
return candidate.name === "GtfsToHtmlError" && typeof candidate.message === "string" && typeof candidate.code === "string" && typeof candidate.category === "string" && typeof candidate.isOperational === "boolean";
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
+
// src/lib/log-utils.ts
|
|
334
|
+
import { clearLine, cursorTo } from "readline";
|
|
335
|
+
import { noop } from "lodash-es";
|
|
336
|
+
import * as colors from "yoctocolors";
|
|
337
|
+
import { getAgencies, getFeedInfo, isGtfsError as isGtfsError2, formatGtfsError } from "gtfs";
|
|
338
|
+
import Table from "cli-table";
|
|
339
|
+
function logWarning(config2) {
|
|
340
|
+
if (config2.logFunction) {
|
|
341
|
+
return config2.logFunction;
|
|
342
|
+
}
|
|
343
|
+
return (text) => {
|
|
344
|
+
process.stdout.write(`
|
|
345
|
+
${formatWarning(text)}
|
|
346
|
+
`);
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function formatWarning(text) {
|
|
350
|
+
const warningMessage = `${colors.underline("Warning")}: ${text}`;
|
|
351
|
+
return colors.yellow(warningMessage);
|
|
352
|
+
}
|
|
353
|
+
|
|
333
354
|
// src/lib/file-utils.ts
|
|
334
355
|
var homeDirectory = homedir();
|
|
335
356
|
function getPathToThisModuleFolder() {
|
|
@@ -405,27 +426,6 @@ import { getShapesAsGeoJSON, getStopsAsGeoJSON } from "gtfs";
|
|
|
405
426
|
import simplify from "@turf/simplify";
|
|
406
427
|
import { featureCollection, round } from "@turf/helpers";
|
|
407
428
|
|
|
408
|
-
// src/lib/log-utils.ts
|
|
409
|
-
import { clearLine, cursorTo } from "readline";
|
|
410
|
-
import { noop } from "lodash-es";
|
|
411
|
-
import * as colors from "yoctocolors";
|
|
412
|
-
import { getAgencies, getFeedInfo, isGtfsError as isGtfsError2, formatGtfsError } from "gtfs";
|
|
413
|
-
import Table from "cli-table";
|
|
414
|
-
function logWarning(config2) {
|
|
415
|
-
if (config2.logFunction) {
|
|
416
|
-
return config2.logFunction;
|
|
417
|
-
}
|
|
418
|
-
return (text) => {
|
|
419
|
-
process.stdout.write(`
|
|
420
|
-
${formatWarning(text)}
|
|
421
|
-
`);
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
function formatWarning(text) {
|
|
425
|
-
const warningMessage = `${colors.underline("Warning")}: ${text}`;
|
|
426
|
-
return colors.yellow(warningMessage);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
429
|
// src/lib/trip-id-utils.ts
|
|
430
430
|
var getBaseTripId = (tripId) => tripId.replace(/_freq_\d+$/, "");
|
|
431
431
|
var getBaseTripIds = (trips) => Array.from(new Set(trips.map((trip) => getBaseTripId(trip.trip_id))));
|
|
@@ -509,7 +509,7 @@ function getAgencyGeoJSON(config2) {
|
|
|
509
509
|
// package.json
|
|
510
510
|
var package_default = {
|
|
511
511
|
name: "gtfs-to-html",
|
|
512
|
-
version: "2.12.
|
|
512
|
+
version: "2.12.9",
|
|
513
513
|
private: false,
|
|
514
514
|
description: "Build human readable transit timetables as HTML, PDF or CSV from GTFS",
|
|
515
515
|
keywords: [
|
|
@@ -555,15 +555,15 @@ var package_default = {
|
|
|
555
555
|
build: "tsup",
|
|
556
556
|
postbuild: "node scripts/postinstall.js",
|
|
557
557
|
start: "node ./dist/app",
|
|
558
|
-
prepare: "husky &&
|
|
558
|
+
prepare: "husky && pnpm run build",
|
|
559
559
|
postinstall: "node scripts/postinstall.js"
|
|
560
560
|
},
|
|
561
561
|
dependencies: {
|
|
562
562
|
"@maplibre/maplibre-gl-geocoder": "^1.9.4",
|
|
563
|
-
"@turf/helpers": "^7.3.
|
|
564
|
-
"@turf/simplify": "^7.3.
|
|
563
|
+
"@turf/helpers": "^7.3.5",
|
|
564
|
+
"@turf/simplify": "^7.3.5",
|
|
565
565
|
anchorme: "^3.0.8",
|
|
566
|
-
archiver: "^
|
|
566
|
+
archiver: "^8.0.0",
|
|
567
567
|
"cli-table": "^0.3.11",
|
|
568
568
|
"css.escape": "^1.5.1",
|
|
569
569
|
"csv-stringify": "^6.7.0",
|
|
@@ -572,15 +572,15 @@ var package_default = {
|
|
|
572
572
|
"gtfs-realtime-pbf-js-module": "^1.0.0",
|
|
573
573
|
"js-beautify": "^1.15.4",
|
|
574
574
|
"lodash-es": "^4.18.1",
|
|
575
|
-
"maplibre-gl": "^5.
|
|
576
|
-
marked: "^
|
|
575
|
+
"maplibre-gl": "^5.24.0",
|
|
576
|
+
marked: "^18.0.3",
|
|
577
577
|
moment: "^2.30.1",
|
|
578
578
|
pbf: "^4.0.1",
|
|
579
579
|
"pretty-error": "^4.0.0",
|
|
580
580
|
pug: "^3.0.4",
|
|
581
|
-
puppeteer: "^24.
|
|
581
|
+
puppeteer: "^24.43.1",
|
|
582
582
|
"sanitize-filename": "^1.6.4",
|
|
583
|
-
"sanitize-html": "^2.17.
|
|
583
|
+
"sanitize-html": "^2.17.4",
|
|
584
584
|
sqlstring: "^2.3.3",
|
|
585
585
|
toposort: "^2.0.2",
|
|
586
586
|
yargs: "^18.0.0",
|
|
@@ -599,14 +599,15 @@ var package_default = {
|
|
|
599
599
|
"@types/toposort": "^2.0.7",
|
|
600
600
|
"@types/yargs": "^17.0.35",
|
|
601
601
|
husky: "^9.1.7",
|
|
602
|
-
"lint-staged": "^
|
|
603
|
-
prettier: "^3.8.
|
|
602
|
+
"lint-staged": "^17.0.4",
|
|
603
|
+
prettier: "^3.8.3",
|
|
604
604
|
tsup: "^8.5.1",
|
|
605
|
-
typescript: "^6.0.
|
|
605
|
+
typescript: "^6.0.3"
|
|
606
606
|
},
|
|
607
607
|
engines: {
|
|
608
608
|
node: ">= 22"
|
|
609
609
|
},
|
|
610
|
+
packageManager: "pnpm@11.1.1+sha512.d1fdf5f73c617b64fa1a56a81c3c8dfe0e966e33a6010aa256b517ae77be21d93e05affc0de1a83b0e4f29d569f68b446ae8f068cd7247c0bb3df0fb4d7bdf9a",
|
|
610
611
|
"release-it": {
|
|
611
612
|
github: {
|
|
612
613
|
release: true
|
|
@@ -617,16 +618,14 @@ var package_default = {
|
|
|
617
618
|
}
|
|
618
619
|
},
|
|
619
620
|
hooks: {
|
|
620
|
-
"after:bump": "
|
|
621
|
+
"after:bump": "pnpm run build"
|
|
621
622
|
}
|
|
622
623
|
},
|
|
623
624
|
prettier: {
|
|
624
625
|
singleQuote: true
|
|
625
626
|
},
|
|
626
627
|
"lint-staged": {
|
|
627
|
-
"*.js": "prettier --write"
|
|
628
|
-
"*.ts": "prettier --write",
|
|
629
|
-
"*.json": "prettier --write"
|
|
628
|
+
"*.{js,ts,json}": "prettier --write"
|
|
630
629
|
}
|
|
631
630
|
};
|
|
632
631
|
|
|
@@ -1251,9 +1250,8 @@ var getCalendarsFromConfig = (config2) => {
|
|
|
1251
1250
|
}
|
|
1252
1251
|
const calendars = db.prepare(`SELECT * FROM calendar ${whereClause}`).all();
|
|
1253
1252
|
const serviceIds = calendars.map((calendar) => calendar.service_id);
|
|
1254
|
-
const
|
|
1255
|
-
|
|
1256
|
-
).all();
|
|
1253
|
+
const calendarDatesQuery = serviceIds.length > 0 ? `SELECT * FROM calendar_dates WHERE exception_type = 1 AND service_id NOT IN (${serviceIds.map((serviceId) => sqlString.escape(serviceId)).join(", ")})` : "SELECT * FROM calendar_dates WHERE exception_type = 1";
|
|
1254
|
+
const calendarDates = db.prepare(calendarDatesQuery).all();
|
|
1257
1255
|
return {
|
|
1258
1256
|
calendars,
|
|
1259
1257
|
calendarDates
|
|
@@ -1325,10 +1323,9 @@ var getCalendarDatesForDateRange = (startDate, endDate) => {
|
|
|
1325
1323
|
if (startDate) {
|
|
1326
1324
|
whereClauses.push(`date >= ${sqlString.escape(startDate)}`);
|
|
1327
1325
|
}
|
|
1326
|
+
const whereClause = whereClauses.length > 0 ? ` WHERE ${whereClauses.join(" AND ")}` : "";
|
|
1328
1327
|
const calendarDates = db.prepare(
|
|
1329
|
-
`SELECT service_id, date, exception_type FROM calendar_dates
|
|
1330
|
-
" AND "
|
|
1331
|
-
)}`
|
|
1328
|
+
`SELECT service_id, date, exception_type FROM calendar_dates${whereClause}`
|
|
1332
1329
|
).all();
|
|
1333
1330
|
return calendarDates;
|
|
1334
1331
|
};
|
|
@@ -1449,6 +1446,46 @@ var filterTrips = (timetable, calendars, config2) => {
|
|
|
1449
1446
|
if (config2.showDuplicateTrips === false) {
|
|
1450
1447
|
filteredTrips = deduplicateTrips(filteredTrips);
|
|
1451
1448
|
}
|
|
1449
|
+
const dayNames = [
|
|
1450
|
+
"monday",
|
|
1451
|
+
"tuesday",
|
|
1452
|
+
"wednesday",
|
|
1453
|
+
"thursday",
|
|
1454
|
+
"friday",
|
|
1455
|
+
"saturday",
|
|
1456
|
+
"sunday"
|
|
1457
|
+
];
|
|
1458
|
+
const timetableDays = dayNames.filter((day) => timetable[day] === 1);
|
|
1459
|
+
if (timetableDays.length > 1) {
|
|
1460
|
+
const warnedServiceIds = /* @__PURE__ */ new Set();
|
|
1461
|
+
for (const trip of filteredTrips) {
|
|
1462
|
+
const tripServiceIds = [
|
|
1463
|
+
trip.service_id,
|
|
1464
|
+
...trip.additional_service_ids ?? []
|
|
1465
|
+
];
|
|
1466
|
+
const tripCalendars = calendars.filter(
|
|
1467
|
+
(c) => tripServiceIds.includes(c.service_id)
|
|
1468
|
+
);
|
|
1469
|
+
if (tripCalendars.length === 0) {
|
|
1470
|
+
continue;
|
|
1471
|
+
}
|
|
1472
|
+
const tripDays = getDaysFromCalendars(tripCalendars);
|
|
1473
|
+
const missingDays = timetableDays.filter(
|
|
1474
|
+
(day) => (tripDays[day] ?? 0) !== 1
|
|
1475
|
+
);
|
|
1476
|
+
if (missingDays.length > 0) {
|
|
1477
|
+
const serviceIdKey = tripServiceIds.sort().join("|");
|
|
1478
|
+
if (!warnedServiceIds.has(serviceIdKey)) {
|
|
1479
|
+
warnedServiceIds.add(serviceIdKey);
|
|
1480
|
+
const tripDayList = formatDays(tripDays, config2);
|
|
1481
|
+
const timetableDayList = formatDays(timetable, config2);
|
|
1482
|
+
timetable.warnings.push(
|
|
1483
|
+
`Timetable ${timetable.timetable_id} (Routes: ${timetable.routes.map((route) => route.route_short_name).join(", ")}) covers ${timetableDayList} but some trips (service_id=${tripServiceIds.join(", ")}) only run on ${tripDayList}. This may indicate a data issue in the GTFS or that you should generate separate timetables for different days of the week.`
|
|
1484
|
+
);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1452
1489
|
const formattedTrips = filteredTrips.map((trip) => {
|
|
1453
1490
|
const tripCalendars = calendars.filter((calendar) => {
|
|
1454
1491
|
return [
|
|
@@ -1807,7 +1844,7 @@ var getTimetablePageById = (timetablePageId, config2) => {
|
|
|
1807
1844
|
}
|
|
1808
1845
|
if (timetablePageId.startsWith("route_")) {
|
|
1809
1846
|
const routes = getRoutes({
|
|
1810
|
-
route_id: timetablePageId.
|
|
1847
|
+
route_id: timetablePageId.slice("route_".length)
|
|
1811
1848
|
});
|
|
1812
1849
|
if (routes.length === 0) {
|
|
1813
1850
|
throw new GtfsToHtmlError(
|