corequery-gtfs 0.1.0 → 0.1.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.
Files changed (185) hide show
  1. package/README.md +1 -1
  2. package/dist/config/gtfs-config.d.ts +10 -0
  3. package/dist/config/gtfs-config.js +2 -0
  4. package/dist/config/gtfs-config.js.map +1 -0
  5. package/dist/config/ids.d.ts +13 -0
  6. package/dist/config/ids.js +2 -0
  7. package/dist/config/ids.js.map +1 -0
  8. package/dist/config/index.d.ts +4 -0
  9. package/dist/config/index.js +5 -0
  10. package/dist/config/index.js.map +1 -0
  11. package/dist/config/routes.d.ts +16 -0
  12. package/dist/config/routes.js +2 -0
  13. package/dist/config/routes.js.map +1 -0
  14. package/dist/config/timezone-data.d.ts +5 -0
  15. package/dist/config/timezone-data.js +2 -0
  16. package/dist/config/timezone-data.js.map +1 -0
  17. package/dist/corequery-types.d.ts +2 -0
  18. package/dist/corequery-types.js +3 -0
  19. package/dist/corequery-types.js.map +1 -0
  20. package/dist/data/bounded-instant-range.d.ts +10 -0
  21. package/dist/data/bounded-instant-range.js +30 -0
  22. package/dist/data/bounded-instant-range.js.map +1 -0
  23. package/dist/data/gtfs-calendar.d.ts +38 -0
  24. package/dist/data/gtfs-calendar.js +116 -0
  25. package/dist/data/gtfs-calendar.js.map +1 -0
  26. package/dist/data/gtfs-feed.d.ts +16 -0
  27. package/dist/data/gtfs-feed.js +33 -0
  28. package/dist/data/gtfs-feed.js.map +1 -0
  29. package/dist/data/gtfs-realtime-data.d.ts +9 -0
  30. package/dist/data/gtfs-realtime-data.js +20 -0
  31. package/dist/data/gtfs-realtime-data.js.map +1 -0
  32. package/dist/data/gtfs-schedule-data.d.ts +13 -0
  33. package/dist/data/gtfs-schedule-data.js +40 -0
  34. package/dist/data/gtfs-schedule-data.js.map +1 -0
  35. package/dist/data/gtfs-scheduled-trip-movements.d.ts +118 -0
  36. package/dist/data/gtfs-scheduled-trip-movements.js +233 -0
  37. package/dist/data/gtfs-scheduled-trip-movements.js.map +1 -0
  38. package/dist/data/gtfs-scheduled-trip.d.ts +41 -0
  39. package/dist/data/gtfs-scheduled-trip.js +100 -0
  40. package/dist/data/gtfs-scheduled-trip.js.map +1 -0
  41. package/dist/data/gtfs-stop-time.d.ts +38 -0
  42. package/dist/data/gtfs-stop-time.js +105 -0
  43. package/dist/data/gtfs-stop-time.js.map +1 -0
  44. package/dist/data/gtfs-updated-trip-movements.d.ts +124 -0
  45. package/dist/data/gtfs-updated-trip-movements.js +150 -0
  46. package/dist/data/gtfs-updated-trip-movements.js.map +1 -0
  47. package/dist/data/gtfs-updated-trip.d.ts +21 -0
  48. package/dist/data/gtfs-updated-trip.js +55 -0
  49. package/dist/data/gtfs-updated-trip.js.map +1 -0
  50. package/dist/data/ids/index.d.ts +2 -0
  51. package/dist/data/ids/index.js +3 -0
  52. package/dist/data/ids/index.js.map +1 -0
  53. package/dist/data/ids/line-gtfs-id-collection.d.ts +16 -0
  54. package/dist/data/ids/line-gtfs-id-collection.js +47 -0
  55. package/dist/data/ids/line-gtfs-id-collection.js.map +1 -0
  56. package/dist/data/ids/line-gtfs-id-mapping.d.ts +17 -0
  57. package/dist/data/ids/line-gtfs-id-mapping.js +50 -0
  58. package/dist/data/ids/line-gtfs-id-mapping.js.map +1 -0
  59. package/dist/data/ids/line-gtfs-id-metadata.d.ts +17 -0
  60. package/dist/data/ids/line-gtfs-id-metadata.js +2 -0
  61. package/dist/data/ids/line-gtfs-id-metadata.js.map +1 -0
  62. package/dist/data/ids/stop-gtfs-id-collection.d.ts +14 -0
  63. package/dist/data/ids/stop-gtfs-id-collection.js +55 -0
  64. package/dist/data/ids/stop-gtfs-id-collection.js.map +1 -0
  65. package/dist/data/ids/stop-gtfs-id-mapping.d.ts +17 -0
  66. package/dist/data/ids/stop-gtfs-id-mapping.js +50 -0
  67. package/dist/data/ids/stop-gtfs-id-mapping.js.map +1 -0
  68. package/dist/data/ids/stop-gtfs-id-metadata.d.ts +23 -0
  69. package/dist/data/ids/stop-gtfs-id-metadata.js +2 -0
  70. package/dist/data/ids/stop-gtfs-id-metadata.js.map +1 -0
  71. package/dist/data/index.d.ts +3 -0
  72. package/dist/data/index.js +4 -0
  73. package/dist/data/index.js.map +1 -0
  74. package/dist/data/plain-date-range.d.ts +11 -0
  75. package/dist/data/plain-date-range.js +36 -0
  76. package/dist/data/plain-date-range.js.map +1 -0
  77. package/dist/data/raw/realtime-data-json.d.ts +26 -0
  78. package/dist/data/raw/realtime-data-json.js +2 -0
  79. package/dist/data/raw/realtime-data-json.js.map +1 -0
  80. package/dist/data/raw/schedule-csvs.d.ts +69 -0
  81. package/dist/data/raw/schedule-csvs.js +2 -0
  82. package/dist/data/raw/schedule-csvs.js.map +1 -0
  83. package/dist/data/route/bonus-lines-mapping.d.ts +17 -0
  84. package/dist/data/route/bonus-lines-mapping.js +29 -0
  85. package/dist/data/route/bonus-lines-mapping.js.map +1 -0
  86. package/dist/data/route/index.d.ts +1 -0
  87. package/dist/data/route/index.js +2 -0
  88. package/dist/data/route/index.js.map +1 -0
  89. package/dist/data/route/line-routes-mapping.d.ts +8 -0
  90. package/dist/data/route/line-routes-mapping.js +20 -0
  91. package/dist/data/route/line-routes-mapping.js.map +1 -0
  92. package/dist/data/route/route-stop.d.ts +12 -0
  93. package/dist/data/route/route-stop.js +18 -0
  94. package/dist/data/route/route-stop.js.map +1 -0
  95. package/dist/data/route/route.d.ts +19 -0
  96. package/dist/data/route/route.js +43 -0
  97. package/dist/data/route/route.js.map +1 -0
  98. package/dist/data/utils.d.ts +3 -0
  99. package/dist/data/utils.js +2 -0
  100. package/dist/data/utils.js.map +1 -0
  101. package/dist/departures/departures-block-iterator.d.ts +18 -0
  102. package/dist/departures/departures-block-iterator.js +70 -0
  103. package/dist/departures/departures-block-iterator.js.map +1 -0
  104. package/dist/departures/departures-block.d.ts +19 -0
  105. package/dist/departures/departures-block.js +13 -0
  106. package/dist/departures/departures-block.js.map +1 -0
  107. package/dist/departures/departures-iterator.d.ts +16 -0
  108. package/dist/departures/departures-iterator.js +15 -0
  109. package/dist/departures/departures-iterator.js.map +1 -0
  110. package/dist/departures/gtfs-scheduled-movements-index.d.ts +18 -0
  111. package/dist/departures/gtfs-scheduled-movements-index.js +66 -0
  112. package/dist/departures/gtfs-scheduled-movements-index.js.map +1 -0
  113. package/dist/departures/index.d.ts +1 -0
  114. package/dist/departures/index.js +2 -0
  115. package/dist/departures/index.js.map +1 -0
  116. package/dist/departures/multifeed-departures-iterator.d.ts +20 -0
  117. package/dist/departures/multifeed-departures-iterator.js +52 -0
  118. package/dist/departures/multifeed-departures-iterator.js.map +1 -0
  119. package/dist/departures/realtime-departures-block-iterator.d.ts +12 -0
  120. package/dist/departures/realtime-departures-block-iterator.js +38 -0
  121. package/dist/departures/realtime-departures-block-iterator.js.map +1 -0
  122. package/dist/departures/realtime-departures-block.d.ts +16 -0
  123. package/dist/departures/realtime-departures-block.js +43 -0
  124. package/dist/departures/realtime-departures-block.js.map +1 -0
  125. package/dist/departures/scheduled-departures-block-iterator.d.ts +12 -0
  126. package/dist/departures/scheduled-departures-block-iterator.js +55 -0
  127. package/dist/departures/scheduled-departures-block-iterator.js.map +1 -0
  128. package/dist/departures/scheduled-departures-block.d.ts +18 -0
  129. package/dist/departures/scheduled-departures-block.js +54 -0
  130. package/dist/departures/scheduled-departures-block.js.map +1 -0
  131. package/dist/departures/scheduled-departures-blocks-builder.d.ts +20 -0
  132. package/dist/departures/scheduled-departures-blocks-builder.js +222 -0
  133. package/dist/departures/scheduled-departures-blocks-builder.js.map +1 -0
  134. package/dist/departures/scheduled-departures-iterator.d.ts +30 -0
  135. package/dist/departures/scheduled-departures-iterator.js +204 -0
  136. package/dist/departures/scheduled-departures-iterator.js.map +1 -0
  137. package/dist/departures/zipper-departures-iterator.d.ts +18 -0
  138. package/dist/departures/zipper-departures-iterator.js +68 -0
  139. package/dist/departures/zipper-departures-iterator.js.map +1 -0
  140. package/dist/gtfs-system.d.ts +31 -0
  141. package/dist/gtfs-system.js +86 -0
  142. package/dist/gtfs-system.js.map +1 -0
  143. package/dist/index.d.ts +6 -0
  144. package/dist/index.js +7 -0
  145. package/dist/index.js.map +1 -0
  146. package/dist/parser/gtfs-feed-parser.d.ts +18 -0
  147. package/dist/parser/gtfs-feed-parser.js +23 -0
  148. package/dist/parser/gtfs-feed-parser.js.map +1 -0
  149. package/dist/parser/index.d.ts +2 -0
  150. package/dist/parser/index.js +3 -0
  151. package/dist/parser/index.js.map +1 -0
  152. package/dist/parser/realtime/gtfs-realtime-data-parser.d.ts +11 -0
  153. package/dist/parser/realtime/gtfs-realtime-data-parser.js +19 -0
  154. package/dist/parser/realtime/gtfs-realtime-data-parser.js.map +1 -0
  155. package/dist/parser/realtime/gtfs-trip-update-parser.d.ts +99 -0
  156. package/dist/parser/realtime/gtfs-trip-update-parser.js +335 -0
  157. package/dist/parser/realtime/gtfs-trip-update-parser.js.map +1 -0
  158. package/dist/parser/realtime/gtfs-trip-update-trip-identifier.d.ts +35 -0
  159. package/dist/parser/realtime/gtfs-trip-update-trip-identifier.js +95 -0
  160. package/dist/parser/realtime/gtfs-trip-update-trip-identifier.js.map +1 -0
  161. package/dist/parser/realtime/index.d.ts +3 -0
  162. package/dist/parser/realtime/index.js +2 -0
  163. package/dist/parser/realtime/index.js.map +1 -0
  164. package/dist/parser/schedule/gtfs-calendar-parser.d.ts +28 -0
  165. package/dist/parser/schedule/gtfs-calendar-parser.js +113 -0
  166. package/dist/parser/schedule/gtfs-calendar-parser.js.map +1 -0
  167. package/dist/parser/schedule/gtfs-route-matcher.d.ts +48 -0
  168. package/dist/parser/schedule/gtfs-route-matcher.js +199 -0
  169. package/dist/parser/schedule/gtfs-route-matcher.js.map +1 -0
  170. package/dist/parser/schedule/gtfs-schedule-parser.d.ts +15 -0
  171. package/dist/parser/schedule/gtfs-schedule-parser.js +19 -0
  172. package/dist/parser/schedule/gtfs-schedule-parser.js.map +1 -0
  173. package/dist/parser/schedule/gtfs-stop-time-normaliser.d.ts +47 -0
  174. package/dist/parser/schedule/gtfs-stop-time-normaliser.js +119 -0
  175. package/dist/parser/schedule/gtfs-stop-time-normaliser.js.map +1 -0
  176. package/dist/parser/schedule/gtfs-transfer-connector.d.ts +52 -0
  177. package/dist/parser/schedule/gtfs-transfer-connector.js +168 -0
  178. package/dist/parser/schedule/gtfs-transfer-connector.js.map +1 -0
  179. package/dist/parser/schedule/gtfs-trip-parser.d.ts +45 -0
  180. package/dist/parser/schedule/gtfs-trip-parser.js +169 -0
  181. package/dist/parser/schedule/gtfs-trip-parser.js.map +1 -0
  182. package/dist/parser/schedule/index.d.ts +6 -0
  183. package/dist/parser/schedule/index.js +2 -0
  184. package/dist/parser/schedule/index.js.map +1 -0
  185. package/package.json +3 -3
