gtfs-to-html 2.5.8 → 2.6.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/CHANGELOG.md +27 -0
- package/config-sample.json +13 -4
- package/lib/file-utils.js +17 -10
- package/lib/formatters.js +37 -12
- package/lib/utils.js +3 -5
- package/package.json +9 -9
- package/public/css/timetable_styles.css +8 -8
- package/public/js/system-map.js +106 -119
- package/public/js/timetable-map.js +62 -59
- package/views/default/overview.pug +4 -4
- package/views/default/overview_full.pug +3 -3
- package/views/default/timetablepage.pug +10 -9
- package/views/default/timetablepage_full.pug +3 -3
- package/www/docs/configuration.md +9 -0
- package/www/package.json +2 -2
- package/www/yarn.lock +447 -446
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,33 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.6.0] - 2024-02-27
|
|
9
|
+
|
|
10
|
+
### Updated
|
|
11
|
+
|
|
12
|
+
- Improved timetable_page_label for pages with a single timetable
|
|
13
|
+
- Better default timetable styles
|
|
14
|
+
- Default route_color and route_text_color values
|
|
15
|
+
- Better system map layer ordering
|
|
16
|
+
- Dependency updates
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `showCalendarExceptions` configuration option
|
|
21
|
+
- Add "timepoint" class to stoptimes
|
|
22
|
+
|
|
23
|
+
## [2.5.9] - 2024-01-24
|
|
24
|
+
|
|
25
|
+
### Updated
|
|
26
|
+
|
|
27
|
+
- Move stop markers above map labels
|
|
28
|
+
- Update to Mapbox GL JS v3
|
|
29
|
+
- Dependency updates
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Handle routes with no route_short_name in map marker popups
|
|
34
|
+
|
|
8
35
|
## [2.5.8] - 2024-01-02
|
|
9
36
|
|
|
10
37
|
### Fixed
|
package/config-sample.json
CHANGED
|
@@ -11,14 +11,22 @@
|
|
|
11
11
|
"coordinatePrecision": 5,
|
|
12
12
|
"dateFormat": "MMM D, YYYY",
|
|
13
13
|
"daysShortStrings": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
|
|
14
|
-
"daysStrings": [
|
|
14
|
+
"daysStrings": [
|
|
15
|
+
"Monday",
|
|
16
|
+
"Tuesday",
|
|
17
|
+
"Wednesday",
|
|
18
|
+
"Thursday",
|
|
19
|
+
"Friday",
|
|
20
|
+
"Saturday",
|
|
21
|
+
"Sunday"
|
|
22
|
+
],
|
|
15
23
|
"defaultOrientation": "vertical",
|
|
16
24
|
"effectiveDate": "July 8, 2016",
|
|
17
25
|
"interpolatedStopSymbol": "•",
|
|
18
26
|
"interpolatedStopText": "Estimated time of arrival",
|
|
19
|
-
"linkStopUrls":
|
|
27
|
+
"linkStopUrls": false,
|
|
20
28
|
"mapboxAccessToken": "YOUR MAPBOX ACCESS TOKEN",
|
|
21
|
-
"menuType": "
|
|
29
|
+
"menuType": "jump",
|
|
22
30
|
"noDropoffSymbol": "‡",
|
|
23
31
|
"noDropoffText": "No drop off available",
|
|
24
32
|
"noHead": false,
|
|
@@ -34,8 +42,9 @@
|
|
|
34
42
|
"serviceNotProvidedOnText": "Service not provided on",
|
|
35
43
|
"serviceProvidedOnText": "Service provided on",
|
|
36
44
|
"showArrivalOnDifference": 0.2,
|
|
45
|
+
"showCalendarExceptions": true,
|
|
37
46
|
"showMap": true,
|
|
38
|
-
"showOnlyTimepoint":
|
|
47
|
+
"showOnlyTimepoint": true,
|
|
39
48
|
"showRouteTitle": true,
|
|
40
49
|
"showStopCity": false,
|
|
41
50
|
"showStopDescription": false,
|
package/lib/file-utils.js
CHANGED
|
@@ -12,7 +12,12 @@ import puppeteer from 'puppeteer';
|
|
|
12
12
|
import sanitize from 'sanitize-filename';
|
|
13
13
|
import untildify from 'untildify';
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
isNullOrEmpty,
|
|
17
|
+
formatDays,
|
|
18
|
+
formatRouteColor,
|
|
19
|
+
formatRouteTextColor,
|
|
20
|
+
} from './formatters.js';
|
|
16
21
|
import * as templateFunctions from './template-functions.js';
|
|
17
22
|
|
|
18
23
|
/*
|
|
@@ -22,12 +27,12 @@ export async function getConfig(argv) {
|
|
|
22
27
|
try {
|
|
23
28
|
const data = await readFile(
|
|
24
29
|
path.resolve(untildify(argv.configPath)),
|
|
25
|
-
'utf8'
|
|
30
|
+
'utf8',
|
|
26
31
|
).catch((error) => {
|
|
27
32
|
console.error(
|
|
28
33
|
new Error(
|
|
29
|
-
`Cannot find configuration file at \`${argv.configPath}\`. Use config-sample.json as a starting point, pass --configPath option
|
|
30
|
-
)
|
|
34
|
+
`Cannot find configuration file at \`${argv.configPath}\`. Use config-sample.json as a starting point, pass --configPath option`,
|
|
35
|
+
),
|
|
31
36
|
);
|
|
32
37
|
throw error;
|
|
33
38
|
});
|
|
@@ -45,8 +50,8 @@ export async function getConfig(argv) {
|
|
|
45
50
|
} catch (error) {
|
|
46
51
|
console.error(
|
|
47
52
|
new Error(
|
|
48
|
-
`Cannot parse configuration file at \`${argv.configPath}\`. Check to ensure that it is valid JSON
|
|
49
|
-
)
|
|
53
|
+
`Cannot parse configuration file at \`${argv.configPath}\`. Check to ensure that it is valid JSON.`,
|
|
54
|
+
),
|
|
50
55
|
);
|
|
51
56
|
throw error;
|
|
52
57
|
}
|
|
@@ -65,14 +70,14 @@ function getTemplatePath(templateFileName, config) {
|
|
|
65
70
|
if (config.templatePath !== undefined) {
|
|
66
71
|
return path.join(
|
|
67
72
|
untildify(config.templatePath),
|
|
68
|
-
`${fullTemplateFileName}.pug
|
|
73
|
+
`${fullTemplateFileName}.pug`,
|
|
69
74
|
);
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
return path.join(
|
|
73
78
|
fileURLToPath(import.meta.url),
|
|
74
79
|
'../../views/default',
|
|
75
|
-
`${fullTemplateFileName}.pug
|
|
80
|
+
`${fullTemplateFileName}.pug`,
|
|
76
81
|
);
|
|
77
82
|
}
|
|
78
83
|
|
|
@@ -86,7 +91,7 @@ export async function prepDirectory(exportPath) {
|
|
|
86
91
|
} catch (error) {
|
|
87
92
|
if (error.code === 'ENOENT') {
|
|
88
93
|
throw new Error(
|
|
89
|
-
`Unable to write to ${exportPath}. Try running this command from a writable directory
|
|
94
|
+
`Unable to write to ${exportPath}. Try running this command from a writable directory.`,
|
|
90
95
|
);
|
|
91
96
|
}
|
|
92
97
|
|
|
@@ -100,7 +105,7 @@ export async function prepDirectory(exportPath) {
|
|
|
100
105
|
export function copyStaticAssets(exportPath) {
|
|
101
106
|
const staticAssetPath = path.join(
|
|
102
107
|
fileURLToPath(import.meta.url),
|
|
103
|
-
'../../public'
|
|
108
|
+
'../../public',
|
|
104
109
|
);
|
|
105
110
|
copydir.sync(path.join(staticAssetPath, 'css'), path.join(exportPath, 'css'));
|
|
106
111
|
copydir.sync(path.join(staticAssetPath, 'js'), path.join(exportPath, 'js'));
|
|
@@ -181,6 +186,8 @@ export async function renderTemplate(templateFileName, templateVars, config) {
|
|
|
181
186
|
const html = await renderFile(templatePath, {
|
|
182
187
|
_,
|
|
183
188
|
...templateFunctions,
|
|
189
|
+
formatRouteColor,
|
|
190
|
+
formatRouteTextColor,
|
|
184
191
|
...templateVars,
|
|
185
192
|
});
|
|
186
193
|
|
package/lib/formatters.js
CHANGED
|
@@ -116,6 +116,10 @@ function formatStopTime(stoptime, timetable, config) {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
if (stoptime.timepoint === 1) {
|
|
120
|
+
stoptime.classes.push('timepoint');
|
|
121
|
+
}
|
|
122
|
+
|
|
119
123
|
return stoptime;
|
|
120
124
|
}
|
|
121
125
|
/* eslint-enable complexity */
|
|
@@ -374,8 +378,8 @@ export function formatStopName(stop) {
|
|
|
374
378
|
stop.type === 'arrival'
|
|
375
379
|
? ' (Arrival)'
|
|
376
380
|
: stop.type === 'departure'
|
|
377
|
-
|
|
378
|
-
|
|
381
|
+
? ' (Departure)'
|
|
382
|
+
: ''
|
|
379
383
|
}`;
|
|
380
384
|
}
|
|
381
385
|
|
|
@@ -442,6 +446,22 @@ export function updateStoptimesByOffset(trip, offsetSeconds) {
|
|
|
442
446
|
});
|
|
443
447
|
}
|
|
444
448
|
|
|
449
|
+
/*
|
|
450
|
+
* Format a route color as a hex color.
|
|
451
|
+
*/
|
|
452
|
+
export function formatRouteColor(route) {
|
|
453
|
+
// Defaults to #000000 (black) if no color is provided.
|
|
454
|
+
return route.route_color ? `#${route.route_color}` : '#000000';
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/*
|
|
458
|
+
* Format a route text color as a hex color.
|
|
459
|
+
*/
|
|
460
|
+
export function formatRouteTextColor(route) {
|
|
461
|
+
// Defaults to #FFFFFF (white) if no color is provided.
|
|
462
|
+
return route.route_text_color ? `#${route.route_text_color}` : '#FFFFFF';
|
|
463
|
+
}
|
|
464
|
+
|
|
445
465
|
/*
|
|
446
466
|
* Format a label for a timetable.
|
|
447
467
|
*/
|
|
@@ -488,21 +508,26 @@ export function formatTimetablePageLabel(timetablePage) {
|
|
|
488
508
|
return timetablePage.timetable_page_label;
|
|
489
509
|
}
|
|
490
510
|
|
|
491
|
-
// Get label from first timetable.
|
|
492
511
|
if (
|
|
493
512
|
timetablePage.consolidatedTimetables &&
|
|
494
513
|
timetablePage.consolidatedTimetables.length > 0
|
|
495
514
|
) {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
515
|
+
if (timetablePage.consolidatedTimetables.length === 1) {
|
|
516
|
+
// Get label from first timetable if there is only one
|
|
517
|
+
return timetablePage.consolidatedTimetables[0].timetable_label;
|
|
518
|
+
} else {
|
|
519
|
+
// Otherwise, use route names from all timetables
|
|
520
|
+
const routes = uniqBy(
|
|
521
|
+
flatMap(
|
|
522
|
+
timetablePage.consolidatedTimetables,
|
|
523
|
+
(timetable) => timetable.routes,
|
|
524
|
+
),
|
|
525
|
+
'route_id',
|
|
526
|
+
);
|
|
527
|
+
const timetablePageLabel = routes.map((route) => formatRouteName(route));
|
|
504
528
|
|
|
505
|
-
|
|
529
|
+
return timetablePageLabel.join(' and ');
|
|
530
|
+
}
|
|
506
531
|
}
|
|
507
532
|
|
|
508
533
|
return 'Unknown';
|
package/lib/utils.js
CHANGED
|
@@ -1413,11 +1413,12 @@ export function setDefaultConfig(initialConfig) {
|
|
|
1413
1413
|
interpolatedStopSymbol: '•',
|
|
1414
1414
|
interpolatedStopText: 'Estimated time of arrival',
|
|
1415
1415
|
gtfsToHtmlVersion: version,
|
|
1416
|
+
linkStopUrls: false,
|
|
1416
1417
|
menuType: 'jump',
|
|
1417
1418
|
noDropoffSymbol: '‡',
|
|
1418
1419
|
noDropoffText: 'No drop off available',
|
|
1419
1420
|
noHead: false,
|
|
1420
|
-
noPickupSymbol: '
|
|
1421
|
+
noPickupSymbol: '**',
|
|
1421
1422
|
noPickupText: 'No pickup available',
|
|
1422
1423
|
noServiceSymbol: '-',
|
|
1423
1424
|
noServiceText: 'No service at this stop',
|
|
@@ -1429,6 +1430,7 @@ export function setDefaultConfig(initialConfig) {
|
|
|
1429
1430
|
serviceNotProvidedOnText: 'Service not provided on',
|
|
1430
1431
|
serviceProvidedOnText: 'Service provided on',
|
|
1431
1432
|
showArrivalOnDifference: 0.2,
|
|
1433
|
+
showCalendarExceptions: true,
|
|
1432
1434
|
showMap: false,
|
|
1433
1435
|
showOnlyTimepoint: false,
|
|
1434
1436
|
showRouteTitle: true,
|
|
@@ -1482,10 +1484,6 @@ export function getFormattedTimetablePage(timetablePageId, config) {
|
|
|
1482
1484
|
['route_color', 'route_text_color', 'agency_id'],
|
|
1483
1485
|
);
|
|
1484
1486
|
|
|
1485
|
-
timetablePage.routeColors = timetableRoutes.map((route) => route.route_color);
|
|
1486
|
-
timetablePage.routeTextColors = timetableRoutes.map(
|
|
1487
|
-
(route) => route.route_text_color,
|
|
1488
|
-
);
|
|
1489
1487
|
timetablePage.agency_ids = compact(
|
|
1490
1488
|
timetableRoutes.map((route) => route.agency_id),
|
|
1491
1489
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gtfs-to-html",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Build human readable transit timetables as HTML, PDF or CSV from GTFS",
|
|
6
6
|
"keywords": [
|
|
@@ -37,19 +37,19 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@turf/helpers": "^6.5.0",
|
|
39
39
|
"@turf/simplify": "^6.5.0",
|
|
40
|
-
"archiver": "^6.0.
|
|
40
|
+
"archiver": "^6.0.2",
|
|
41
41
|
"cli-table": "^0.3.11",
|
|
42
42
|
"copy-dir": "^1.3.0",
|
|
43
|
-
"csv-stringify": "^6.4.
|
|
43
|
+
"csv-stringify": "^6.4.6",
|
|
44
44
|
"express": "^4.18.2",
|
|
45
|
-
"gtfs": "^4.
|
|
46
|
-
"js-beautify": "^1.
|
|
45
|
+
"gtfs": "^4.7.1",
|
|
46
|
+
"js-beautify": "^1.15.1",
|
|
47
47
|
"lodash-es": "^4.17.21",
|
|
48
48
|
"moment": "^2.30.1",
|
|
49
49
|
"morgan": "^1.10.0",
|
|
50
50
|
"pretty-error": "^4.0.0",
|
|
51
51
|
"pug": "^3.0.2",
|
|
52
|
-
"puppeteer": "^
|
|
52
|
+
"puppeteer": "^22.3.0",
|
|
53
53
|
"sanitize-filename": "^1.6.3",
|
|
54
54
|
"sqlstring": "^2.3.3",
|
|
55
55
|
"timer-machine": "^1.1.0",
|
|
@@ -59,9 +59,9 @@
|
|
|
59
59
|
"yoctocolors": "^1.0.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"husky": "^
|
|
63
|
-
"lint-staged": "^15.2.
|
|
64
|
-
"prettier": "^3.
|
|
62
|
+
"husky": "^9.0.11",
|
|
63
|
+
"lint-staged": "^15.2.2",
|
|
64
|
+
"prettier": "^3.2.5"
|
|
65
65
|
},
|
|
66
66
|
"engines": {
|
|
67
67
|
"node": ">= 18.0.0"
|
|
@@ -163,22 +163,22 @@ a:hover {
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
.route-color-swatch {
|
|
166
|
-
min-width:
|
|
167
|
-
height:
|
|
168
|
-
border-radius:
|
|
166
|
+
min-width: 34px;
|
|
167
|
+
height: 34px;
|
|
168
|
+
border-radius: 17px;
|
|
169
169
|
text-align: center;
|
|
170
|
-
line-height:
|
|
170
|
+
line-height: 34px;
|
|
171
171
|
font-size: 14px;
|
|
172
172
|
letter-spacing: -0.5px;
|
|
173
173
|
padding: 0 5px;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
.route-color-swatch-large {
|
|
177
|
-
min-width:
|
|
178
|
-
height:
|
|
179
|
-
border-radius:
|
|
177
|
+
min-width: 46px;
|
|
178
|
+
height: 46px;
|
|
179
|
+
border-radius: 23px;
|
|
180
180
|
text-align: center;
|
|
181
|
-
line-height:
|
|
181
|
+
line-height: 46px;
|
|
182
182
|
font-size: 20px;
|
|
183
183
|
font-weight: bold;
|
|
184
184
|
letter-spacing: -1px;
|