gtfs-to-html 2.7.0 → 2.7.2

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 CHANGED
@@ -5,6 +5,23 @@ 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.7.2] - 2024-08-16
9
+
10
+ ### Updated
11
+ - Updated docker to node 20
12
+ - Dependency updates
13
+
14
+ ## [2.7.1] - 2024-08-13
15
+
16
+ ### Fixed
17
+ - Handle MultiLineString geojson
18
+
19
+ ### Updated
20
+ - Warning about invalid timetable start/end dates
21
+ - Deduplicate timetablePage agency_ids
22
+ - Don't show drop_off_type info for first stoptime of a trip, don't show pickup_type info for last stoptime of a trip.
23
+ - Dependency updates
24
+
8
25
  ## [2.7.0] - 2024-07-27
9
26
 
10
27
  ### Fixed
package/README.md CHANGED
@@ -73,10 +73,12 @@ Many transit agencies use `gtfs-to-html` to generate the schedule pages used on
73
73
  - [Marin Transit](https://marintransit.org)
74
74
  - [Morongo Basin Transit Authority](https://mbtabus.com)
75
75
  - [Mountain Transit](http://mountaintransit.org)
76
+ - [Mountain View Community Shuttle](http://mvcommunityshuttle.com)
76
77
  - [MVgo (Mountain View, CA)](https://mvgo.org)
77
78
  - [NW Connector (Oregon)](http://www.nworegontransit.org)
78
79
  - [Palo Verde Valley Transit Agency](http://pvvta.com)
79
80
  - [Petaluma Transit](http://transit.cityofpetaluma.net)
81
+ - [Rogue Valley Transportation District (Medford, OR)](https://rvtd.org)
80
82
  - [RTC Washoe (Reno, NV)](https://www.rtcwashoe.com)
81
83
  - [Santa Barbara Metropolitan Transit District](https://sbmtd.gov)
82
84
  - [Sonoma County Transit](http://sctransit.com)
package/docker/Dockerfile CHANGED
@@ -1,5 +1,5 @@
1
1
  # syntax=docker/dockerfile:1
2
- FROM node:16
2
+ FROM node:20
3
3
 
4
4
  RUN apt update
5
5
  RUN apt install -y chromium
@@ -7,7 +7,8 @@ RUN apt install -y chromium
7
7
  RUN cd ~/
8
8
  COPY config.json ./
9
9
 
10
- RUN npm -g config set user root
10
+ ENV NPM_CONFIG_PREFIX=/home/node/.npm-global
11
+ ENV PATH=$PATH:/home/node/.npm-global/bin
11
12
  RUN npm install -g gtfs-to-html
12
13
 
13
14
  CMD [ "gtfs-to-html" ]
package/docker/README.md CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  Use the files contained in this folder to run GTFS-to-HTML with `docker` (or `docker-compose`).
4
4
 
5
- Read more into the documentation.
5
+ Read more in the documentation.
package/lib/formatters.js CHANGED
@@ -313,6 +313,16 @@ export function formatStops(timetable, config) {
313
313
  continue;
314
314
  }
315
315
 
316
+ // If first stoptime of the trip, remove drop_off_type information
317
+ if (idx === 0) {
318
+ stoptime.drop_off_type = 0;
319
+ }
320
+
321
+ // If last stoptime of the trip, remove pickup_type information
322
+ if (idx === trip.stoptimes.length - 1) {
323
+ stoptime.pickup_type = 0;
324
+ }
325
+
316
326
  // If showing arrival and departure times as separate columns/rows, add
317
327
  // trip to the departure stop, unless it is the last stoptime of the trip.
318
328
  if (stop.type === 'arrival' && idx < trip.stoptimes.length - 1) {
@@ -1,7 +1,7 @@
1
1
  import { getShapesAsGeoJSON, getStopsAsGeoJSON } from 'gtfs';
2
2
  import { flatMap } from 'lodash-es';
3
3
  import simplify from '@turf/simplify';
4
- import { featureCollection } from '@turf/helpers';
4
+ import { featureCollection, round } from '@turf/helpers';
5
5
 
6
6
  /*
7
7
  * Merge any number of geojson objects into one. Only works for `FeatureCollection`.
@@ -9,14 +9,6 @@ import { featureCollection } from '@turf/helpers';
9
9
  const mergeGeojson = (...geojsons) =>
10
10
  featureCollection(flatMap(geojsons, (geojson) => geojson.features));
11
11
 
12
- /*
13
- * Truncate a coordinate to a specific number of decimal places.
14
- */
15
- const truncateCoordinate = (coordinate, precision) => [
16
- Math.round(coordinate[0] * 10 ** precision) / 10 ** precision,
17
- Math.round(coordinate[1] * 10 ** precision) / 10 ** precision,
18
- ];
19
-
20
12
  /*
21
13
  * Truncate a geojson coordinates to a specific number of decimal places.
22
14
  */
@@ -24,14 +16,24 @@ const truncateGeoJSONDecimals = (geojson, config) => {
24
16
  for (const feature of geojson.features) {
25
17
  if (feature.geometry.coordinates) {
26
18
  if (feature.geometry.type.toLowerCase() === 'point') {
27
- feature.geometry.coordinates = truncateCoordinate(
28
- feature.geometry.coordinates,
29
- config.coordinatePrecision
19
+ feature.geometry.coordinates = feature.geometry.coordinates.map(
20
+ (number) => round(number, config.coordinatePrecision),
30
21
  );
31
22
  } else if (feature.geometry.type.toLowerCase() === 'linestring') {
32
23
  feature.geometry.coordinates = feature.geometry.coordinates.map(
33
24
  (coordinate) =>
34
- truncateCoordinate(coordinate, config.coordinatePrecision)
25
+ coordinate.map((number) =>
26
+ round(number, config.coordinatePrecision),
27
+ ),
28
+ );
29
+ } else if (feature.geometry.type.toLowerCase() === 'multilinestring') {
30
+ feature.geometry.coordinates = feature.geometry.coordinates.map(
31
+ (linestring) =>
32
+ linestring.map((coordinate) =>
33
+ coordinate.map((number) =>
34
+ round(number, config.coordinatePrecision),
35
+ ),
36
+ ),
35
37
  );
36
38
  }
37
39
  }
@@ -67,7 +69,7 @@ export function getTimetableGeoJSON(timetable, config) {
67
69
  route_id: routeId,
68
70
  direction_id: timetable.direction_id,
69
71
  trip_id: timetable.orderedTrips.map((trip) => trip.trip_id),
70
- })
72
+ }),
71
73
  );
72
74
 
73
75
  const stopsGeojsons = timetable.route_ids.map((routeId) =>
@@ -75,7 +77,7 @@ export function getTimetableGeoJSON(timetable, config) {
75
77
  route_id: routeId,
76
78
  direction_id: timetable.direction_id,
77
79
  trip_id: timetable.orderedTrips.map((trip) => trip.trip_id),
78
- })
80
+ }),
79
81
  );
80
82
 
81
83
  const geojson = mergeGeojson(...shapesGeojsons, ...stopsGeojsons);
package/lib/utils.js CHANGED
@@ -823,10 +823,24 @@ const getCalendarsFromTimetable = (timetable) => {
823
823
  const whereClauses = [];
824
824
 
825
825
  if (timetable.end_date) {
826
+ // Validate timetable.end_date is a valid date
827
+ if (!moment(timetable.end_date, 'YYYYMMDD', true).isValid()) {
828
+ throw new Error(
829
+ `Invalid end_date=${timetable.end_date} for timetable_id=${timetable.timetable_id}`,
830
+ );
831
+ }
832
+
826
833
  whereClauses.push(`start_date <= ${sqlString.escape(timetable.end_date)}`);
827
834
  }
828
835
 
829
836
  if (timetable.start_date) {
837
+ // Validate timetable.start_date is a valid date
838
+ if (!moment(timetable.start_date, 'YYYYMMDD', true).isValid()) {
839
+ throw new Error(
840
+ `Invalid start_date=${timetable.start_date} for timetable_id=${timetable.timetable_id}`,
841
+ );
842
+ }
843
+
830
844
  whereClauses.push(`end_date >= ${sqlString.escape(timetable.start_date)}`);
831
845
  }
832
846
 
@@ -1519,11 +1533,11 @@ export function getFormattedTimetablePage(timetablePageId, config) {
1519
1533
  {
1520
1534
  route_id: timetablePage.route_ids,
1521
1535
  },
1522
- ['route_color', 'route_text_color', 'agency_id'],
1536
+ ['agency_id'],
1523
1537
  );
1524
1538
 
1525
- timetablePage.agency_ids = compact(
1526
- timetableRoutes.map((route) => route.agency_id),
1539
+ timetablePage.agency_ids = uniq(
1540
+ compact(timetableRoutes.map((route) => route.agency_id)),
1527
1541
  );
1528
1542
 
1529
1543
  // Set default filename.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtfs-to-html",
3
- "version": "2.7.0",
3
+ "version": "2.7.2",
4
4
  "private": false,
5
5
  "description": "Build human readable transit timetables as HTML, PDF or CSV from GTFS",
6
6
  "keywords": [
@@ -35,23 +35,23 @@
35
35
  "start": "node ./app"
36
36
  },
37
37
  "dependencies": {
38
- "@turf/helpers": "^7.0.0",
39
- "@turf/simplify": "^7.0.0",
38
+ "@turf/helpers": "^7.1.0",
39
+ "@turf/simplify": "^7.1.0",
40
40
  "archiver": "^7.0.1",
41
41
  "cli-table": "^0.3.11",
42
42
  "copy-dir": "^1.3.0",
43
43
  "csv-stringify": "^6.5.1",
44
44
  "express": "^4.19.2",
45
- "gtfs": "^4.13.2",
45
+ "gtfs": "^4.13.4",
46
46
  "insane": "^2.6.2",
47
47
  "js-beautify": "^1.15.1",
48
48
  "lodash-es": "^4.17.21",
49
- "marked": "^13.0.2",
49
+ "marked": "^14.0.0",
50
50
  "moment": "^2.30.1",
51
51
  "morgan": "^1.10.0",
52
52
  "pretty-error": "^4.0.0",
53
53
  "pug": "^3.0.3",
54
- "puppeteer": "^22.14.0",
54
+ "puppeteer": "^23.1.0",
55
55
  "sanitize-filename": "^1.6.3",
56
56
  "sqlstring": "^2.3.3",
57
57
  "timer-machine": "^1.1.0",
@@ -61,8 +61,8 @@
61
61
  "yoctocolors": "^2.1.1"
62
62
  },
63
63
  "devDependencies": {
64
- "husky": "^9.1.3",
65
- "lint-staged": "^15.2.7",
64
+ "husky": "^9.1.4",
65
+ "lint-staged": "^15.2.9",
66
66
  "prettier": "^3.3.3"
67
67
  },
68
68
  "engines": {
@@ -68,12 +68,7 @@
68
68
  }
69
69
 
70
70
  for (const feature of timetable.geojson.features) {
71
- if (feature.geometry.type === 'LineString') {
72
- feature.properties = {
73
- route_color: feature.properties.route_color
74
- }
75
- minifiedGeojson.features.push(feature)
76
- } else if (feature.geometry.type === 'Point') {
71
+ if (feature.geometry.type.toLowerCase() === 'point') {
77
72
  for (const route of feature.properties.routes) {
78
73
  routes[route.route_id] = route
79
74
  }
@@ -81,6 +76,16 @@
81
76
  feature.properties.routes = feature.properties.routes.map(route => route.route_id)
82
77
 
83
78
  minifiedGeojson.features.push(_.omit(feature, ['location_type', 'tts_stop_name']))
79
+ } else if (feature.geometry.type.toLowerCase() === 'linestring') {
80
+ feature.properties = {
81
+ route_color: feature.properties.route_color
82
+ }
83
+ minifiedGeojson.features.push(feature)
84
+ } else if (feature.geometry.type.toLowerCase() === 'multilinestring') {
85
+ feature.properties = {
86
+ route_color: feature.properties.route_color
87
+ }
88
+ minifiedGeojson.features.push(feature)
84
89
  }
85
90
  }
86
91
 
@@ -96,12 +96,18 @@ function formatStopPopup(feature) {
96
96
  function getBounds(geojson) {
97
97
  const bounds = new mapboxgl.LngLatBounds();
98
98
  for (const feature of geojson.features) {
99
- if (feature.geometry.type === 'Point') {
99
+ if (feature.geometry.type.toLowerCase() === 'point') {
100
100
  bounds.extend(feature.geometry.coordinates);
101
- } else if (feature.geometry.type === 'LineString') {
101
+ } else if (feature.geometry.type.toLowerCase() === 'linestring') {
102
102
  for (const coordinate of feature.geometry.coordinates) {
103
103
  bounds.extend(coordinate);
104
104
  }
105
+ } else if (feature.geometry.type.toLowerCase() === 'multilinestring') {
106
+ for (const linestring of feature.geometry.coordinates) {
107
+ for (const coordinate of linestring) {
108
+ bounds.extend(coordinate);
109
+ }
110
+ }
105
111
  }
106
112
  }
107
113
 
@@ -84,12 +84,18 @@ function formatStopPopup(feature, routes) {
84
84
  function getBounds(geojson) {
85
85
  const bounds = new mapboxgl.LngLatBounds();
86
86
  for (const feature of geojson.features) {
87
- if (feature.geometry.type === 'Point') {
87
+ if (feature.geometry.type.toLowerCase() === 'point') {
88
88
  bounds.extend(feature.geometry.coordinates);
89
- } else if (feature.geometry.type === 'LineString') {
89
+ } else if (feature.geometry.type.toLowerCase() === 'linestring') {
90
90
  for (const coordinate of feature.geometry.coordinates) {
91
91
  bounds.extend(coordinate);
92
92
  }
93
+ } else if (feature.geometry.type.toLowerCase() === 'multilinestring') {
94
+ for (const linestring of feature.geometry.coordinates) {
95
+ for (const coordinate of linestring) {
96
+ bounds.extend(coordinate);
97
+ }
98
+ }
93
99
  }
94
100
  }
95
101
 
@@ -23,10 +23,12 @@ Many transit agencies use GTFS-to-HTML to generate the schedule pages used on th
23
23
  | [Marin Transit](https://marintransit.org) | Marin County, California |
24
24
  | [Morongo Basin Transit Authority](https://mbtabus.com) | Morongo Basin, California |
25
25
  | [Mountain Transit](http://mountaintransit.org) | Big Bear Valley, California |
26
+ | [Mountain View Community Shuttle](https://mvcommunityshuttle.com) | Mountain View, California |
26
27
  | [MVgo](https://mvgo.org) | Mountain View, California |
27
28
  | [NW Connector](http://www.nworegontransit.org) | Northwest Oregon |
28
29
  | [Palo Verde Valley Transit Agency](http://pvvta.com) | Palo Verde Valley, California |
29
30
  | [Petaluma Transit](http://transit.cityofpetaluma.net) | Petaluma, California |
31
+ | [Rogue Valley Transportation District](https://rvtd.org) | Medford, Oregon |
30
32
  | [RTC Washoe](https://www.rtcwashoe.com) | Reno, Nevada |
31
33
  | [Santa Barbara Metropolitan Transit District](https://sbmtd.gov) | Santa Barbara, California |
32
34
  | [Sonoma County Transit](http://sctransit.com) | Sonoma County, California |
package/www/package.json CHANGED
@@ -9,8 +9,8 @@
9
9
  "deploy": "docusaurus deploy"
10
10
  },
11
11
  "dependencies": {
12
- "@docusaurus/core": "^3.4.0",
13
- "@docusaurus/preset-classic": "^3.4.0",
12
+ "@docusaurus/core": "^3.5.2",
13
+ "@docusaurus/preset-classic": "^3.5.2",
14
14
  "clsx": "^2.1.1",
15
15
  "react": "^18.3.1",
16
16
  "react-dom": "^18.3.1"