vulture-wasm 0.2.0 → 0.4.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/README.md CHANGED
@@ -47,15 +47,16 @@ const tt = new VultureTimetable(
47
47
  - `tt.nStops()`, `tt.nRoutes()` — counts.
48
48
  - `tt.stopIdx(gtfsId)` — GTFS stop id → opaque `StopIdx`, or `undefined`.
49
49
  - `tt.stopName(stopIdx)`, `tt.stopCoords(stopIdx)`, `tt.routeName(routeIdx)`, `tt.routeAgency(routeIdx)` — display-data accessors.
50
- - `tt.allStops()` — bulk catalogue of `{idx, id, name, lat, lon}` per stop.
51
- - `tt.allRoutes()` — bulk catalogue of `{idx, id, name, agency, route_type, route_color}` per route. `route_type` is the GTFS integer (0=tram, 1=metro, 2=rail, 3=bus, 4=ferry, 5=cable, 6=aerial, 7=funicular, …); `route_color` is `"#rrggbb"` or `null`.
50
+ - `tt.allStops()` — bulk catalogue of `{idx, id, name, lat, lon, location_type, parent_station}` per stop. `location_type` is the GTFS integer (0 = stop or platform, 1 = parent station, 2 = entrance/exit, 3 = generic node, 4 = boarding area); `parent_station` is the parent's GTFS id or `null`.
51
+ - `tt.allRoutes()` — bulk catalogue of `{idx, id, name, agency, route_type, route_color}` per route. `route_type` is the GTFS integer (0 = tram, 1 = metro, 2 = rail, 3 = bus, 4 = ferry, 5 = cable, 6 = aerial, 7 = funicular, …); `route_color` is `"#rrggbb"` or `null`.
52
+ - `tt.stationStops(parentId)` — given a parent station's GTFS id, returns a `Uint32Array` of the platform stop indices that hang off it. Pass the result as `originStops` / `targetStops` to query against every platform of a station; queries rooted at a `location_type = 1` stop return zero journeys without this expansion, because in GTFS vehicles only board at platform-level stops.
52
53
  - `tt.withWalkingFootpaths(maxDistMeters, walkSpeedMetersPerSec)` — replace the footpath set with one derived from stop coordinates.
53
54
  - `tt.resetFootpaths()` — restore the original `transfers.txt` set.
54
55
 
55
56
  Free functions:
56
57
 
57
58
  - `runArrival(tt, originStops, targetStops, maxTransfers, departSeconds, requireWheelchair)` — single-departure query. Returns an array of journeys with timed legs.
58
- - `runRange(tt, origin, target, maxTransfers, departures, requireWheelchair)` — depart-in-window Pareto profile. Returns `[{depart, journey}]` where each `journey` has the same shape as a `runArrival` entry (`origin`, `target`, `arrival`, `legs`).
59
+ - `runRange(tt, originStops, targetStops, maxTransfers, departures, requireWheelchair)` — depart-in-window Pareto profile. Returns `[{depart, journey}]` where each `journey` has the same shape as a `runArrival` entry (`origin`, `target`, `arrival`, `legs`).
59
60
 
60
61
  Each leg in `journey.legs` is `{board_stop, alight_stop, route, trip, route_id, depart, arrive, shape}`. `shape` is a `[lat, lon][]` polyline for that leg's segment of the trip's `shapes.txt` geometry, or `null` if the feed has no shape for the trip.
61
62
 
@@ -132,7 +133,14 @@ const departures = new Uint32Array(
132
133
  Array.from({ length: 13 }, (_, i) => 9 * 3600 + i * 300),
133
134
  );
134
135
 
135
- const profile = runRange(tt, origin, target, 10, departures, false);
136
+ const profile = runRange(
137
+ tt,
138
+ new Uint32Array([origin]),
139
+ new Uint32Array([target]),
140
+ 10,
141
+ departures,
142
+ false,
143
+ );
136
144
 
137
145
  for (const entry of profile) {
138
146
  console.log(
@@ -175,6 +183,49 @@ console.log(`with footpaths: ${after.length} journey(s)`);
175
183
 
176
184
  `tt.resetFootpaths()` restores the original `transfers.txt` set.
177
185
 
186
+ ### 4. Querying entire stations (parent-station expansion)
187
+
188
+ GTFS feeds with deep station hierarchies (regional German rail, French SNCF, UK NaPTAN, etc.) model a single station as one parent stop (`location_type = 1`) with many child platforms (`location_type = 0`) hanging off it. Vehicles only board at platforms; querying directly against a parent station returns zero journeys. Detect parents and expand them into the platform set:
189
+
190
+ ```js
191
+ import init, { VultureTimetable, runArrival } from "vulture-wasm";
192
+
193
+ await init();
194
+
195
+ const tt = new VultureTimetable(
196
+ new Uint8Array(await (await fetch("/path/to/feed.zip")).arrayBuffer()),
197
+ "2026-05-04",
198
+ );
199
+
200
+ // Resolve a stop record (from `tt.allStops()` or any other lookup
201
+ // path) into the array of stop indices to query against.
202
+ function endpointsFor(stop) {
203
+ if (stop.location_type === 1) {
204
+ const platforms = tt.stationStops(stop.id);
205
+ if (platforms.length > 0) return platforms;
206
+ }
207
+ return new Uint32Array([stop.idx]);
208
+ }
209
+
210
+ const stops = tt.allStops();
211
+ const stopByName = new Map(stops.map((s) => [s.name, s]));
212
+
213
+ const from = stopByName.get("Karlsruhe Hauptbahnhof"); // a parent station
214
+ const to = stopByName.get("Basel SBB"); // also a parent
215
+
216
+ const journeys = runArrival(
217
+ tt,
218
+ endpointsFor(from),
219
+ endpointsFor(to),
220
+ /* maxTransfers */ 10,
221
+ /* depart */ 9 * 3600,
222
+ /* wheelchair */ false,
223
+ );
224
+ console.log(`${journeys.length} journey(s) across all platform combinations`);
225
+ ```
226
+
227
+ The same `endpointsFor(stop)` helper is what the bundled demo uses across all three panels.
228
+
178
229
  ## Build from source
179
230
 
180
231
  ```sh
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "Stephan Hügel <urschrei@gmail.com>"
6
6
  ],
7
7
  "description": "WebAssembly bindings for vulture (RAPTOR transit routing)",
8
- "version": "0.2.0",
8
+ "version": "0.4.0",
9
9
  "license": "Apache-2.0",
10
10
  "repository": {
11
11
  "type": "git",
package/vulture_wasm.d.ts CHANGED
@@ -58,6 +58,19 @@ export class VultureTimetable {
58
58
  * routes by stop-pattern.
59
59
  */
60
60
  routeName(idx: number): string | undefined;
61
+ /**
62
+ * Expand a parent station's GTFS id into the platform stop
63
+ * indices that hang off it. Returns an empty array if `parent_id`
64
+ * is not a parent station in the feed (or has no children).
65
+ *
66
+ * Useful for the "any platform of this station" pattern: in
67
+ * GTFS, vehicles only board at platform-level stops
68
+ * (`location_type = 0`), so a query rooted at the parent station
69
+ * (`location_type = 1`) returns nothing. Pass this method's
70
+ * result as `originStops` / `targetStops` to query against every
71
+ * platform.
72
+ */
73
+ stationStops(parent_id: string): Uint32Array;
61
74
  /**
62
75
  * Returns the GTFS `[lat, lon]` for the given stop index, or
63
76
  * `undefined` if the stop has no coordinates set.
@@ -94,15 +107,17 @@ export function runArrival(tt: VultureTimetable, origins: Uint32Array, targets:
94
107
 
95
108
  /**
96
109
  * Range query: run RAPTOR across a window of departure times. JS
97
- * passes `departures` as a `Uint32Array` of seconds-since-midnight
98
- * values. Returns `[{depart, journey}]` Pareto-filtered on
99
- * `(later depart, fewer transfers, earlier arrival)`.
110
+ * passes `origins` / `targets` as `Uint32Array`s of stop indices
111
+ * (each entry gets walk-time zero), and `departures` as a
112
+ * `Uint32Array` of seconds-since-midnight values. Returns
113
+ * `[{depart, journey}]` Pareto-filtered on `(later depart, fewer
114
+ * transfers, earlier arrival)`.
100
115
  *
101
- * Currently single-source / single-target only the demo's range
102
- * panel doesn't need multi-endpoint, and rRAPTOR's serial path
103
- * (the one we use here) requires `ArrivalTime`.
116
+ * Multi-source / multi-target so the "any platform of this station"
117
+ * pattern (via `tt.stationStops(parent_id)`) works the same way as
118
+ * in `runArrival`.
104
119
  */
105
- export function runRange(tt: VultureTimetable, origin: number, target: number, max_transfers: number, departures: Uint32Array, require_wheelchair: boolean): any;
120
+ export function runRange(tt: VultureTimetable, origins: Uint32Array, targets: Uint32Array, max_transfers: number, departures: Uint32Array, require_wheelchair: boolean): any;
106
121
 
107
122
  export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
108
123
 
@@ -110,7 +125,7 @@ export interface InitOutput {
110
125
  readonly memory: WebAssembly.Memory;
111
126
  readonly __wbg_vulturetimetable_free: (a: number, b: number) => void;
112
127
  readonly runArrival: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => [number, number, number];
113
- readonly runRange: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number];
128
+ readonly runRange: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => [number, number, number];
114
129
  readonly vulturetimetable_allRoutes: (a: number) => [number, number, number];
115
130
  readonly vulturetimetable_allStops: (a: number) => [number, number, number];
116
131
  readonly vulturetimetable_nRoutes: (a: number) => number;
@@ -119,6 +134,7 @@ export interface InitOutput {
119
134
  readonly vulturetimetable_resetFootpaths: (a: number) => [number, number];
120
135
  readonly vulturetimetable_routeAgency: (a: number, b: number) => [number, number];
121
136
  readonly vulturetimetable_routeName: (a: number, b: number) => [number, number];
137
+ readonly vulturetimetable_stationStops: (a: number, b: number, c: number) => [number, number];
122
138
  readonly vulturetimetable_stopCoords: (a: number, b: number) => [number, number];
123
139
  readonly vulturetimetable_stopIdx: (a: number, b: number, c: number) => number;
124
140
  readonly vulturetimetable_stopName: (a: number, b: number) => [number, number];
package/vulture_wasm.js CHANGED
@@ -126,6 +126,28 @@ export class VultureTimetable {
126
126
  }
127
127
  return v1;
128
128
  }
129
+ /**
130
+ * Expand a parent station's GTFS id into the platform stop
131
+ * indices that hang off it. Returns an empty array if `parent_id`
132
+ * is not a parent station in the feed (or has no children).
133
+ *
134
+ * Useful for the "any platform of this station" pattern: in
135
+ * GTFS, vehicles only board at platform-level stops
136
+ * (`location_type = 0`), so a query rooted at the parent station
137
+ * (`location_type = 1`) returns nothing. Pass this method's
138
+ * result as `originStops` / `targetStops` to query against every
139
+ * platform.
140
+ * @param {string} parent_id
141
+ * @returns {Uint32Array}
142
+ */
143
+ stationStops(parent_id) {
144
+ const ptr0 = passStringToWasm0(parent_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
145
+ const len0 = WASM_VECTOR_LEN;
146
+ const ret = wasm.vulturetimetable_stationStops(this.__wbg_ptr, ptr0, len0);
147
+ var v2 = getArrayU32FromWasm0(ret[0], ret[1]).slice();
148
+ wasm.__wbindgen_free(ret[0], ret[1] * 4, 4);
149
+ return v2;
150
+ }
129
151
  /**
130
152
  * Returns the GTFS `[lat, lon]` for the given stop index, or
131
153
  * `undefined` if the stop has no coordinates set.
@@ -212,26 +234,32 @@ export function runArrival(tt, origins, targets, max_transfers, depart, require_
212
234
 
213
235
  /**
214
236
  * Range query: run RAPTOR across a window of departure times. JS
215
- * passes `departures` as a `Uint32Array` of seconds-since-midnight
216
- * values. Returns `[{depart, journey}]` Pareto-filtered on
217
- * `(later depart, fewer transfers, earlier arrival)`.
237
+ * passes `origins` / `targets` as `Uint32Array`s of stop indices
238
+ * (each entry gets walk-time zero), and `departures` as a
239
+ * `Uint32Array` of seconds-since-midnight values. Returns
240
+ * `[{depart, journey}]` Pareto-filtered on `(later depart, fewer
241
+ * transfers, earlier arrival)`.
218
242
  *
219
- * Currently single-source / single-target only the demo's range
220
- * panel doesn't need multi-endpoint, and rRAPTOR's serial path
221
- * (the one we use here) requires `ArrivalTime`.
243
+ * Multi-source / multi-target so the "any platform of this station"
244
+ * pattern (via `tt.stationStops(parent_id)`) works the same way as
245
+ * in `runArrival`.
222
246
  * @param {VultureTimetable} tt
223
- * @param {number} origin
224
- * @param {number} target
247
+ * @param {Uint32Array} origins
248
+ * @param {Uint32Array} targets
225
249
  * @param {number} max_transfers
226
250
  * @param {Uint32Array} departures
227
251
  * @param {boolean} require_wheelchair
228
252
  * @returns {any}
229
253
  */
230
- export function runRange(tt, origin, target, max_transfers, departures, require_wheelchair) {
254
+ export function runRange(tt, origins, targets, max_transfers, departures, require_wheelchair) {
231
255
  _assertClass(tt, VultureTimetable);
232
- const ptr0 = passArray32ToWasm0(departures, wasm.__wbindgen_malloc);
256
+ const ptr0 = passArray32ToWasm0(origins, wasm.__wbindgen_malloc);
233
257
  const len0 = WASM_VECTOR_LEN;
234
- const ret = wasm.runRange(tt.__wbg_ptr, origin, target, max_transfers, ptr0, len0, require_wheelchair);
258
+ const ptr1 = passArray32ToWasm0(targets, wasm.__wbindgen_malloc);
259
+ const len1 = WASM_VECTOR_LEN;
260
+ const ptr2 = passArray32ToWasm0(departures, wasm.__wbindgen_malloc);
261
+ const len2 = WASM_VECTOR_LEN;
262
+ const ret = wasm.runRange(tt.__wbg_ptr, ptr0, len0, ptr1, len1, max_transfers, ptr2, len2, require_wheelchair);
235
263
  if (ret[2]) {
236
264
  throw takeFromExternrefTable0(ret[1]);
237
265
  }
@@ -343,6 +371,11 @@ function getArrayF64FromWasm0(ptr, len) {
343
371
  return getFloat64ArrayMemory0().subarray(ptr / 8, ptr / 8 + len);
344
372
  }
345
373
 
374
+ function getArrayU32FromWasm0(ptr, len) {
375
+ ptr = ptr >>> 0;
376
+ return getUint32ArrayMemory0().subarray(ptr / 4, ptr / 4 + len);
377
+ }
378
+
346
379
  let cachedDataViewMemory0 = null;
347
380
  function getDataViewMemory0() {
348
381
  if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
Binary file