gtfs-to-html 2.5.5 → 2.5.7

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/lib/utils.js CHANGED
@@ -3,16 +3,21 @@ import { readFileSync } from 'node:fs';
3
3
  import {
4
4
  cloneDeep,
5
5
  compact,
6
+ countBy,
7
+ entries,
6
8
  every,
7
9
  find,
8
10
  findLast,
9
11
  first,
10
12
  flatMap,
11
13
  flattenDeep,
14
+ flow,
12
15
  isEqual,
13
16
  groupBy,
17
+ head,
14
18
  last,
15
19
  maxBy,
20
+ partialRight,
16
21
  reduce,
17
22
  size,
18
23
  some,
@@ -75,13 +80,13 @@ import {
75
80
  import { formatTripNameForCSV } from './template-functions.js';
76
81
 
77
82
  const { version } = JSON.parse(
78
- readFileSync(new URL('../package.json', import.meta.url))
83
+ readFileSync(new URL('../package.json', import.meta.url)),
79
84
  );
80
85
 
81
86
  /*
82
87
  * Determine if a stoptime is a timepoint.
83
88
  */
84
- const isTimepoint = (stoptime) => {
89
+ export const isTimepoint = (stoptime) => {
85
90
  if (isNullOrEmpty(stoptime.timepoint)) {
86
91
  return (
87
92
  !isNullOrEmpty(stoptime.arrival_time) &&
@@ -96,13 +101,15 @@ const isTimepoint = (stoptime) => {
96
101
  * Find the longest trip (most stops) in a group of trips and return stoptimes.
97
102
  */
98
103
  const getLongestTripStoptimes = (trips, config) => {
99
- // If `showOnlyTimepoint` is true, then filter out all non-timepoints.
100
- const filteredTripStoptimes =
101
- config.showOnlyTimepoint === true
102
- ? trips.map((trip) =>
103
- trip.stoptimes.filter((stoptime) => isTimepoint(stoptime))
104
- )
105
- : trips.map((trip) => trip.stoptimes);
104
+ const filteredTripStoptimes = trips.map((trip) =>
105
+ trip.stoptimes.filter((stoptime) => {
106
+ // If `showOnlyTimepoint` is true, then filter out all non-timepoints.
107
+ if (config.showOnlyTimepoint === true) {
108
+ return isTimepoint(stoptime);
109
+ }
110
+ return true;
111
+ }),
112
+ );
106
113
 
107
114
  return maxBy(filteredTripStoptimes, (stoptimes) => size(stoptimes));
108
115
  };
@@ -134,8 +141,8 @@ const findCommonStopId = (trips, config) => {
134
141
  trip.stoptimes.find(
135
142
  (tripStoptime) =>
136
143
  tripStoptime.stop_id === stoptime.stop_id &&
137
- tripStoptime.arrival_time !== null
138
- )
144
+ tripStoptime.arrival_time !== null,
145
+ ),
139
146
  );
140
147
  });
141
148
 
@@ -178,7 +185,7 @@ const deduplicateTrips = (trips, commonStopId) => {
178
185
  // Only add trip if no existing trip with the same set of timepoints has already been added.
179
186
  const tripIsUnique = every(similarTrips, (similarTrip) => {
180
187
  const similarTripStoptimes = similarTrip.stoptimes.map(
181
- (stoptime) => stoptime.departure_time
188
+ (stoptime) => stoptime.departure_time,
182
189
  );
183
190
  return !isEqual(stoptimes, similarTripStoptimes);
184
191
  });
@@ -227,7 +234,7 @@ const sortTrips = (trips, config) => {
227
234
  sortedTrips = sortBy(
228
235
  trips,
229
236
  ['firstStoptime', 'lastStoptime'],
230
- ['asc', 'asc']
237
+ ['asc', 'asc'],
231
238
  );
232
239
  } else if (config.sortingAlgorithm === 'end') {
233
240
  // Sort trips chronologically using last stoptime of each trip, which can be at different stops.
@@ -244,7 +251,7 @@ const sortTrips = (trips, config) => {
244
251
  sortedTrips = sortBy(
245
252
  trips,
246
253
  ['lastStoptime', 'firstStoptime'],
247
- ['asc', 'asc']
254
+ ['asc', 'asc'],
248
255
  );
249
256
  } else if (config.sortingAlgorithm === 'first') {
250
257
  // Sort trips chronologically using the stoptime of a the first stop of the longest trip.
@@ -281,7 +288,7 @@ const getCalendarDatesForTimetable = (timetable, config) => {
281
288
  service_id: timetable.service_ids,
282
289
  },
283
290
  [],
284
- [['date', 'ASC']]
291
+ [['date', 'ASC']],
285
292
  );
286
293
  const start = fromGTFSDate(timetable.start_date);
287
294
  const end = fromGTFSDate(timetable.end_date);
@@ -294,11 +301,11 @@ const getCalendarDatesForTimetable = (timetable, config) => {
294
301
  if (moment(calendarDate.date, 'YYYYMMDD').isBetween(start, end)) {
295
302
  if (calendarDate.exception_type === 1) {
296
303
  filteredCalendarDates.includedDates.push(
297
- formatDate(calendarDate, config.dateFormat)
304
+ formatDate(calendarDate, config.dateFormat),
298
305
  );
299
306
  } else if (calendarDate.exception_type === 2) {
300
307
  filteredCalendarDates.excludedDates.push(
301
- formatDate(calendarDate, config.dateFormat)
308
+ formatDate(calendarDate, config.dateFormat),
302
309
  );
303
310
  }
304
311
  }
@@ -340,14 +347,21 @@ const getDirectionHeadsignFromTimetable = (timetable) => {
340
347
  direction_id: timetable.direction_id,
341
348
  route_id: timetable.route_ids,
342
349
  },
343
- ['trip_headsign']
350
+ ['trip_headsign'],
344
351
  );
345
352
 
346
353
  if (trips.length === 0) {
347
354
  return '';
348
355
  }
349
356
 
350
- return first(trips).trip_headsign;
357
+ const mostCommonHeadsign = flow(
358
+ countBy,
359
+ entries,
360
+ partialRight(maxBy, last),
361
+ head,
362
+ )(compact(trips.map((trip) => trip.trip_headsign)));
363
+
364
+ return mostCommonHeadsign;
351
365
  };
352
366
 
353
367
  /*
@@ -394,13 +408,13 @@ const getTimetableNotesForTimetable = (timetable, config) => {
394
408
  // Note references with stop_sequence must also have stop_id.
395
409
  if (noteReference.stop_id === '' || noteReference.stop_id === null) {
396
410
  config.logWarning(
397
- `Timetable Note Reference for note_id=${noteReference.note_id} has a \`stop_sequence\` but no \`stop_id\` - ignoring`
411
+ `Timetable Note Reference for note_id=${noteReference.note_id} has a \`stop_sequence\` but no \`stop_id\` - ignoring`,
398
412
  );
399
413
  continue;
400
414
  }
401
415
 
402
416
  const stop = timetable.stops.find(
403
- (stop) => stop.stop_id === noteReference.stop_id
417
+ (stop) => stop.stop_id === noteReference.stop_id,
404
418
  );
405
419
 
406
420
  if (!stop) {
@@ -408,7 +422,7 @@ const getTimetableNotesForTimetable = (timetable, config) => {
408
422
  }
409
423
 
410
424
  const tripWithMatchingStopSequence = stop.trips.find(
411
- (trip) => trip.stop_sequence === noteReference.stop_sequence
425
+ (trip) => trip.stop_sequence === noteReference.stop_sequence,
412
426
  );
413
427
 
414
428
  if (tripWithMatchingStopSequence) {
@@ -472,7 +486,7 @@ const convertRouteToTimetablePage = (
472
486
  direction,
473
487
  calendars,
474
488
  calendarDates,
475
- config
489
+ config,
476
490
  ) => {
477
491
  const timetable = {
478
492
  route_ids: [route.route_id],
@@ -501,10 +515,12 @@ const convertRouteToTimetablePage = (
501
515
  Object.assign(timetable, getDaysFromCalendars(calendars));
502
516
 
503
517
  timetable.start_date = toGTFSDate(
504
- moment.min(calendars.map((calendar) => fromGTFSDate(calendar.start_date)))
518
+ moment.min(
519
+ calendars.map((calendar) => fromGTFSDate(calendar.start_date)),
520
+ ),
505
521
  );
506
522
  timetable.end_date = toGTFSDate(
507
- moment.max(calendars.map((calendar) => fromGTFSDate(calendar.end_date)))
523
+ moment.max(calendars.map((calendar) => fromGTFSDate(calendar.end_date))),
508
524
  );
509
525
  }
510
526
 
@@ -528,7 +544,7 @@ const convertRoutesToTimetablePages = (config) => {
528
544
  .prepare(
529
545
  `SELECT * FROM calendar_dates WHERE exception_type = 1 AND service_id NOT IN (${serviceIds
530
546
  .map((serviceId) => `'${serviceId}'`)
531
- .join(', ')})`
547
+ .join(', ')})`,
532
548
  )
533
549
  .all();
534
550
 
@@ -537,7 +553,7 @@ const convertRoutesToTimetablePages = (config) => {
537
553
  {
538
554
  route_id: route.route_id,
539
555
  },
540
- ['trip_headsign', 'direction_id']
556
+ ['trip_headsign', 'direction_id'],
541
557
  );
542
558
  const directions = uniqBy(trips, (trip) => trip.direction_id);
543
559
  const dayGroups = groupBy(calendars, calendarToCalendarCode);
@@ -545,7 +561,7 @@ const convertRoutesToTimetablePages = (config) => {
545
561
 
546
562
  return directions.map((direction) => [
547
563
  Object.values(dayGroups).map((calendars) =>
548
- convertRouteToTimetablePage(route, direction, calendars, null, config)
564
+ convertRouteToTimetablePage(route, direction, calendars, null, config),
549
565
  ),
550
566
  Object.values(calendarDateGroups).map((calendarDates) =>
551
567
  convertRouteToTimetablePage(
@@ -553,8 +569,8 @@ const convertRoutesToTimetablePages = (config) => {
553
569
  direction,
554
570
  null,
555
571
  calendarDates,
556
- config
557
- )
572
+ config,
573
+ ),
558
574
  ),
559
575
  ]);
560
576
  });
@@ -567,7 +583,7 @@ const convertRoutesToTimetablePages = (config) => {
567
583
  */
568
584
  const generateTripsByFrequencies = (trip, frequencies, config) => {
569
585
  const formattedFrequencies = frequencies.map((frequency) =>
570
- formatFrequency(frequency, config)
586
+ formatFrequency(frequency, config),
571
587
  );
572
588
  const resetTrip = resetStoptimesToMidnight(trip);
573
589
  const trips = [];
@@ -600,7 +616,7 @@ const generateTripsByFrequencies = (trip, frequencies, config) => {
600
616
  const duplicateStopsForDifferentArrivalDeparture = (
601
617
  stopIds,
602
618
  timetable,
603
- config
619
+ config,
604
620
  ) => {
605
621
  if (config.showArrivalOnDifference === null) {
606
622
  return stopIds;
@@ -610,7 +626,7 @@ const duplicateStopsForDifferentArrivalDeparture = (
610
626
  for (const stoptime of trip.stoptimes) {
611
627
  const timepointDifference = fromGTFSTime(stoptime.departure_time).diff(
612
628
  fromGTFSTime(stoptime.arrival_time),
613
- 'minutes'
629
+ 'minutes',
614
630
  );
615
631
 
616
632
  if (timepointDifference < config.showArrivalOnDifference) {
@@ -646,12 +662,12 @@ const getStopOrder = (timetable, config) => {
646
662
  timetable_id: timetable.timetable_id,
647
663
  },
648
664
  ['stop_id'],
649
- [['stop_sequence', 'ASC']]
665
+ [['stop_sequence', 'ASC']],
650
666
  );
651
667
 
652
668
  if (timetableStopOrders.length > 0) {
653
669
  return timetableStopOrders.map(
654
- (timetableStopOrder) => timetableStopOrder.stop_id
670
+ (timetableStopOrder) => timetableStopOrder.stop_id,
655
671
  );
656
672
  }
657
673
 
@@ -660,13 +676,15 @@ const getStopOrder = (timetable, config) => {
660
676
  const stopGraph = [];
661
677
 
662
678
  for (const trip of timetable.orderedTrips) {
663
- // If `showOnlyTimepoint` is true, then filter out all non-timepoints.
664
- const sortedStopIds =
665
- config.showOnlyTimepoint === true
666
- ? trip.stoptimes
667
- .filter((stoptime) => isTimepoint(stoptime))
668
- .map((stoptime) => stoptime.stop_id)
669
- : trip.stoptimes.map((stoptime) => stoptime.stop_id);
679
+ const sortedStopIds = trip.stoptimes
680
+ .filter((stoptime) => {
681
+ // If `showOnlyTimepoint` is true, then filter out all non-timepoints.
682
+ if (config.showOnlyTimepoint === true) {
683
+ return isTimepoint(stoptime);
684
+ }
685
+ return true;
686
+ })
687
+ .map((stoptime) => stoptime.stop_id);
670
688
 
671
689
  for (const [index, stopId] of sortedStopIds.entries()) {
672
690
  if (index === sortedStopIds.length - 1) {
@@ -682,7 +700,7 @@ const getStopOrder = (timetable, config) => {
682
700
  return duplicateStopsForDifferentArrivalDeparture(
683
701
  stopIds,
684
702
  timetable,
685
- config
703
+ config,
686
704
  );
687
705
  } catch {
688
706
  // Ignore errors and move to next strategy.
@@ -691,7 +709,7 @@ const getStopOrder = (timetable, config) => {
691
709
  // Finally, fall back to using the stop order from the trip with the most stoptimes.
692
710
  const longestTripStoptimes = getLongestTripStoptimes(
693
711
  timetable.orderedTrips,
694
- config
712
+ config,
695
713
  );
696
714
  const stopIds = longestTripStoptimes.map((stoptime) => stoptime.stop_id);
697
715
 
@@ -714,7 +732,7 @@ const getStopsForTimetable = (timetable, config) => {
714
732
 
715
733
  if (stops.length === 0) {
716
734
  throw new Error(
717
- `No stop found found for stop_id=${stopId} in timetable_id=${timetable.timetable_id}`
735
+ `No stop found found for stop_id=${stopId} in timetable_id=${timetable.timetable_id}`,
718
736
  );
719
737
  }
720
738
 
@@ -743,7 +761,7 @@ const getStopsForTimetable = (timetable, config) => {
743
761
 
744
762
  for (const stopAttribute of stopAttributes) {
745
763
  const stop = orderedStops.find(
746
- (stop) => stop.stop_id === stopAttribute.stop_id
764
+ (stop) => stop.stop_id === stopAttribute.stop_id,
747
765
  );
748
766
 
749
767
  if (stop) {
@@ -782,7 +800,7 @@ const getCalendarsFromTimetable = (timetable) => {
782
800
 
783
801
  return memo;
784
802
  },
785
- []
803
+ [],
786
804
  );
787
805
 
788
806
  if (dayQueries.length > 0) {
@@ -814,8 +832,8 @@ const getCalendarDatesServiceIds = (startDate, endDate) => {
814
832
  const calendarDates = db
815
833
  .prepare(
816
834
  `SELECT DISTINCT service_id FROM calendar_dates WHERE ${whereClauses.join(
817
- ' AND '
818
- )}`
835
+ ' AND ',
836
+ )}`,
819
837
  )
820
838
  .all();
821
839
  return calendarDates.map((calendarDate) => calendarDate.service_id);
@@ -841,7 +859,7 @@ const getAllStationStopIds = (stopId) => {
841
859
  {
842
860
  parent_station: stop.parent_station,
843
861
  },
844
- ['stop_id']
862
+ ['stop_id'],
845
863
  );
846
864
 
847
865
  return [
@@ -859,7 +877,7 @@ const getTripsWithSameBlock = (trip, timetable) => {
859
877
  block_id: trip.block_id,
860
878
  service_id: timetable.service_ids,
861
879
  },
862
- ['trip_id', 'route_id']
880
+ ['trip_id', 'route_id'],
863
881
  );
864
882
 
865
883
  for (const blockTrip of trips) {
@@ -868,12 +886,12 @@ const getTripsWithSameBlock = (trip, timetable) => {
868
886
  trip_id: blockTrip.trip_id,
869
887
  },
870
888
  [],
871
- [['stop_sequence', 'ASC']]
889
+ [['stop_sequence', 'ASC']],
872
890
  );
873
891
 
874
892
  if (stopTimes.length === 0) {
875
893
  throw new Error(
876
- `No stoptimes found found for trip_id=${blockTrip.trip_id}`
894
+ `No stoptimes found found for trip_id=${blockTrip.trip_id}`,
877
895
  );
878
896
  }
879
897
 
@@ -906,7 +924,7 @@ const addTripContinuation = (trip, timetable) => {
906
924
  blockTrips,
907
925
  (blockTrip) =>
908
926
  blockTrip.lastStoptime.arrival_timestamp <=
909
- firstStoptime.departure_timestamp
927
+ firstStoptime.departure_timestamp,
910
928
  );
911
929
 
912
930
  /*
@@ -936,7 +954,7 @@ const addTripContinuation = (trip, timetable) => {
936
954
  blockTrips,
937
955
  (blockTrip) =>
938
956
  blockTrip.firstStoptime.departure_timestamp >=
939
- lastStoptime.arrival_timestamp
957
+ lastStoptime.arrival_timestamp,
940
958
  );
941
959
 
942
960
  // "Continues As" trips must be a different route_id.
@@ -993,7 +1011,7 @@ const filterTrips = (timetable) => {
993
1011
  const timetableStopIds = new Set(timetable.stops.map((stop) => stop.stop_id));
994
1012
  for (const trip of filteredTrips) {
995
1013
  trip.stoptimes = trip.stoptimes.filter((stoptime) =>
996
- timetableStopIds.has(stoptime.stop_id)
1014
+ timetableStopIds.has(stoptime.stop_id),
997
1015
  );
998
1016
  }
999
1017
 
@@ -1023,10 +1041,10 @@ const getTripsForTimetable = (timetable, calendars, config) => {
1023
1041
  if (trips.length === 0) {
1024
1042
  timetable.warnings.push(
1025
1043
  `No trips found for route_id=${timetable.route_ids.join(
1026
- '_'
1044
+ '_',
1027
1045
  )}, direction_id=${timetable.direction_id}, service_ids=${JSON.stringify(
1028
- timetable.service_ids
1029
- )}, timetable_id=${timetable.timetable_id}`
1046
+ timetable.service_ids,
1047
+ )}, timetable_id=${timetable.timetable_id}`,
1030
1048
  );
1031
1049
  }
1032
1050
 
@@ -1046,7 +1064,7 @@ const getTripsForTimetable = (timetable, calendars, config) => {
1046
1064
  trip_id: formattedTrip.trip_id,
1047
1065
  },
1048
1066
  [],
1049
- [['stop_sequence', 'ASC']]
1067
+ [['stop_sequence', 'ASC']],
1050
1068
  );
1051
1069
 
1052
1070
  if (formattedTrip.stoptimes.length === 0) {
@@ -1055,7 +1073,7 @@ const getTripsForTimetable = (timetable, calendars, config) => {
1055
1073
  formattedTrip.trip_id
1056
1074
  }, route_id=${timetable.route_ids.join('_')}, timetable_id=${
1057
1075
  timetable.timetable_id
1058
- }`
1076
+ }`,
1059
1077
  );
1060
1078
  }
1061
1079
 
@@ -1092,7 +1110,7 @@ const getTripsForTimetable = (timetable, calendars, config) => {
1092
1110
  }
1093
1111
 
1094
1112
  const tripFrequencies = frequencies.filter(
1095
- (frequency) => frequency.trip_id === trip.trip_id
1113
+ (frequency) => frequency.trip_id === trip.trip_id,
1096
1114
  );
1097
1115
 
1098
1116
  if (tripFrequencies.length === 0) {
@@ -1101,7 +1119,7 @@ const getTripsForTimetable = (timetable, calendars, config) => {
1101
1119
  const frequencyTrips = generateTripsByFrequencies(
1102
1120
  formattedTrip,
1103
1121
  frequencies,
1104
- config
1122
+ config,
1105
1123
  );
1106
1124
  formattedTrips.push(...frequencyTrips);
1107
1125
  timetable.frequencies = frequencies;
@@ -1124,13 +1142,13 @@ const getTripsForTimetable = (timetable, calendars, config) => {
1124
1142
  {
1125
1143
  stop_id: uniq(stopIds),
1126
1144
  },
1127
- ['parent_station', 'stop_id']
1145
+ ['parent_station', 'stop_id'],
1128
1146
  );
1129
1147
 
1130
1148
  for (const trip of formattedTrips) {
1131
1149
  for (const stoptime of trip.stoptimes) {
1132
1150
  const parentStationStop = stops.find(
1133
- (stop) => stop.stop_id === stoptime.stop_id
1151
+ (stop) => stop.stop_id === stoptime.stop_id,
1134
1152
  );
1135
1153
  stoptime.stop_id =
1136
1154
  parentStationStop.parent_station || parentStationStop.stop_id;
@@ -1155,7 +1173,7 @@ const formatTimetables = (timetables, config) => {
1155
1173
  if (timetable.include_exceptions === 1) {
1156
1174
  const calendarDatesServiceIds = getCalendarDatesServiceIds(
1157
1175
  timetable.start_date,
1158
- timetable.end_date
1176
+ timetable.end_date,
1159
1177
  );
1160
1178
  serviceIds = uniq([...serviceIds, ...calendarDatesServiceIds]);
1161
1179
  }
@@ -1205,7 +1223,7 @@ const formatTimetables = (timetables, config) => {
1205
1223
  }
1206
1224
 
1207
1225
  return formattedTimetables.filter(
1208
- (timetable) => timetable.orderedTrips.length > 0
1226
+ (timetable) => timetable.orderedTrips.length > 0,
1209
1227
  );
1210
1228
  };
1211
1229
 
@@ -1223,14 +1241,14 @@ export function getTimetablePagesForAgency(config) {
1223
1241
  const timetablePages = getTimetablePages(
1224
1242
  {},
1225
1243
  [],
1226
- [['timetable_page_id', 'ASC']]
1244
+ [['timetable_page_id', 'ASC']],
1227
1245
  );
1228
1246
 
1229
1247
  // Check if there are any timetable pages defined in `timetable_pages.txt`.
1230
1248
  if (timetablePages.length === 0) {
1231
1249
  // If no timetablepages, use timetables
1232
1250
  return timetables.map((timetable) =>
1233
- convertTimetableToTimetablePage(timetable, config)
1251
+ convertTimetableToTimetablePage(timetable, config),
1234
1252
  );
1235
1253
  }
1236
1254
 
@@ -1241,15 +1259,15 @@ export function getTimetablePagesForAgency(config) {
1241
1259
  timetablePage.timetables = sortBy(
1242
1260
  timetables.filter(
1243
1261
  (timetable) =>
1244
- timetable.timetable_page_id === timetablePage.timetable_page_id
1262
+ timetable.timetable_page_id === timetablePage.timetable_page_id,
1245
1263
  ),
1246
- 'timetable_sequence'
1264
+ 'timetable_sequence',
1247
1265
  );
1248
1266
 
1249
1267
  // Add routes for each timetable.
1250
1268
  for (const timetable of timetablePage.timetables) {
1251
1269
  timetable.routes = routes.filter((route) =>
1252
- timetable.route_ids.includes(route.route_id)
1270
+ timetable.route_ids.includes(route.route_id),
1253
1271
  );
1254
1272
  }
1255
1273
 
@@ -1270,7 +1288,7 @@ const getTimetablePageById = (timetablePageId, config) => {
1270
1288
 
1271
1289
  if (timetablePages.length > 1) {
1272
1290
  throw new Error(
1273
- `Multiple timetable_pages found for timetable_page_id=${timetablePageId}`
1291
+ `Multiple timetable_pages found for timetable_page_id=${timetablePageId}`,
1274
1292
  );
1275
1293
  }
1276
1294
 
@@ -1279,9 +1297,9 @@ const getTimetablePageById = (timetablePageId, config) => {
1279
1297
  const timetablePage = timetablePages[0];
1280
1298
  timetablePage.timetables = sortBy(
1281
1299
  timetables.filter(
1282
- (timetable) => timetable.timetable_page_id === timetablePageId
1300
+ (timetable) => timetable.timetable_page_id === timetablePageId,
1283
1301
  ),
1284
- 'timetable_sequence'
1302
+ 'timetable_sequence',
1285
1303
  );
1286
1304
 
1287
1305
  // Add routes for each timetable
@@ -1297,12 +1315,12 @@ const getTimetablePageById = (timetablePageId, config) => {
1297
1315
  if (timetables.length > 0) {
1298
1316
  // If no timetable_page, use timetable defined in `timetables.txt`.
1299
1317
  const timetablePageTimetables = timetables.filter(
1300
- (timetable) => timetable.timetable_id === timetablePageId
1318
+ (timetable) => timetable.timetable_id === timetablePageId,
1301
1319
  );
1302
1320
 
1303
1321
  if (timetablePageTimetables.length === 0) {
1304
1322
  throw new Error(
1305
- `No timetable found for timetable_page_id=${timetablePageId}`
1323
+ `No timetable found for timetable_page_id=${timetablePageId}`,
1306
1324
  );
1307
1325
  }
1308
1326
 
@@ -1335,13 +1353,13 @@ const getTimetablePageById = (timetablePageId, config) => {
1335
1353
  route_id: routeId,
1336
1354
  direction_id: directionId,
1337
1355
  },
1338
- ['trip_headsign', 'direction_id']
1356
+ ['trip_headsign', 'direction_id'],
1339
1357
  );
1340
1358
  const directions = uniqBy(trips, (trip) => trip.direction_id);
1341
1359
 
1342
1360
  if (directions.length === 0) {
1343
1361
  throw new Error(
1344
- `No trips found for timetable_page_id=${timetablePageId} route_id=${routeId} direction_id=${directionId}`
1362
+ `No trips found for timetable_page_id=${timetablePageId} route_id=${routeId} direction_id=${directionId}`,
1345
1363
  );
1346
1364
  }
1347
1365
 
@@ -1362,7 +1380,7 @@ const getTimetablePageById = (timetablePageId, config) => {
1362
1380
  directions[0],
1363
1381
  calendars,
1364
1382
  calendarDates,
1365
- config
1383
+ config,
1366
1384
  );
1367
1385
  };
1368
1386
 
@@ -1437,33 +1455,33 @@ export function getFormattedTimetablePage(timetablePageId, config) {
1437
1455
 
1438
1456
  timetablePage.consolidatedTimetables = formatTimetables(
1439
1457
  timetablePage.timetables,
1440
- config
1458
+ config,
1441
1459
  );
1442
1460
  timetablePage.timetable_page_label = formatTimetablePageLabel(timetablePage);
1443
1461
  timetablePage.dayList = formatDays(
1444
1462
  getDaysFromCalendars(timetablePage.consolidatedTimetables),
1445
- config
1463
+ config,
1446
1464
  );
1447
1465
  timetablePage.dayLists = uniq(
1448
- timetablePage.consolidatedTimetables.map((timetable) => timetable.dayList)
1466
+ timetablePage.consolidatedTimetables.map((timetable) => timetable.dayList),
1449
1467
  );
1450
1468
  timetablePage.route_ids = uniq(
1451
- flatMap(timetablePage.consolidatedTimetables, 'route_ids')
1469
+ flatMap(timetablePage.consolidatedTimetables, 'route_ids'),
1452
1470
  );
1453
1471
 
1454
1472
  const timetableRoutes = getRoutes(
1455
1473
  {
1456
1474
  route_id: timetablePage.route_ids,
1457
1475
  },
1458
- ['route_color', 'route_text_color', 'agency_id']
1476
+ ['route_color', 'route_text_color', 'agency_id'],
1459
1477
  );
1460
1478
 
1461
1479
  timetablePage.routeColors = timetableRoutes.map((route) => route.route_color);
1462
1480
  timetablePage.routeTextColors = timetableRoutes.map(
1463
- (route) => route.route_text_color
1481
+ (route) => route.route_text_color,
1464
1482
  );
1465
1483
  timetablePage.agency_ids = compact(
1466
- timetableRoutes.map((route) => route.agency_id)
1484
+ timetableRoutes.map((route) => route.agency_id),
1467
1485
  );
1468
1486
 
1469
1487
  // Set default filename.
@@ -1537,7 +1555,7 @@ export function generateTimetableCSV(timetable) {
1537
1555
  lines.push([
1538
1556
  '',
1539
1557
  ...timetable.orderedTrips.map((trip) =>
1540
- formatTripNameForCSV(trip, timetable)
1558
+ formatTripNameForCSV(trip, timetable),
1541
1559
  ),
1542
1560
  ]);
1543
1561
 
@@ -1592,7 +1610,7 @@ export function generateOverviewHTML(timetablePages, config) {
1592
1610
  ) {
1593
1611
  return Number.parseInt(
1594
1612
  timetablePage.consolidatedTimetables[0].routes[0].route_short_name,
1595
- 10
1613
+ 10,
1596
1614
  );
1597
1615
  }
1598
1616
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtfs-to-html",
3
- "version": "2.5.5",
3
+ "version": "2.5.7",
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": "^5.3.1",
40
+ "archiver": "^6.0.1",
41
41
  "cli-table": "^0.3.11",
42
42
  "copy-dir": "^1.3.0",
43
- "csv-stringify": "^6.4.0",
43
+ "csv-stringify": "^6.4.4",
44
44
  "express": "^4.18.2",
45
- "gtfs": "^4.4.3",
46
- "js-beautify": "^1.14.8",
45
+ "gtfs": "^4.5.0",
46
+ "js-beautify": "^1.14.11",
47
47
  "lodash-es": "^4.17.21",
48
48
  "moment": "^2.29.4",
49
49
  "morgan": "^1.10.0",
50
50
  "pretty-error": "^4.0.0",
51
51
  "pug": "^3.0.2",
52
- "puppeteer": "^20.8.3",
52
+ "puppeteer": "^21.5.0",
53
53
  "sanitize-filename": "^1.6.3",
54
54
  "sqlstring": "^2.3.3",
55
55
  "timer-machine": "^1.1.0",
@@ -60,8 +60,8 @@
60
60
  },
61
61
  "devDependencies": {
62
62
  "husky": "^8.0.3",
63
- "lint-staged": "^13.2.3",
64
- "prettier": "^3.0.0"
63
+ "lint-staged": "^15.0.2",
64
+ "prettier": "^3.0.3"
65
65
  },
66
66
  "engines": {
67
67
  "node": ">= 18.0.0"
@@ -28,7 +28,7 @@
28
28
  th(scope="row" colspan=`${stop.trips.length + 1}`)= stop.stop_city
29
29
  - previousCity = stop.stop_city
30
30
 
31
- tr.stop-row(id=`stop_id_${formatHtmlId(stop.stop_id)}` data-stop-id=`${stop.stop_id}`)
31
+ tr.stop-row(id=`stop_id_${formatHtmlId(stop.stop_id)}` data-stop-id=`${stop.stop_id}` data-is-timepoint=`${stop.is_timepoint}`)
32
32
  th.stop-name-container(scope="row")
33
33
  include timetable_stop_name.pug
34
34