@@ -0,0 +1,30 @@
1
+ import { DeparturesIterator, type DeparturesIteratorResult } from "./departures-iterator.js";
2
+ import type { GtfsScheduledMovementsIndex } from "./gtfs-scheduled-movements-index.js";
3
+ import type { GtfsRealtimeData } from "../data/gtfs-realtime-data.js";
4
+ import { ScheduledDeparturesBlocksBuilder } from "./scheduled-departures-blocks-builder.js";
5
+ import type { TimezoneData } from "../config/timezone-data.js";
6
+ import type { DeparturesSearchDirection } from "../corequery-types.js";
7
+ export declare class ScheduledDeparturesIterator extends DeparturesIterator {
8
+ private readonly _blockBuilder;
9
+ private readonly _realtimeData;
10
+ private _direction;
11
+ private _searchRange;
12
+ private _iterators;
13
+ private _nextIterator;
14
+ private _blockSearchesRan;
15
+ private _subiteratorsCreated;
16
+ constructor(_blockBuilder: ScheduledDeparturesBlocksBuilder, _realtimeData: GtfsRealtimeData);
17
+ static tryBuild(stopId: number, scheduledMovementsIndex: GtfsScheduledMovementsIndex, realtimeData: GtfsRealtimeData, timezoneData: TimezoneData): ScheduledDeparturesIterator | null;
18
+ getStats(): {
19
+ blockSearchesRan: number;
20
+ subiteratorsCreated: number;
21
+ };
22
+ set(instant: Temporal.Instant, direction: DeparturesSearchDirection): void;
23
+ peek(): DeparturesIteratorResult | null;
24
+ take(): DeparturesIteratorResult;
25
+ private _determineIteratorWithNextValue;
26
+ private _updateIteratorsForSearchRange;
27
+ private _getBestOfCurrentIterators;
28
+ private _isBetter;
29
+ private _areMoreBlocksAvailable;
30
+ }
@@ -0,0 +1,204 @@
1
+ import { assertNever, removeIf } from "@dan-schel/js-utils";
2
+ import { DeparturesIterator, } from "./departures-iterator.js";
3
+ import { BoundedInstantRange } from "../data/bounded-instant-range.js";
4
+ import { ScheduledDeparturesBlockIterator } from "./scheduled-departures-block-iterator.js";
5
+ import { ScheduledDeparturesBlocksBuilder } from "./scheduled-departures-blocks-builder.js";
6
+ const BLOCK_SCAN_HRS = 48;
7
+ export class ScheduledDeparturesIterator extends DeparturesIterator {
8
+ _blockBuilder;
9
+ _realtimeData;
10
+ _direction;
11
+ _searchRange;
12
+ _iterators;
13
+ _nextIterator;
14
+ _blockSearchesRan = 0;
15
+ _subiteratorsCreated = 0;
16
+ constructor(_blockBuilder, _realtimeData) {
17
+ super();
18
+ this._blockBuilder = _blockBuilder;
19
+ this._realtimeData = _realtimeData;
20
+ this._direction = "forwards";
21
+ this._searchRange = null;
22
+ this._iterators = [];
23
+ this._nextIterator = null;
24
+ }
25
+ static tryBuild(stopId, scheduledMovementsIndex, realtimeData, timezoneData) {
26
+ const blockBuilder = ScheduledDeparturesBlocksBuilder.tryBuild(stopId, scheduledMovementsIndex, timezoneData);
27
+ if (blockBuilder == null)
28
+ return null;
29
+ return new ScheduledDeparturesIterator(blockBuilder, realtimeData);
30
+ }
31
+ getStats() {
32
+ return {
33
+ blockSearchesRan: this._blockSearchesRan,
34
+ subiteratorsCreated: this._subiteratorsCreated,
35
+ };
36
+ }
37
+ set(instant, direction) {
38
+ this._direction = direction;
39
+ this._searchRange = SearchRange.create(instant, direction, BLOCK_SCAN_HRS).optimize(this._blockBuilder);
40
+ this._iterators = [];
41
+ this._nextIterator = null;
42
+ this._updateIteratorsForSearchRange();
43
+ this._determineIteratorWithNextValue();
44
+ }
45
+ peek() {
46
+ return this._nextIterator?.peek() ?? null;
47
+ }
48
+ take() {
49
+ const iterator = this._nextIterator;
50
+ if (iterator == null)
51
+ throw new Error("Nothing to take.");
52
+ const value = iterator.take();
53
+ this._determineIteratorWithNextValue();
54
+ return value;
55
+ }
56
+ _determineIteratorWithNextValue() {
57
+ if (this._searchRange == null)
58
+ throw new Error("Search range not set.");
59
+ let bestIterator = this._getBestOfCurrentIterators();
60
+ let bestValue = bestIterator?.peek() ?? null;
61
+ // TODO: Allow the maximum number of iterations to be configurable. Even
62
+ // though I've gone to great effort to make an algorithm which gives
63
+ // theoretically correct results and stops once all calendars are exhausted,
64
+ // reality can be messy, and there could be giant gaps between services
65
+ // matching certain criteria (e.g. terminating in an unusual location), and
66
+ // we probably don't want to make it super easy to DDOS the server by making
67
+ // it perform searches which require it to search through several months of
68
+ // blocks. Maybe instead of a max number of iterations, we could do a max
69
+ // number of days from the start time or something (effectively the same
70
+ // thing, but without relying on the value of BLOCK_SCAN_HRS).
71
+ while ((bestValue == null || !this._searchRange.includes(bestValue.instant)) &&
72
+ this._areMoreBlocksAvailable()) {
73
+ this._searchRange = this._searchRange.getNextWithDuration(BLOCK_SCAN_HRS);
74
+ this._updateIteratorsForSearchRange();
75
+ bestIterator = this._getBestOfCurrentIterators();
76
+ bestValue = bestIterator?.peek() ?? null;
77
+ }
78
+ this._nextIterator = bestIterator;
79
+ }
80
+ _updateIteratorsForSearchRange() {
81
+ if (this._searchRange == null)
82
+ throw new Error("Search range not set.");
83
+ this._blockSearchesRan++;
84
+ const blocks = this._blockBuilder.allWithinTimeRange(this._searchRange.range);
85
+ for (const block of blocks) {
86
+ const alreadyIteratingThisBlock = this._iterators.some((i) => i.block.isSameServiceDay(block));
87
+ if (!alreadyIteratingThisBlock) {
88
+ this._subiteratorsCreated++;
89
+ const rtData = this._realtimeData;
90
+ const iterator = new ScheduledDeparturesBlockIterator(block, rtData);
91
+ iterator.set(this._searchRange.front, this._direction);
92
+ this._iterators.push(iterator);
93
+ }
94
+ }
95
+ removeIf(this._iterators, (iterator) => iterator.peek() == null);
96
+ }
97
+ _getBestOfCurrentIterators() {
98
+ let best = null;
99
+ let bestIterator = null;
100
+ for (const iterator of this._iterators) {
101
+ const nextValue = iterator.peek();
102
+ if (nextValue == null)
103
+ continue;
104
+ if (best == null || this._isBetter(best.instant, nextValue.instant)) {
105
+ best = nextValue;
106
+ bestIterator = iterator;
107
+ }
108
+ }
109
+ return bestIterator;
110
+ }
111
+ _isBetter(currentBest, candidate) {
112
+ if (this._direction === "forwards") {
113
+ return Temporal.Instant.compare(candidate, currentBest) < 0;
114
+ }
115
+ else if (this._direction === "backwards") {
116
+ return Temporal.Instant.compare(candidate, currentBest) > 0;
117
+ }
118
+ else {
119
+ assertNever(this._direction);
120
+ }
121
+ }
122
+ _areMoreBlocksAvailable() {
123
+ if (this._searchRange == null)
124
+ throw new Error("Search range not set.");
125
+ if (this._direction === "forwards") {
126
+ return this._blockBuilder.hasMoreAfter(this._searchRange.back);
127
+ }
128
+ else if (this._direction === "backwards") {
129
+ return this._blockBuilder.hasMoreBefore(this._searchRange.back);
130
+ }
131
+ else {
132
+ assertNever(this._direction);
133
+ }
134
+ }
135
+ }
136
+ class SearchRange {
137
+ range;
138
+ direction;
139
+ constructor(range, direction) {
140
+ this.range = range;
141
+ this.direction = direction;
142
+ }
143
+ get front() {
144
+ return {
145
+ forwards: this.range.start,
146
+ backwards: this.range.end,
147
+ }[this.direction];
148
+ }
149
+ get back() {
150
+ return {
151
+ forwards: this.range.end,
152
+ backwards: this.range.start,
153
+ }[this.direction];
154
+ }
155
+ get durationHours() {
156
+ const startMillis = this.range.start.epochMilliseconds;
157
+ const endMillis = this.range.end.epochMilliseconds;
158
+ const durationMillis = endMillis - startMillis;
159
+ return durationMillis / (1000 * 60 * 60);
160
+ }
161
+ getNextWithDuration(durationHours) {
162
+ return SearchRange.create(this.back, this.direction, durationHours);
163
+ }
164
+ static create(instant, direction, durationHours) {
165
+ const start = {
166
+ forwards: instant,
167
+ backwards: instant.subtract({ hours: durationHours }),
168
+ }[direction];
169
+ const end = {
170
+ forwards: instant.add({ hours: durationHours }),
171
+ backwards: instant,
172
+ }[direction];
173
+ return new SearchRange(new BoundedInstantRange(start, end), direction);
174
+ }
175
+ includes(instant) {
176
+ return this.range.includes(instant);
177
+ }
178
+ /**
179
+ * If the search range is beginning before the earliest movement (or after the
180
+ * latest if the direction is backwards) then we can optimise our search by
181
+ * pushing the range forward/backward so the first search occurs within the
182
+ * range of movements we have.
183
+ *
184
+ * This means someone searching for all departures from the year 1900 doesn't
185
+ * crash the server as it patiently searches through 120+ years of blocks,
186
+ * finding that none of them have any departures for those service days!
187
+ */
188
+ optimize(blocks) {
189
+ const earliest = blocks.earliestPossibleMovementInstant;
190
+ const latest = blocks.latestPossibleMovementInstant;
191
+ if (this.direction === "forwards" &&
192
+ earliest != null &&
193
+ Temporal.Instant.compare(earliest, this.range.start) > 0) {
194
+ return SearchRange.create(earliest, this.direction, this.durationHours);
195
+ }
196
+ if (this.direction === "backwards" &&
197
+ latest != null &&
198
+ Temporal.Instant.compare(latest, this.range.end) < 0) {
199
+ return SearchRange.create(latest, this.direction, this.durationHours);
200
+ }
201
+ return this;
202
+ }
203
+ }
204
+ //# sourceMappingURL=scheduled-departures-iterator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduled-departures-iterator.js","sourceRoot":"","sources":["../../src/departures/scheduled-departures-iterator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { DeparturesIterator, DeparturesIteratorResult } from "./departures-iterator.js";
2
+ import type { GtfsScheduledMovementsIndex } from "./gtfs-scheduled-movements-index.js";
3
+ import type { GtfsRealtimeData } from "../data/gtfs-realtime-data.js";
4
+ import type { TimezoneData } from "../config/timezone-data.js";
5
+ import type { DeparturesSearchDirection } from "../corequery-types.js";
6
+ export declare class ZipperDeparturesIterator extends DeparturesIterator {
7
+ private readonly _iterators;
8
+ private _direction;
9
+ private _nextIterator;
10
+ constructor(_iterators: DeparturesIterator[]);
11
+ set(instant: Temporal.Instant, direction: DeparturesSearchDirection): void;
12
+ peek(): DeparturesIteratorResult | null;
13
+ peekAtIterator(): DeparturesIterator | null;
14
+ take(): DeparturesIteratorResult;
15
+ private _determineBestIterator;
16
+ private _isBetter;
17
+ static forFeed(stopId: number, scheduledMovementsIndex: GtfsScheduledMovementsIndex, realtimeData: GtfsRealtimeData, timezoneData: TimezoneData): ZipperDeparturesIterator;
18
+ }
@@ -0,0 +1,68 @@
1
+ import { assertNever, nonNull } from "@dan-schel/js-utils";
2
+ import { DeparturesIterator, DeparturesIteratorResult, } from "./departures-iterator.js";
3
+ import { ScheduledDeparturesIterator } from "./scheduled-departures-iterator.js";
4
+ import { RealtimeDeparturesBlockIterator } from "./realtime-departures-block-iterator.js";
5
+ export class ZipperDeparturesIterator extends DeparturesIterator {
6
+ _iterators;
7
+ _direction;
8
+ _nextIterator;
9
+ constructor(_iterators) {
10
+ super();
11
+ this._iterators = _iterators;
12
+ this._direction = "forwards";
13
+ this._nextIterator = null;
14
+ }
15
+ set(instant, direction) {
16
+ this._direction = direction;
17
+ for (const iterator of this._iterators) {
18
+ iterator.set(instant, direction);
19
+ }
20
+ this._nextIterator = this._determineBestIterator();
21
+ }
22
+ peek() {
23
+ return this._nextIterator?.peek() ?? null;
24
+ }
25
+ peekAtIterator() {
26
+ return this._nextIterator;
27
+ }
28
+ take() {
29
+ const iterator = this._nextIterator;
30
+ if (iterator == null)
31
+ throw new Error("Nothing to take.");
32
+ const value = iterator.take();
33
+ this._nextIterator = this._determineBestIterator();
34
+ return value;
35
+ }
36
+ _determineBestIterator() {
37
+ let best = null;
38
+ let bestIterator = null;
39
+ for (const iterator of this._iterators) {
40
+ const nextValue = iterator.peek();
41
+ if (nextValue == null)
42
+ continue;
43
+ if (best == null || this._isBetter(best.instant, nextValue.instant)) {
44
+ best = nextValue;
45
+ bestIterator = iterator;
46
+ }
47
+ }
48
+ return bestIterator;
49
+ }
50
+ _isBetter(currentBest, candidate) {
51
+ if (this._direction === "forwards") {
52
+ return Temporal.Instant.compare(candidate, currentBest) < 0;
53
+ }
54
+ else if (this._direction === "backwards") {
55
+ return Temporal.Instant.compare(candidate, currentBest) > 0;
56
+ }
57
+ else {
58
+ assertNever(this._direction);
59
+ }
60
+ }
61
+ static forFeed(stopId, scheduledMovementsIndex, realtimeData, timezoneData) {
62
+ const scheduled = ScheduledDeparturesIterator.tryBuild(stopId, scheduledMovementsIndex, realtimeData, timezoneData);
63
+ const realtime = RealtimeDeparturesBlockIterator.tryBuild(stopId, realtimeData);
64
+ const iterators = [scheduled, realtime].filter(nonNull);
65
+ return new ZipperDeparturesIterator(iterators);
66
+ }
67
+ }
68
+ //# sourceMappingURL=zipper-departures-iterator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zipper-departures-iterator.js","sourceRoot":"","sources":["../../src/departures/zipper-departures-iterator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { GtfsConfig, TimezoneData } from "./config/index.js";
2
+ import type { GtfsFeed } from "./data/gtfs-feed.js";
3
+ import { LineGtfsIdMapping } from "./data/ids/line-gtfs-id-mapping.js";
4
+ import { StopGtfsIdMapping } from "./data/ids/stop-gtfs-id-mapping.js";
5
+ import { BonusLinesMapping } from "./data/route/bonus-lines-mapping.js";
6
+ import { LineRoutesMapping } from "./data/route/line-routes-mapping.js";
7
+ import type { GtfsScheduleParsingError } from "./parser/schedule/gtfs-schedule-parser.js";
8
+ import type { RealtimeDataJson } from "./data/raw/realtime-data-json.js";
9
+ import type { GtfsFeedCsv } from "./data/raw/schedule-csvs.js";
10
+ export declare class GtfsSystem {
11
+ private readonly _corequeryDataSourceId;
12
+ private readonly _lineGtfsIdMapping;
13
+ private readonly _stopGtfsIdMapping;
14
+ private readonly _lineRoutesMapping;
15
+ private readonly _bonusLinesMapping;
16
+ private readonly _timezoneData;
17
+ private readonly _parser;
18
+ private _feed;
19
+ private _scheduleParsingErrors;
20
+ private _realtimeParsingErrors;
21
+ constructor(_corequeryDataSourceId: string, _lineGtfsIdMapping: LineGtfsIdMapping, _stopGtfsIdMapping: StopGtfsIdMapping, _lineRoutesMapping: LineRoutesMapping, _bonusLinesMapping: BonusLinesMapping, _timezoneData: TimezoneData);
22
+ static build(corequeryDataSourceId: string, config: GtfsConfig): GtfsSystem;
23
+ getFeed(): GtfsFeed | null;
24
+ requireFeed(): GtfsFeed;
25
+ get realtimeParsingErrors(): import("./index.js").GtfsTripUpdateParsingError[];
26
+ get scheduleParsingErrors(): GtfsScheduleParsingError[];
27
+ onNewScheduleData(scheduleCsvs: GtfsFeedCsv, realtimeJson: RealtimeDataJson): void;
28
+ onNewRealtimeData(realtimeJson: RealtimeDataJson): void;
29
+ private _onScheduledParsingError;
30
+ private _onRealtimeParsingError;
31
+ }
@@ -0,0 +1,86 @@
1
+ import { LineGtfsIdMapping } from "./data/ids/line-gtfs-id-mapping.js";
2
+ import { StopGtfsIdMapping } from "./data/ids/stop-gtfs-id-mapping.js";
3
+ import { BonusLinesMapping } from "./data/route/bonus-lines-mapping.js";
4
+ import { LineRoutesMapping } from "./data/route/line-routes-mapping.js";
5
+ import { GtfsFeedParser } from "./parser/gtfs-feed-parser.js";
6
+ export class GtfsSystem {
7
+ _corequeryDataSourceId;
8
+ _lineGtfsIdMapping;
9
+ _stopGtfsIdMapping;
10
+ _lineRoutesMapping;
11
+ _bonusLinesMapping;
12
+ _timezoneData;
13
+ // TODO: The parsers are designed assuming that the same instance will be used
14
+ // to parse multiple feeds. I'm now thinking that this GtfsSystem class should
15
+ // house the feed, but only represent one GTFS feed so that it can be a
16
+ // departure source for Corequery just like any other, e.g. Corequery would
17
+ // have departure sources:
18
+ //
19
+ // - GTFS regional
20
+ // - GTFS suburban
21
+ // - PTV API
22
+ // - Static timetables
23
+ //
24
+ // And it would all be zipped together at the Corequery level. The advantage
25
+ // is that we don't have to track subfeed IDs within the GTFS systems, and we
26
+ // can say within Corequery itself that every service is uniquely identified
27
+ // by a source ID and service ID within that source.
28
+ //
29
+ // This is a long winded way of saying that I think GtfsFeedParser will
30
+ // probably only be used for one feed, and I should check if there's any
31
+ // reason I decided to make it reusable for multiple feeds (some expensive
32
+ // setup that I wanted to avoid repeating?).
33
+ _parser;
34
+ _feed;
35
+ _scheduleParsingErrors;
36
+ _realtimeParsingErrors;
37
+ constructor(_corequeryDataSourceId, _lineGtfsIdMapping, _stopGtfsIdMapping, _lineRoutesMapping, _bonusLinesMapping, _timezoneData) {
38
+ this._corequeryDataSourceId = _corequeryDataSourceId;
39
+ this._lineGtfsIdMapping = _lineGtfsIdMapping;
40
+ this._stopGtfsIdMapping = _stopGtfsIdMapping;
41
+ this._lineRoutesMapping = _lineRoutesMapping;
42
+ this._bonusLinesMapping = _bonusLinesMapping;
43
+ this._timezoneData = _timezoneData;
44
+ this._parser = new GtfsFeedParser(this._lineRoutesMapping, this._bonusLinesMapping, this._timezoneData, (error) => this._onScheduledParsingError(error), (error) => this._onRealtimeParsingError(error));
45
+ this._feed = null;
46
+ this._scheduleParsingErrors = [];
47
+ this._realtimeParsingErrors = [];
48
+ }
49
+ static build(corequeryDataSourceId, config) {
50
+ return new GtfsSystem(corequeryDataSourceId, LineGtfsIdMapping.build(config.lineGtfsIds), StopGtfsIdMapping.build(config.stopGtfsIds), LineRoutesMapping.build(config.lineRoutesMapping), BonusLinesMapping.build(config.bonusLinesMapping ?? {}), config.timezoneData);
51
+ }
52
+ getFeed() {
53
+ return this._feed;
54
+ }
55
+ requireFeed() {
56
+ if (this._feed == null)
57
+ throw new Error("No feed parsed yet.");
58
+ return this._feed;
59
+ }
60
+ get realtimeParsingErrors() {
61
+ return this._realtimeParsingErrors;
62
+ }
63
+ get scheduleParsingErrors() {
64
+ return this._scheduleParsingErrors;
65
+ }
66
+ onNewScheduleData(scheduleCsvs, realtimeJson) {
67
+ this._scheduleParsingErrors = [];
68
+ this._realtimeParsingErrors = [];
69
+ const result = this._parser.parse(this._corequeryDataSourceId, scheduleCsvs, realtimeJson, this._lineGtfsIdMapping, this._stopGtfsIdMapping);
70
+ this._feed = result;
71
+ }
72
+ onNewRealtimeData(realtimeJson) {
73
+ if (this._feed == null)
74
+ throw new Error("No schedule data parsed yet.");
75
+ this._realtimeParsingErrors = [];
76
+ const result = this._parser.updateWithNewRealtimeData(this._feed, realtimeJson, this._stopGtfsIdMapping);
77
+ this._feed = result;
78
+ }
79
+ _onScheduledParsingError(error) {
80
+ this._scheduleParsingErrors.push(error);
81
+ }
82
+ _onRealtimeParsingError(error) {
83
+ this._realtimeParsingErrors.push(error);
84
+ }
85
+ }
86
+ //# sourceMappingURL=gtfs-system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gtfs-system.js","sourceRoot":"","sources":["../src/gtfs-system.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import "temporal-polyfill/global";
2
+ export * from "./config/index.js";
3
+ export * from "./data/index.js";
4
+ export * from "./departures/index.js";
5
+ export * from "./parser/index.js";
6
+ export * from "./gtfs-system.js";
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import "temporal-polyfill/global";
2
+ export * from "./config/index.js";
3
+ export * from "./data/index.js";
4
+ export * from "./departures/index.js";
5
+ export * from "./parser/index.js";
6
+ export * from "./gtfs-system.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,0BAA0B,CAAC;AAElC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAElC,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { GtfsFeed } from "../data/gtfs-feed.js";
2
+ import type { LineGtfsIdMapping } from "../data/ids/line-gtfs-id-mapping.js";
3
+ import type { StopGtfsIdMapping } from "../data/ids/stop-gtfs-id-mapping.js";
4
+ import type { BonusLinesMapping } from "../data/route/bonus-lines-mapping.js";
5
+ import type { LineRoutesMapping } from "../data/route/line-routes-mapping.js";
6
+ import type { RealtimeDataJson } from "../data/raw/realtime-data-json.js";
7
+ import { type GtfsRealtimeDataParsingError } from "./realtime/gtfs-realtime-data-parser.js";
8
+ import { type GtfsScheduleParsingError } from "./schedule/gtfs-schedule-parser.js";
9
+ import type { GtfsFeedCsv } from "../data/raw/schedule-csvs.js";
10
+ import type { TimezoneData } from "../config/timezone-data.js";
11
+ export declare class GtfsFeedParser {
12
+ private readonly _timezoneData;
13
+ private readonly _scheduleParser;
14
+ private readonly _realtimeParser;
15
+ constructor(lineRoutesMapping: LineRoutesMapping, bonusLinesMapping: BonusLinesMapping, _timezoneData: TimezoneData, _onScheduleParsingError: (error: GtfsScheduleParsingError) => void, _onRealtimeParsingError: (error: GtfsRealtimeDataParsingError) => void);
16
+ parse(corequeryDataSourceId: string, scheduleCsvs: GtfsFeedCsv, realtimeJson: RealtimeDataJson, lineGtfsIdMapping: LineGtfsIdMapping, stopGtfsIdMapping: StopGtfsIdMapping): GtfsFeed;
17
+ updateWithNewRealtimeData(gtfsFeed: GtfsFeed, realtimeData: RealtimeDataJson, stopGtfsIdMapping: StopGtfsIdMapping): GtfsFeed;
18
+ }
@@ -0,0 +1,23 @@
1
+ import { GtfsFeed } from "../data/gtfs-feed.js";
2
+ import { GtfsRealtimeDataParser, } from "./realtime/gtfs-realtime-data-parser.js";
3
+ import { GtfsScheduleParser, } from "./schedule/gtfs-schedule-parser.js";
4
+ export class GtfsFeedParser {
5
+ _timezoneData;
6
+ _scheduleParser;
7
+ _realtimeParser;
8
+ constructor(lineRoutesMapping, bonusLinesMapping, _timezoneData, _onScheduleParsingError, _onRealtimeParsingError) {
9
+ this._timezoneData = _timezoneData;
10
+ this._scheduleParser = new GtfsScheduleParser(lineRoutesMapping, bonusLinesMapping, _onScheduleParsingError);
11
+ this._realtimeParser = new GtfsRealtimeDataParser(_timezoneData.timezone, _onRealtimeParsingError);
12
+ }
13
+ parse(corequeryDataSourceId, scheduleCsvs, realtimeJson, lineGtfsIdMapping, stopGtfsIdMapping) {
14
+ const scheduleData = this._scheduleParser.parse(scheduleCsvs, lineGtfsIdMapping, stopGtfsIdMapping);
15
+ const realtimeData = this._realtimeParser.parse(realtimeJson, scheduleData, stopGtfsIdMapping);
16
+ return GtfsFeed.fromNewScheduleData(corequeryDataSourceId, scheduleData, realtimeData, this._timezoneData);
17
+ }
18
+ updateWithNewRealtimeData(gtfsFeed, realtimeData, stopGtfsIdMapping) {
19
+ const updatedRealtime = this._realtimeParser.parse(realtimeData, gtfsFeed.scheduleData, stopGtfsIdMapping);
20
+ return gtfsFeed.withUpdatedRealtimeData(updatedRealtime);
21
+ }
22
+ }
23
+ //# sourceMappingURL=gtfs-feed-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gtfs-feed-parser.js","sourceRoot":"","sources":["../../src/parser/gtfs-feed-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAMhD,OAAO,EACL,sBAAsB,GAEvB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,kBAAkB,GAEnB,MAAM,oCAAoC,CAAC;AAI5C,MAAM,OAAO,cAAc;IAON;IANF,eAAe,CAAqB;IACpC,eAAe,CAAyB;IAEzD,YACE,iBAAoC,EACpC,iBAAoC,EACnB,aAA2B,EAC5C,uBAAkE,EAClE,uBAAsE;QAFrD,kBAAa,GAAb,aAAa,CAAc;QAI5C,IAAI,CAAC,eAAe,GAAG,IAAI,kBAAkB,CAC3C,iBAAiB,EACjB,iBAAiB,EACjB,uBAAuB,CACxB,CAAC;QACF,IAAI,CAAC,eAAe,GAAG,IAAI,sBAAsB,CAC/C,aAAa,CAAC,QAAQ,EACtB,uBAAuB,CACxB,CAAC;IACJ,CAAC;IAED,KAAK,CACH,qBAA6B,EAC7B,YAAyB,EACzB,YAA8B,EAC9B,iBAAoC,EACpC,iBAAoC;QAEpC,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,CAC7C,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,CAClB,CAAC;QACF,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,CAC7C,YAAY,EACZ,YAAY,EACZ,iBAAiB,CAClB,CAAC;QAEF,OAAO,QAAQ,CAAC,mBAAmB,CACjC,qBAAqB,EACrB,YAAY,EACZ,YAAY,EACZ,IAAI,CAAC,aAAa,CACnB,CAAC;IACJ,CAAC;IAED,yBAAyB,CACvB,QAAkB,EAClB,YAA8B,EAC9B,iBAAoC;QAEpC,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,CAChD,YAAY,EACZ,QAAQ,CAAC,YAAY,EACrB,iBAAiB,CAClB,CAAC;QAEF,OAAO,QAAQ,CAAC,uBAAuB,CAAC,eAAe,CAAC,CAAC;IAC3D,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export * from "./realtime/index.js";
2
+ export * from "./schedule/index.js";
@@ -0,0 +1,3 @@
1
+ export * from "./realtime/index.js";
2
+ export * from "./schedule/index.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { GtfsRealtimeData } from "../../data/gtfs-realtime-data.js";
2
+ import type { GtfsScheduleData } from "../../data/gtfs-schedule-data.js";
3
+ import type { StopGtfsIdMapping } from "../../data/ids/stop-gtfs-id-mapping.js";
4
+ import type { RealtimeDataJson } from "../../data/raw/realtime-data-json.js";
5
+ import { type GtfsTripUpdateParsingError } from "./gtfs-trip-update-parser.js";
6
+ export declare class GtfsRealtimeDataParser {
7
+ private readonly _tripUpdateParser;
8
+ constructor(timezone: string, onError: (error: GtfsRealtimeDataParsingError) => void);
9
+ parse(realtimeData: RealtimeDataJson, scheduleData: GtfsScheduleData, stopGtfsIdMapping: StopGtfsIdMapping): GtfsRealtimeData;
10
+ }
11
+ export type GtfsRealtimeDataParsingError = GtfsTripUpdateParsingError;
@@ -0,0 +1,19 @@
1
+ import { GtfsRealtimeData } from "../../data/gtfs-realtime-data.js";
2
+ import { GtfsTripUpdateParser, } from "./gtfs-trip-update-parser.js";
3
+ export class GtfsRealtimeDataParser {
4
+ _tripUpdateParser;
5
+ constructor(timezone, onError) {
6
+ this._tripUpdateParser = new GtfsTripUpdateParser(timezone, onError);
7
+ }
8
+ parse(realtimeData, scheduleData, stopGtfsIdMapping) {
9
+ const updatedTrips = [];
10
+ for (const tripUpdates of realtimeData.tripUpdates) {
11
+ const result = this._tripUpdateParser.parse(tripUpdates, scheduleData, stopGtfsIdMapping);
12
+ if (result != null) {
13
+ updatedTrips.push(result);
14
+ }
15
+ }
16
+ return new GtfsRealtimeData(updatedTrips);
17
+ }
18
+ }
19
+ //# sourceMappingURL=gtfs-realtime-data-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gtfs-realtime-data-parser.js","sourceRoot":"","sources":["../../../src/parser/realtime/gtfs-realtime-data-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAKpE,OAAO,EACL,oBAAoB,GAErB,MAAM,8BAA8B,CAAC;AAEtC,MAAM,OAAO,sBAAsB;IAChB,iBAAiB,CAAuB;IAEzD,YACE,QAAgB,EAChB,OAAsD;QAEtD,IAAI,CAAC,iBAAiB,GAAG,IAAI,oBAAoB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACvE,CAAC;IAED,KAAK,CACH,YAA8B,EAC9B,YAA8B,EAC9B,iBAAoC;QAEpC,MAAM,YAAY,GAAsB,EAAE,CAAC;QAE3C,KAAK,MAAM,WAAW,IAAI,YAAY,CAAC,WAAW,EAAE,CAAC;YACnD,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,CACzC,WAAW,EACX,YAAY,EACZ,iBAAiB,CAClB,CAAC;YAEF,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;gBACnB,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;QAED,OAAO,IAAI,gBAAgB,CAAC,YAAY,CAAC,CAAC;IAC5C,CAAC;CACF"}