gtfs-to-html 2.7.0 → 2.7.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/CHANGELOG.md CHANGED
@@ -5,6 +5,17 @@ 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.1] - 2024-08-13
9
+
10
+ ### Fixed
11
+ - Handle MultiLineString geojson
12
+
13
+ ### Updated
14
+ - Warning about invalid timetable start/end dates
15
+ - Deduplicate timetablePage agency_ids
16
+ - 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.
17
+ - Dependency updates
18
+
8
19
  ## [2.7.0] - 2024-07-27
9
20
 
10
21
  ### Fixed
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.1",
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.0.2",
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
 
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"