gtfs-sqljs 0.1.0 → 0.2.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
@@ -1,9 +1,14 @@
1
1
  <div align="center">
2
2
  <img src="logo.svg" alt="gtfs-sqljs logo" width="200" height="200">
3
3
  <h1>gtfs-sqljs</h1>
4
- <p>A TypeScript library for loading GTFS (General Transit Feed Specification) data into a sql.js SQLite database for querying in both browser and Node.js environments.</p>
4
+
5
+ [![npm version](https://img.shields.io/npm/v/gtfs-sqljs)](https://www.npmjs.com/package/gtfs-sqljs)
6
+
7
+ <p>A TypeScript library for loading <a href="https://gtfs.org/documentation/schedule/reference/">GTFS</a> (General Transit Feed Specification) data into a <a href="https://sql.js.org/">sql.js</a> SQLite database for querying in both browser and Node.js environments.</p>
5
8
  </div>
6
9
 
10
+ > **[Live Demo](https://sysdevrun.github.io/gtfs-sqljs-demo/)** — A fully static demo website with GTFS and GTFS-RT data running in a Web Worker, with no backend.
11
+
7
12
  ## Author
8
13
 
9
14
  **Théophile Helleboid / SysDevRun**
@@ -11,130 +16,38 @@
11
16
  - Email: contact@sys-dev-run.fr
12
17
  - Website: https://www.sys-dev-run.fr/
13
18
 
14
- ## Documentation & Demo
19
+ This project is greatly inspired by [node-gtfs](https://github.com/BlinkTagInc/node-gtfs), also MIT licensed. The main difference is that gtfs-sqljs aims to run on both browser and Node.js environments.
15
20
 
16
- 📚 **[View Documentation and Interactive Demo](https://sysdevrun.github.io/gtfs-sqljs/)**
21
+ ## Documentation & Demo
17
22
 
18
- Try the live demo to explore GTFS data, view routes with colors, and see trip schedules in action!
23
+ - [Documentation and Interactive Demo](https://sysdevrun.github.io/gtfs-sqljs/)
24
+ - [Usage Guide](https://sysdevrun.github.io/gtfs-sqljs/docs/documents/Usage_Guide.html) — detailed examples for all features
25
+ - [API Reference](https://sysdevrun.github.io/gtfs-sqljs/docs/) — full TypeDoc-generated API docs
19
26
 
20
27
  ## Features
21
28
 
22
29
  ### GTFS Static Data
23
- - Load GTFS data from ZIP files (URL or local path)
24
- - **High-performance loading** - 7-8x faster than previous versions
25
- - **Progress tracking** - Real-time progress callbacks (0-100%)
26
- - Skip importing specific files (e.g., shapes.txt) to reduce memory usage
27
- - Load existing SQLite databases
28
- - Export databases to ArrayBuffer for persistence
29
- - Flexible filter-based query API - combine multiple filters easily
30
- - Agency query support with agency-based filtering
31
- - Full TypeScript support with comprehensive types
32
- - ✅ Works in both browser and Node.js
33
- - Efficient querying with indexed SQLite database
34
- - Proper handling of GTFS required/optional fields
35
- - Active service detection based on calendar/calendar_dates
36
- - Optimized bulk loading with transactions and batch inserts
37
-
38
- ### GTFS Realtime Support
39
- - ✅ Load GTFS-RT data from protobuf feeds (URLs or local files)
40
- - ✅ Support for Alerts, Trip Updates, and Vehicle Positions
41
- - ✅ Automatic staleness filtering (configurable threshold)
42
- - ✅ Active alert period checking
43
- - ✅ Merge realtime data with static schedules
44
- - ✅ Filter alerts and vehicle positions by route, stop, or trip
45
- - ✅ Store RT data in SQLite for consistent querying
46
- - ✅ Include RT data in database exports
47
- - ✅ Full support for both GTFS-RT `time` and `delay` fields in stop time updates
30
+ - Load GTFS data from ZIP files (URL or local path)
31
+ - **High-performance loading** with optimized bulk inserts
32
+ - **Progress tracking** - Real-time progress callbacks (0-100%)
33
+ - Skip importing specific files (e.g., shapes.txt) to reduce memory usage
34
+ - Load existing SQLite databases
35
+ - Export databases to ArrayBuffer for persistence
36
+ - Flexible filter-based query API - combine multiple filters easily
37
+ - Full TypeScript support with comprehensive types
38
+ - Works in both browser and Node.js
39
+
40
+ ### [GTFS Realtime](https://gtfs.org/documentation/realtime/reference/) Support
41
+ - Load GTFS-RT data from protobuf feeds (URLs or local files)
42
+ - Support for Alerts, Trip Updates, and Vehicle Positions
43
+ - Automatic staleness filtering (configurable threshold)
44
+ - Merge realtime data with static schedules
48
45
 
49
46
  ### Smart Caching
50
- - **Optional caching** - Copy cache implementations from `examples/cache/`
51
- - **Platform-specific stores** - IndexedDBCacheStore (browser) or FileSystemCacheStore (Node.js)
52
- - **Smart invalidation** - Based on file checksum, size, version, and library version
53
- - **Automatic expiration** - Configurable (default: 7 days)
54
- - ✅ **Cache management API** - Get stats, clean expired entries, clear cache
55
- - ✅ **Custom cache stores** - Implement your own (Redis, S3, etc.)
56
- - ✅ **Dramatic speed improvement** - Subsequent loads in <1 second
57
-
58
- ## Migration Guide (v1.0.0+)
59
-
60
- If you're upgrading from an earlier version, the API has been simplified and improved:
61
-
62
- ### Unified Filter-Based API
63
-
64
- All `getXXXByYYYY` methods have been removed in favor of unified `getXXXX` methods with optional filters. Filters now support both single values and arrays for maximum flexibility.
65
-
66
- **Migration examples:**
67
-
68
- ```typescript
69
- // ❌ Old API
70
- const stop = gtfs.getStopById('STOP_123');
71
- const route = gtfs.getRouteById('ROUTE_1');
72
- const trip = gtfs.getTripById('TRIP_123');
73
- const stopTimes = gtfs.getStopTimesByTrip('TRIP_123');
74
- const agency = gtfs.getAgencyById('AGENCY_1');
75
- const alert = gtfs.getAlertById('alert:123');
76
- const vehicle = gtfs.getVehiclePositionByTripId('TRIP_123');
77
-
78
- // ✅ New API
79
- const stops = gtfs.getStops({ stopId: 'STOP_123' });
80
- const stop = stops.length > 0 ? stops[0] : null;
81
-
82
- const routes = gtfs.getRoutes({ routeId: 'ROUTE_1' });
83
- const route = routes.length > 0 ? routes[0] : null;
84
-
85
- const trips = gtfs.getTrips({ tripId: 'TRIP_123' });
86
- const trip = trips.length > 0 ? trips[0] : null;
87
-
88
- const stopTimes = gtfs.getStopTimes({ tripId: 'TRIP_123' });
89
-
90
- const agencies = gtfs.getAgencies({ agencyId: 'AGENCY_1' });
91
- const agency = agencies.length > 0 ? agencies[0] : null;
92
-
93
- const alerts = gtfs.getAlerts({ alertId: 'alert:123' });
94
- const alert = alerts.length > 0 ? alerts[0] : null;
95
-
96
- const vehicles = gtfs.getVehiclePositions({ tripId: 'TRIP_123' });
97
- const vehicle = vehicles.length > 0 ? vehicles[0] : null;
98
- ```
99
-
100
- **Benefits of the new API:**
101
- - ✅ Support for filtering by multiple IDs at once (arrays)
102
- - ✅ More consistent API surface
103
- - ✅ Easier to combine multiple filters
104
- - ✅ Better TypeScript inference
105
-
106
- **Array filtering example:**
107
- ```typescript
108
- // Get multiple stops at once
109
- const stops = gtfs.getStops({ stopId: ['STOP_1', 'STOP_2', 'STOP_3'] });
110
-
111
- // Get trips for multiple routes
112
- const trips = gtfs.getTrips({ routeId: ['ROUTE_1', 'ROUTE_2'], date: '20240115' });
113
-
114
- // Get stop times for multiple trips
115
- const stopTimes = gtfs.getStopTimes({ tripId: ['TRIP_1', 'TRIP_2'] });
116
- ```
117
-
118
- ### GTFS-RT Time/Delay Support
119
-
120
- Stop time updates now include both `time` and `delay` fields:
121
-
122
- ```typescript
123
- const stopTimes = gtfs.getStopTimes({
124
- tripId: 'TRIP_123',
125
- includeRealtime: true
126
- });
127
-
128
- for (const st of stopTimes) {
129
- if (st.realtime) {
130
- // Both delay (relative to schedule) and time (absolute) are now available
131
- console.log('Arrival delay:', st.realtime.arrival_delay, 'seconds');
132
- console.log('Arrival time:', st.realtime.arrival_time, '(UNIX timestamp)');
133
- console.log('Departure delay:', st.realtime.departure_delay, 'seconds');
134
- console.log('Departure time:', st.realtime.departure_time, '(UNIX timestamp)');
135
- }
136
- }
137
- ```
47
+ - **Optional caching** - Copy cache implementations from `examples/cache/`
48
+ - **Platform-specific stores** - IndexedDBCacheStore (browser) or FileSystemCacheStore (Node.js)
49
+ - **Smart invalidation** - Based on file checksum, size, version, and library version
50
+ - **Dramatic speed improvement** - Subsequent loads in <1 second
138
51
 
139
52
  ## Installation
140
53
 
@@ -148,1013 +61,40 @@ You also need to install sql.js as a peer dependency:
148
61
  npm install sql.js
149
62
  ```
150
63
 
151
- ## Loading the sql.js WASM File
152
-
153
- sql.js requires a WASM file to be loaded. There are several ways to handle this:
154
-
155
- ### Node.js
156
-
157
- In Node.js, sql.js will automatically locate the WASM file from the installed package:
158
-
159
- ```typescript
160
- import { GtfsSqlJs } from 'gtfs-sqljs';
161
-
162
- // The WASM file is loaded automatically
163
- const gtfs = await GtfsSqlJs.fromZip('path/to/gtfs.zip');
164
- ```
165
-
166
- ### Browser with CDN
167
-
168
- You can use a CDN to serve the WASM file:
169
-
170
- ```typescript
171
- import initSqlJs from 'sql.js';
172
- import { GtfsSqlJs } from 'gtfs-sqljs';
173
-
174
- // Initialize sql.js with CDN WASM file
175
- const SQL = await initSqlJs({
176
- locateFile: (filename) => `https://sql.js.org/dist/${filename}`
177
- });
178
-
179
- // Pass the SQL instance to GtfsSqlJs
180
- const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip', { SQL });
181
- ```
182
-
183
- ### Browser with Bundler (Webpack, Vite, etc.)
184
-
185
- If you're using a bundler, you need to configure it to handle the WASM file:
186
-
187
- #### Vite
64
+ ## Quick Start
188
65
 
189
66
  ```typescript
190
- import initSqlJs from 'sql.js';
191
67
  import { GtfsSqlJs } from 'gtfs-sqljs';
192
- import sqlWasmUrl from 'sql.js/dist/sql-wasm.wasm?url';
193
68
 
194
- const SQL = await initSqlJs({
195
- locateFile: () => sqlWasmUrl
196
- });
197
-
198
- const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip', { SQL });
199
- ```
200
-
201
- #### Webpack
202
-
203
- ```typescript
204
- import initSqlJs from 'sql.js';
205
- import { GtfsSqlJs } from 'gtfs-sqljs';
206
-
207
- const SQL = await initSqlJs({
208
- locateFile: (filename) => `/path/to/public/${filename}`
209
- });
210
-
211
- const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip', { SQL });
212
- ```
213
-
214
- Make sure to copy `sql-wasm.wasm` from `node_modules/sql.js/dist/` to your public directory.
215
-
216
- ## Usage
217
-
218
- ### Creating an Instance
219
-
220
- #### From a GTFS ZIP file
221
-
222
- ```typescript
223
- import { GtfsSqlJs } from 'gtfs-sqljs';
224
-
225
- // From URL
69
+ // Load GTFS data from a ZIP file
226
70
  const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip');
227
71
 
228
- // From local file (Node.js)
229
- const gtfs = await GtfsSqlJs.fromZip('./path/to/gtfs.zip');
230
-
231
- // Skip importing specific files to reduce memory usage
232
- // Tables will be created but data won't be imported
233
- const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip', {
234
- skipFiles: ['shapes.txt', 'frequencies.txt']
235
- });
236
- ```
237
-
238
- #### From an existing SQLite database
239
-
240
- ```typescript
241
- import { GtfsSqlJs } from 'gtfs-sqljs';
242
-
243
- // Load from ArrayBuffer
244
- const dbBuffer = await fetch('https://example.com/gtfs.db').then(r => r.arrayBuffer());
245
- const gtfs = await GtfsSqlJs.fromDatabase(dbBuffer);
246
- ```
247
-
248
- ### Progress Tracking
249
-
250
- Track loading progress with a callback function - perfect for displaying progress bars or updating UI:
251
-
252
- ```typescript
253
- import { GtfsSqlJs, type ProgressInfo } from 'gtfs-sqljs';
254
-
255
- const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip', {
256
- onProgress: (progress: ProgressInfo) => {
257
- console.log(`${progress.percentComplete}% - ${progress.message}`);
258
-
259
- // Progress information available:
260
- console.log('Phase:', progress.phase); // Current phase
261
- console.log('File:', progress.currentFile); // Current file being processed
262
- console.log('Files:', progress.filesCompleted, '/', progress.totalFiles);
263
- console.log('Rows:', progress.rowsProcessed, '/', progress.totalRows);
264
- }
265
- });
266
- ```
267
-
268
- #### Progress Phases
269
-
270
- The loading process goes through these phases:
271
-
272
- 1. **`checking_cache`** - Checking if cached database exists (0%)
273
- 2. **`loading_from_cache`** - Loading from cache (if found, jumps to 100%)
274
- 3. **`downloading`** - Downloading GTFS ZIP file (1-30%)
275
- 4. **`extracting`** - Extracting GTFS ZIP file (35%)
276
- 5. **`creating_schema`** - Creating database tables (40%)
277
- 6. **`inserting_data`** - Importing data from CSV files (40-75%)
278
- 7. **`creating_indexes`** - Building database indexes (75-85%)
279
- 8. **`analyzing`** - Optimizing query performance (85-90%)
280
- 9. **`loading_realtime`** - Loading realtime data from feeds (90-95%) *(if configured)*
281
- 10. **`saving_cache`** - Saving to cache (95-98%)
282
- 11. **`complete`** - Load complete (100%)
283
-
284
- **Note:** When a cached database is found, phases 3-10 are skipped, and loading completes in <1 second.
285
-
286
- **Note:** The `loading_realtime` phase only occurs if `realtimeFeedUrls` are configured during initialization.
287
-
288
- #### Web Worker Example
289
-
290
- The progress callback is especially useful for web workers:
291
-
292
- ```typescript
293
- // In your web worker
294
- import { GtfsSqlJs } from 'gtfs-sqljs';
295
-
296
- self.onmessage = async (event) => {
297
- if (event.data.type === 'load') {
298
- const gtfs = await GtfsSqlJs.fromZip(event.data.url, {
299
- onProgress: (progress) => {
300
- // Send progress updates to main thread
301
- self.postMessage({
302
- type: 'progress',
303
- data: progress
304
- });
305
- }
306
- });
307
-
308
- self.postMessage({ type: 'complete' });
309
- }
310
- };
311
- ```
312
-
313
- ```typescript
314
- // In your main thread
315
- const worker = new Worker('gtfs-worker.js');
316
-
317
- worker.onmessage = (event) => {
318
- if (event.data.type === 'progress') {
319
- const progress = event.data.data;
320
- updateProgressBar(progress.percentComplete);
321
- updateStatusText(progress.message);
322
- }
323
- };
324
-
325
- worker.postMessage({ type: 'load', url: 'https://example.com/gtfs.zip' });
326
- ```
327
-
328
- #### ProgressInfo Type
329
-
330
- ```typescript
331
- interface ProgressInfo {
332
- phase: 'checking_cache' | 'loading_from_cache' | 'downloading' | 'extracting' |
333
- 'creating_schema' | 'inserting_data' | 'creating_indexes' | 'analyzing' |
334
- 'loading_realtime' | 'saving_cache' | 'complete';
335
- currentFile: string | null; // e.g., "stop_times.txt"
336
- filesCompleted: number; // Files processed so far
337
- totalFiles: number; // Total number of files
338
- rowsProcessed: number; // CSV rows imported so far
339
- totalRows: number; // Total CSV rows to import
340
- bytesDownloaded?: number; // Bytes downloaded (during 'downloading' phase)
341
- totalBytes?: number; // Total bytes to download (during 'downloading' phase)
342
- percentComplete: number; // 0-100
343
- message: string; // Human-readable status message
344
- }
345
- ```
346
-
347
- ### Querying Data
348
-
349
- The library provides two ways to query GTFS data:
350
- 1. **Flexible filter-based methods** (recommended) - Pass an object with optional filters
351
- 2. **Convenience methods** - Direct methods for common use cases
352
-
353
- #### Flexible Filter-Based Queries (Recommended)
354
-
355
- The new flexible API allows you to pass multiple optional filters in a single method call:
356
-
357
- ```typescript
358
- // Get stops - combine any filters
359
- const stops = gtfs.getStops({
360
- name: 'Station', // Search by name
361
- limit: 10 // Limit results
362
- });
363
-
364
- // Get routes - with or without filters
365
- const allRoutes = gtfs.getRoutes();
366
- const agencyRoutes = gtfs.getRoutes({ agencyId: 'AGENCY_1' });
367
-
368
- // Get trips - combine multiple filters
369
- const trips = gtfs.getTrips({
370
- routeId: 'ROUTE_1', // Filter by route
371
- date: '20240115', // Filter by date (gets active services)
372
- directionId: 0, // Filter by direction
373
- limit: 50 // Limit results
374
- });
375
-
376
- // Get stop times - flexible filtering
377
- const stopTimes = gtfs.getStopTimes({
378
- stopId: 'STOP_123', // At a specific stop
379
- routeId: 'ROUTE_1', // For a specific route
380
- date: '20240115', // On a specific date
381
- directionId: 0 // In a specific direction
382
- });
383
- ```
384
-
385
- **Available Filter Options:**
386
-
387
- - `getStops(filters?)`:
388
- - `stopId`: string - Filter by stop ID
389
- - `stopCode`: string - Filter by stop code
390
- - `name`: string - Search by stop name (partial match)
391
- - `tripId`: string - Get stops for a trip
392
- - `limit`: number - Limit results
393
-
394
- - `getRoutes(filters?)`:
395
- - `routeId`: string - Filter by route ID
396
- - `agencyId`: string - Filter by agency
397
- - `limit`: number - Limit results
398
-
399
- - `getTrips(filters?)`:
400
- - `tripId`: string - Filter by trip ID
401
- - `routeId`: string - Filter by route
402
- - `date`: string - Filter by date (YYYYMMDD format)
403
- - `directionId`: number - Filter by direction
404
- - `limit`: number - Limit results
405
-
406
- - `getStopTimes(filters?)`:
407
- - `tripId`: string - Filter by trip
408
- - `stopId`: string - Filter by stop
409
- - `routeId`: string - Filter by route
410
- - `date`: string - Filter by date (YYYYMMDD format)
411
- - `directionId`: number - Filter by direction
412
- - `limit`: number - Limit results
413
-
414
- - `getShapes(filters?)`:
415
- - `shapeId`: string | string[] - Filter by shape ID
416
- - `routeId`: string | string[] - Filter by route (via trips table)
417
- - `tripId`: string | string[] - Filter by trip
418
- - `limit`: number - Limit results
419
-
420
- - `getShapesToGeojson(filters?, precision?)`:
421
- - Same filters as `getShapes`
422
- - `precision`: number - Decimal places for coordinates (default: 6)
423
-
424
- #### Get Stop Information
425
-
426
- ```typescript
427
- // Get stop by ID
428
- const stops = gtfs.getStops({ stopId: 'STOP_123' });
429
- const stop = stops.length > 0 ? stops[0] : null;
430
- console.log(stop?.stop_name);
431
-
432
- // Get stop by code (using filters)
433
- const stops = gtfs.getStops({ stopCode: 'ABC' });
434
- const stop = stops[0];
435
-
436
- // Search stops by name (using filters)
437
- const stops = gtfs.getStops({ name: 'Main Street' });
438
-
439
- // Get all stops (using filters with no parameters)
440
- const allStops = gtfs.getStops();
441
-
442
- // Get stops with limit
443
- const stops = gtfs.getStops({ limit: 10 });
444
-
445
- // Get stops for a specific trip
446
- const stops = gtfs.getStops({ tripId: 'TRIP_123' });
447
- ```
448
-
449
- #### Get Route Information
450
-
451
- ```typescript
452
- // Get route by ID
453
- const routes = gtfs.getRoutes({ routeId: 'ROUTE_1' });
454
- const route = routes.length > 0 ? routes[0] : null;
455
-
456
- // Get all routes (using filters with no parameters)
72
+ // Query routes
457
73
  const routes = gtfs.getRoutes();
458
74
 
459
- // Get routes by agency (using filters)
460
- const agencyRoutes = gtfs.getRoutes({ agencyId: 'AGENCY_1' });
461
-
462
- // Get routes with limit
463
- const routes = gtfs.getRoutes({ limit: 10 });
464
- ```
465
-
466
- #### Get Agency Information
467
-
468
- ```typescript
469
- // Get agency by ID
470
- const agencies = gtfs.getAgencies({ agencyId: 'AGENCY_1' });
471
- const agency = agencies.length > 0 ? agencies[0] : null;
472
-
473
- // Get all agencies
474
- const allAgencies = gtfs.getAgencies();
475
-
476
- // Get agencies with limit
477
- const agencies = gtfs.getAgencies({ limit: 5 });
478
- ```
479
-
480
- #### Get Calendar Information
481
-
482
- ```typescript
483
- // Get active services for a date (YYYYMMDD format)
484
- const serviceIds = gtfs.getActiveServiceIds('20240115');
485
-
486
- // Get calendar by service ID
487
- const calendar = gtfs.getCalendarByServiceId('WEEKDAY');
488
-
489
- // Get calendar date exceptions
490
- const exceptions = gtfs.getCalendarDates('WEEKDAY');
491
- ```
492
-
493
- #### Get Trip Information
494
-
495
- ```typescript
496
- // Get trip by ID
497
- const trips = gtfs.getTrips({ tripId: 'TRIP_123' });
498
- const trip = trips.length > 0 ? trips[0] : null;
75
+ // Query stops with filters
76
+ const stops = gtfs.getStops({ name: 'Central Station' });
499
77
 
500
- // Get trips by route (using filters)
501
- const trips = gtfs.getTrips({ routeId: 'ROUTE_1' });
502
-
503
- // Get trips by route and date (using filters)
504
- const trips = gtfs.getTrips({ routeId: 'ROUTE_1', date: '20240115' });
505
-
506
- // Get trips by route, date, and direction (using filters)
78
+ // Get trips for a route on a specific date
507
79
  const trips = gtfs.getTrips({
508
80
  routeId: 'ROUTE_1',
509
81
  date: '20240115',
510
82
  directionId: 0
511
83
  });
512
84
 
513
- // Get all trips for a date
514
- const trips = gtfs.getTrips({ date: '20240115' });
515
-
516
- // Get trips by agency
517
- const trips = gtfs.getTrips({ agencyId: 'AGENCY_1' });
518
- ```
519
-
520
- #### Get Stop Time Information
521
-
522
- ```typescript
523
- // Get stop times for a trip (ordered by stop_sequence)
524
- const stopTimes = gtfs.getStopTimes({ tripId: 'TRIP_123' });
525
-
526
- // Get stop times for a stop (using filters)
527
- const stopTimes = gtfs.getStopTimes({ stopId: 'STOP_123' });
528
-
529
- // Get stop times for a stop and route (using filters)
530
- const stopTimes = gtfs.getStopTimes({
531
- stopId: 'STOP_123',
532
- routeId: 'ROUTE_1'
533
- });
534
-
535
- // Get stop times for a stop, route, and date (using filters)
536
- const stopTimes = gtfs.getStopTimes({
537
- stopId: 'STOP_123',
538
- routeId: 'ROUTE_1',
539
- date: '20240115'
540
- });
541
-
542
- // Get stop times with direction filter (using filters)
543
- const stopTimes = gtfs.getStopTimes({
544
- stopId: 'STOP_123',
545
- routeId: 'ROUTE_1',
546
- date: '20240115',
547
- directionId: 0
548
- });
549
-
550
- // Get stop times by agency
551
- const stopTimes = gtfs.getStopTimes({
552
- agencyId: 'AGENCY_1',
553
- date: '20240115'
554
- });
555
- ```
556
-
557
- #### Building Ordered Stop Lists for Multiple Trips
85
+ // Get stop times for a trip
86
+ const stopTimes = gtfs.getStopTimes({ tripId: trips[0].trip_id });
558
87
 
559
- When displaying timetables for routes where different trips may stop at different stops (e.g., express vs local service, or trips with varying start/end points), use `buildOrderedStopList()` to build an optimal ordered list of all unique stops:
560
-
561
- ```typescript
562
- // Get all trips for a route in one direction
563
- const trips = gtfs.getTrips({
564
- routeId: 'ROUTE_1',
565
- directionId: 0,
566
- date: '20240115'
567
- });
568
-
569
- // Build ordered list of all stops served by these trips
570
- const tripIds = trips.map(t => t.trip_id);
571
- const orderedStops = gtfs.buildOrderedStopList(tripIds);
572
-
573
- // Now display a timetable with all possible stops
574
- console.log('Route stops:');
575
- orderedStops.forEach(stop => {
576
- console.log(`- ${stop.stop_name}`);
577
- });
578
-
579
- // For each trip, you can now show which stops it serves
580
- for (const trip of trips) {
581
- const tripStopTimes = gtfs.getStopTimes({ tripId: trip.trip_id });
582
- console.log(`\nTrip ${trip.trip_headsign}:`);
583
-
584
- // Show all stops, marking which ones this trip serves
585
- orderedStops.forEach(stop => {
586
- const stopTime = tripStopTimes.find(st => st.stop_id === stop.stop_id);
587
- if (stopTime) {
588
- console.log(` ${stopTime.arrival_time} - ${stop.stop_name}`);
589
- } else {
590
- console.log(` --- (not served) - ${stop.stop_name}`);
591
- }
592
- });
593
- }
594
- ```
595
-
596
- **Use Cases:**
597
- - **Express vs Local Service** - Some trips skip stops that others serve
598
- - **Different Start/End Points** - Short-turn trips or extended service trips
599
- - **Peak vs Off-Peak Service** - Different stop coverage based on time of day
600
- - **Route Variations** - Multiple branches or patterns on the same route
601
-
602
- **How it works:**
603
- The method intelligently merges stop sequences from all provided trips:
604
- 1. Fetches stop times for all trips
605
- 2. Processes each trip's stops in sequence order
606
- 3. When encountering a new stop, finds the best insertion position by analyzing stops before and after it
607
- 4. Returns full Stop objects in the determined order
608
-
609
- **Example - Real-world scenario:**
610
- ```typescript
611
- // You have a bus route with:
612
- // - Local trips: A → B → C → D → E → F
613
- // - Express trips: A → C → E → F (skips B and D)
614
- // - Short trips: B → C → D (doesn't go to end of line)
615
-
616
- const allTrips = gtfs.getTrips({ routeId: 'BUS_42', directionId: 0 });
617
- const tripIds = allTrips.map(t => t.trip_id);
618
- const stops = gtfs.buildOrderedStopList(tripIds);
619
-
620
- // Result: [A, B, C, D, E, F] - all stops in correct order
621
- // Now you can create a timetable showing all stops with departure times
622
- ```
623
-
624
- #### Get Shape Information
625
-
626
- Shapes define the path a vehicle takes along a route. Use `getShapes()` to get raw shape point data and `getShapesToGeojson()` to get shapes as GeoJSON for mapping.
627
-
628
- ```typescript
629
- // Get all shape points for a specific shape
630
- const shapePoints = gtfs.getShapes({ shapeId: 'SHAPE_1' });
631
- console.log(`Shape has ${shapePoints.length} points`);
632
-
633
- // Get shapes for a specific route
634
- const routeShapes = gtfs.getShapes({ routeId: 'ROUTE_1' });
635
-
636
- // Get shapes for multiple trips
637
- const tripShapes = gtfs.getShapes({ tripId: ['TRIP_1', 'TRIP_2'] });
638
-
639
- // Each shape point contains:
640
- // - shape_id: string
641
- // - shape_pt_lat: number
642
- // - shape_pt_lon: number
643
- // - shape_pt_sequence: number
644
- // - shape_dist_traveled?: number (optional)
645
- ```
646
-
647
- #### Get Shapes as GeoJSON
648
-
649
- Convert shapes to GeoJSON format for use with mapping libraries (Leaflet, Mapbox, etc.):
650
-
651
- ```typescript
652
- // Get all shapes as GeoJSON FeatureCollection
653
- const geojson = gtfs.getShapesToGeojson();
654
-
655
- // Get shapes for a specific route
656
- const routeGeojson = gtfs.getShapesToGeojson({ routeId: 'ROUTE_1' });
657
-
658
- // Customize coordinate precision (default: 6 decimals = ~10cm)
659
- const lowPrecision = gtfs.getShapesToGeojson({ routeId: 'ROUTE_1' }, 4); // ~11m precision
660
-
661
- // GeoJSON structure:
662
- // {
663
- // type: 'FeatureCollection',
664
- // features: [{
665
- // type: 'Feature',
666
- // properties: {
667
- // shape_id: 'SHAPE_1',
668
- // route_id: 'ROUTE_1',
669
- // route_short_name: '1',
670
- // route_long_name: 'Main Street',
671
- // route_type: 3,
672
- // route_color: 'FF0000',
673
- // route_text_color: 'FFFFFF',
674
- // agency_id: 'AGENCY_1'
675
- // },
676
- // geometry: {
677
- // type: 'LineString',
678
- // coordinates: [[-122.123456, 37.123456], [-122.234567, 37.234567], ...]
679
- // }
680
- // }]
681
- // }
682
-
683
- // Use with Leaflet
684
- const geoJsonLayer = L.geoJSON(geojson, {
685
- style: (feature) => ({
686
- color: `#${feature.properties.route_color || '000000'}`,
687
- weight: 3
688
- })
689
- }).addTo(map);
690
- ```
691
-
692
- **Precision values:**
693
- - `6` decimals: ~10cm precision (default)
694
- - `5` decimals: ~1m precision
695
- - `4` decimals: ~11m precision
696
- - `3` decimals: ~111m precision
697
-
698
- ### GTFS Realtime Support
699
-
700
- This library supports GTFS Realtime data (alerts, trip updates, and vehicle positions) with automatic merging into static schedule data.
701
-
702
- #### Loading Realtime Data
703
-
704
- ```typescript
705
- // Configure RT feed URLs - data will be fetched automatically after GTFS load
706
- const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip', {
707
- realtimeFeedUrls: [
708
- 'https://example.com/gtfs-rt/alerts',
709
- 'https://example.com/gtfs-rt/trip-updates',
710
- 'https://example.com/gtfs-rt/vehicle-positions'
711
- ],
712
- stalenessThreshold: 120 // seconds (default: 120)
713
- });
714
- // RT data is already loaded and ready to use!
715
-
716
- // Or manually fetch RT data later (uses configured URLs or pass custom URLs)
717
- await gtfs.fetchRealtimeData();
718
-
719
- // Or fetch from specific URLs
720
- await gtfs.fetchRealtimeData([
721
- 'https://example.com/gtfs-rt/combined-feed'
722
- ]);
723
-
724
- // Support local files in Node.js
725
- await gtfs.fetchRealtimeData(['./path/to/feed.pb']);
726
-
727
- // Update configuration
728
- gtfs.setRealtimeFeedUrls(['https://example.com/new-feed']);
729
- gtfs.setStalenessThreshold(60); // 60 seconds
730
-
731
- // Check when realtime data was last fetched
732
- const lastFetch = gtfs.getLastRealtimeFetchTimestamp();
733
- if (lastFetch) {
734
- const ageSeconds = Math.floor(Date.now() / 1000) - lastFetch;
735
- console.log(`RT data is ${ageSeconds} seconds old`);
736
- } else {
737
- console.log('No RT data has been fetched yet');
738
- }
739
- ```
740
-
741
- #### Querying Alerts
742
-
743
- ```typescript
744
- // Get all active alerts
745
- const activeAlerts = gtfs.getAlerts({ activeOnly: true });
746
-
747
- // Filter alerts by route
748
- const routeAlerts = gtfs.getAlerts({
749
- routeId: 'ROUTE_1',
750
- activeOnly: true
751
- });
752
-
753
- // Filter alerts by stop
754
- const stopAlerts = gtfs.getAlerts({
755
- stopId: 'STOP_123',
756
- activeOnly: true
757
- });
758
-
759
- // Filter alerts by trip
760
- const tripAlerts = gtfs.getAlerts({
761
- tripId: 'TRIP_456'
762
- });
763
-
764
- // Get alert by ID
765
- const alerts = gtfs.getAlerts({ alertId: 'alert:12345' });
766
- const alert = alerts.length > 0 ? alerts[0] : null;
767
-
768
- // Alert structure
769
- console.log(alert.header_text); // TranslatedString
770
- console.log(alert.description_text); // TranslatedString
771
- console.log(alert.cause); // AlertCause enum
772
- console.log(alert.effect); // AlertEffect enum
773
- console.log(alert.active_period); // TimeRange[]
774
- console.log(alert.informed_entity); // EntitySelector[]
775
- ```
776
-
777
- #### Querying Vehicle Positions
778
-
779
- ```typescript
780
- // Get all vehicle positions
781
- const vehicles = gtfs.getVehiclePositions();
782
-
783
- // Filter by route
784
- const routeVehicles = gtfs.getVehiclePositions({
785
- routeId: 'ROUTE_1'
786
- });
787
-
788
- // Filter by trip
789
- const tripVehicles = gtfs.getVehiclePositions({
790
- tripId: 'TRIP_123'
791
- });
792
- const vehicle = tripVehicles.length > 0 ? tripVehicles[0] : null;
793
-
794
- // Vehicle structure
795
- console.log(vehicle.position); // { latitude, longitude, bearing, speed }
796
- console.log(vehicle.current_stop_sequence);
797
- console.log(vehicle.current_status); // VehicleStopStatus enum
798
- console.log(vehicle.timestamp);
799
- ```
800
-
801
- #### Merging Realtime with Static Data
802
-
803
- The library automatically merges realtime data with static schedules when requested:
804
-
805
- ```typescript
806
- // Get trips with realtime data
807
- const tripsWithRT = gtfs.getTrips({
808
- routeId: 'ROUTE_1',
809
- date: '20240115',
810
- includeRealtime: true // Include RT data
811
- });
812
-
813
- for (const trip of tripsWithRT) {
814
- if (trip.realtime?.vehicle_position) {
815
- console.log('Vehicle location:', trip.realtime.vehicle_position.position);
816
- }
817
- if (trip.realtime?.trip_update) {
818
- console.log('Trip delay:', trip.realtime.trip_update.delay, 'seconds');
819
- }
820
- }
821
-
822
- // Get stop times with realtime delays
823
- const stopTimesWithRT = gtfs.getStopTimes({
824
- tripId: 'TRIP_123',
825
- includeRealtime: true // Include RT data
826
- });
827
-
828
- for (const st of stopTimesWithRT) {
829
- console.log(`Stop: ${st.stop_id}`);
830
- console.log(`Scheduled: ${st.arrival_time}`);
831
- if (st.realtime?.arrival_delay) {
832
- console.log(`Delay: ${st.realtime.arrival_delay} seconds`);
833
- }
834
- }
835
- ```
836
-
837
- #### Clearing Realtime Data
838
-
839
- ```typescript
840
- // Clear all realtime data
841
- gtfs.clearRealtimeData();
842
-
843
- // Then fetch fresh data
844
- await gtfs.fetchRealtimeData();
845
- ```
846
-
847
- #### GTFS-RT Enums
848
-
849
- The library exports all GTFS-RT enums for type checking:
850
-
851
- ```typescript
852
- import {
853
- AlertCause,
854
- AlertEffect,
855
- ScheduleRelationship,
856
- VehicleStopStatus,
857
- CongestionLevel,
858
- OccupancyStatus
859
- } from 'gtfs-sqljs';
860
-
861
- // Use enums for filtering or comparison
862
- if (alert.cause === AlertCause.ACCIDENT) {
863
- console.log('Alert is due to an accident');
864
- }
865
- ```
866
-
867
- ### Smart Caching
868
-
869
- The library supports optional caching of processed GTFS databases to dramatically speed up subsequent loads. The first load processes the GTFS zip file (~5-10 seconds), but subsequent loads use the cached database (<1 second).
870
-
871
- #### Setting Up Caching
872
-
873
- Cache store implementations are available in `examples/cache/`. Copy the appropriate implementation to your project:
874
-
875
- **Browser - IndexedDB:**
876
- ```typescript
877
- // Copy examples/cache/IndexedDBCacheStore.ts to your project
878
- import { GtfsSqlJs } from 'gtfs-sqljs';
879
- import { IndexedDBCacheStore } from './IndexedDBCacheStore';
880
-
881
- const cache = new IndexedDBCacheStore();
882
-
883
- // First load: processes GTFS zip file and caches the result
884
- const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { cache });
885
-
886
- // Second load: uses cached database (much faster!)
887
- const gtfs2 = await GtfsSqlJs.fromZip('gtfs.zip', { cache });
888
- ```
889
-
890
- **Node.js - FileSystem:**
891
- ```typescript
892
- // Copy examples/cache/FileSystemCacheStore.ts to your project
893
- import { GtfsSqlJs } from 'gtfs-sqljs';
894
- import { FileSystemCacheStore } from './FileSystemCacheStore';
895
-
896
- const cache = new FileSystemCacheStore({ dir: './.cache/gtfs' });
897
-
898
- const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { cache });
899
- ```
900
-
901
- **Note:** `FileSystemCacheStore` uses Node.js built-in modules (`fs`, `path`, `os`) and is **NOT compatible** with browser or React Native environments.
902
-
903
- #### Cache Invalidation
904
-
905
- The cache is automatically invalidated when any of these change:
906
- - **File checksum** (SHA-256) - Different GTFS data
907
- - **File size** - Quick check before computing checksum
908
- - **Library version** - Schema or processing logic updated
909
- - **Data version** - User-specified version (see below)
910
- - **Skipped files** - Different `skipFiles` options
911
-
912
- #### Data Versioning
913
-
914
- Use `cacheVersion` to control cache invalidation:
915
-
916
- ```typescript
917
- // Load with version 1.0
918
- const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', {
919
- cacheVersion: '1.0'
920
- });
921
-
922
- // Load with version 2.0 - will reprocess and create new cache
923
- const gtfs2 = await GtfsSqlJs.fromZip('gtfs.zip', {
924
- cacheVersion: '2.0'
925
- });
926
- ```
927
-
928
- **When to increment version:**
929
- - GTFS data is updated but filename stays the same
930
- - You want to force cache refresh
931
- - Testing different processing configurations
932
-
933
- #### Cache Store Options
934
-
935
- **IndexedDBCacheStore options:**
936
- ```typescript
937
- import { IndexedDBCacheStore } from './IndexedDBCacheStore';
938
-
939
- const cache = new IndexedDBCacheStore({
940
- dbName: 'my-app-gtfs-cache' // Custom database name
941
- });
942
- ```
943
-
944
- **FileSystemCacheStore options:**
945
- ```typescript
946
- import { FileSystemCacheStore } from './FileSystemCacheStore';
947
-
948
- const cache = new FileSystemCacheStore({
949
- dir: './my-cache-dir' // Custom cache directory
950
- });
951
- ```
952
-
953
- #### Cache Management
954
-
955
- Cache management methods require passing the cache store instance:
956
-
957
- **Get cache statistics:**
958
- ```typescript
959
- import { IndexedDBCacheStore } from './IndexedDBCacheStore';
960
-
961
- const cache = new IndexedDBCacheStore();
962
- const stats = await GtfsSqlJs.getCacheStats(cache);
963
-
964
- console.log(`Total entries: ${stats.totalEntries}`);
965
- console.log(`Active entries: ${stats.activeEntries}`);
966
- console.log(`Expired entries: ${stats.expiredEntries}`);
967
- console.log(`Total size: ${stats.totalSizeMB} MB`);
968
- ```
969
-
970
- **List cache entries:**
971
- ```typescript
972
- const entries = await GtfsSqlJs.listCache(cache);
973
-
974
- entries.forEach(entry => {
975
- console.log(`Key: ${entry.key}`);
976
- console.log(`Source: ${entry.metadata.source}`);
977
- console.log(`Size: ${(entry.metadata.size / 1024 / 1024).toFixed(2)} MB`);
978
- console.log(`Age: ${((Date.now() - entry.metadata.timestamp) / 1000 / 60 / 60).toFixed(1)} hours`);
979
- });
980
- ```
981
-
982
- **Clean expired entries:**
983
- ```typescript
984
- // Remove entries older than 7 days (default)
985
- const deletedCount = await GtfsSqlJs.cleanExpiredCache(cache);
986
- console.log(`Deleted ${deletedCount} expired entries`);
987
-
988
- // Custom expiration time (3 days)
989
- const threeDays = 3 * 24 * 60 * 60 * 1000;
990
- await GtfsSqlJs.cleanExpiredCache(cache, threeDays);
991
- ```
992
-
993
- **Clear all cache:**
994
- ```typescript
995
- await GtfsSqlJs.clearCache(cache);
996
- ```
997
-
998
- #### Without Caching
999
-
1000
- By default, caching is disabled. Simply omit the `cache` option:
1001
-
1002
- ```typescript
1003
- const gtfs = await GtfsSqlJs.fromZip('gtfs.zip');
1004
- // No caching - GTFS is processed fresh each time
1005
- ```
1006
-
1007
- #### Custom Cache Expiration
1008
-
1009
- Change the default expiration time (default: 7 days):
1010
-
1011
- ```typescript
1012
- const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', {
1013
- cacheExpirationMs: 3 * 24 * 60 * 60 * 1000 // 3 days
1014
- });
1015
- ```
1016
-
1017
- #### Custom Cache Store Implementation
1018
-
1019
- Implement your own cache store (e.g., Redis, S3):
1020
-
1021
- ```typescript
1022
- import type { CacheStore, CacheMetadata } from 'gtfs-sqljs';
1023
-
1024
- class RedisCacheStore implements CacheStore {
1025
- async get(key: string): Promise<ArrayBuffer | null> {
1026
- // Implement Redis get
1027
- }
1028
-
1029
- async set(key: string, data: ArrayBuffer, metadata: CacheMetadata): Promise<void> {
1030
- // Implement Redis set
1031
- }
1032
-
1033
- async has(key: string): Promise<boolean> {
1034
- // Implement Redis exists check
1035
- }
1036
-
1037
- async delete(key: string): Promise<void> {
1038
- // Implement Redis delete
1039
- }
1040
-
1041
- async clear(): Promise<void> {
1042
- // Implement Redis clear
1043
- }
1044
-
1045
- async list(): Promise<CacheEntry[]> {
1046
- // Optional: Implement list
1047
- }
1048
- }
1049
-
1050
- const cache = new RedisCacheStore();
1051
- const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { cache });
1052
- ```
1053
-
1054
- ### Export Database
1055
-
1056
- ```typescript
1057
- // Export to ArrayBuffer for storage (includes RT data)
1058
- const buffer = gtfs.export();
1059
-
1060
- // Save to file (Node.js)
1061
- import fs from 'fs';
1062
- fs.writeFileSync('gtfs.db', Buffer.from(buffer));
1063
-
1064
- // Store in IndexedDB (Browser)
1065
- // ... use IndexedDB API to store the ArrayBuffer
1066
- ```
1067
-
1068
- ### Advanced Usage
1069
-
1070
- #### Direct Database Access
1071
-
1072
- For advanced queries not covered by the API:
1073
-
1074
- ```typescript
1075
- const db = gtfs.getDatabase();
1076
-
1077
- const stmt = db.prepare('SELECT * FROM stops WHERE stop_lat > ? AND stop_lon < ?');
1078
- stmt.bind([40.7, -74.0]);
1079
-
1080
- while (stmt.step()) {
1081
- const row = stmt.getAsObject();
1082
- console.log(row);
1083
- }
1084
-
1085
- stmt.free();
1086
- ```
1087
-
1088
- #### Close Database
1089
-
1090
- ```typescript
1091
- // Close the database when done
88
+ // Clean up
1092
89
  gtfs.close();
1093
90
  ```
1094
91
 
1095
- ## Complete Example
1096
-
1097
- ```typescript
1098
- import { GtfsSqlJs } from 'gtfs-sqljs';
1099
-
1100
- async function example() {
1101
- // Load GTFS data (skip shapes.txt to reduce memory usage)
1102
- const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip', {
1103
- skipFiles: ['shapes.txt']
1104
- });
1105
-
1106
- // Find a stop using flexible filters
1107
- const stops = gtfs.getStops({ name: 'Central Station' });
1108
- const stop = stops[0];
1109
- console.log(`Found stop: ${stop.stop_name}`);
1110
-
1111
- // Find routes serving this stop (via stop_times and trips)
1112
- const allStopTimes = gtfs.getStopTimes({ stopId: stop.stop_id });
1113
- const routeIds = new Set(
1114
- allStopTimes.map(st => {
1115
- const trips = gtfs.getTrips({ tripId: st.trip_id });
1116
- return trips.length > 0 ? trips[0].route_id : null;
1117
- })
1118
- );
1119
-
1120
- // Get route details
1121
- for (const routeId of routeIds) {
1122
- if (!routeId) continue;
1123
- const routes = gtfs.getRoutes({ routeId });
1124
- const route = routes.length > 0 ? routes[0] : null;
1125
- console.log(`Route: ${route?.route_short_name} - ${route?.route_long_name}`);
1126
- }
1127
-
1128
- // Get trips for a specific route on a date using flexible filters
1129
- const today = '20240115'; // YYYYMMDD format
1130
- const trips = gtfs.getTrips({
1131
- routeId: Array.from(routeIds)[0]!,
1132
- date: today
1133
- });
1134
- console.log(`Found ${trips.length} trips for today`);
1135
-
1136
- // Get stop times for a specific trip
1137
- const stopTimes = gtfs.getStopTimes({ tripId: trips[0].trip_id });
1138
- console.log('Trip schedule:');
1139
- for (const st of stopTimes) {
1140
- const stops = gtfs.getStops({ stopId: st.stop_id });
1141
- const stop = stops.length > 0 ? stops[0] : null;
1142
- console.log(` ${st.arrival_time} - ${stop?.stop_name}`);
1143
- }
1144
-
1145
- // Export database for later use
1146
- const buffer = gtfs.export();
1147
- // ... save buffer to file or storage
1148
-
1149
- // Clean up
1150
- gtfs.close();
1151
- }
1152
-
1153
- example();
1154
- ```
92
+ For detailed usage examples, see the [Usage Guide](https://sysdevrun.github.io/gtfs-sqljs/docs/documents/Usage_Guide.html).
1155
93
 
1156
94
  ## API Reference
1157
95
 
96
+ Full API documentation: [API Reference](https://sysdevrun.github.io/gtfs-sqljs/docs/)
97
+
1158
98
  ### Static Methods
1159
99
 
1160
100
  - `GtfsSqlJs.fromZip(zipPath, options?)` - Create instance from GTFS ZIP file
@@ -1176,7 +116,7 @@ All methods support flexible filtering with both single values and arrays:
1176
116
 
1177
117
  #### Calendar Methods
1178
118
  - `getActiveServiceIds(date)` - Get active service IDs for a date (YYYYMMDD format)
1179
- - `getCalendarByServiceId(serviceId)` - Get calendar by service_id
119
+ - `getCalendars(filters?)` - Get calendars (filters: serviceId, limit)
1180
120
  - `getCalendarDates(serviceId)` - Get calendar date exceptions for a service
1181
121
  - `getCalendarDatesForDate(date)` - Get calendar exceptions for a specific date
1182
122
 
@@ -1223,25 +163,6 @@ import type {
1223
163
  // Progress tracking types
1224
164
  ProgressInfo, ProgressCallback
1225
165
  } from 'gtfs-sqljs';
1226
-
1227
- const stop: Stop = gtfs.getStopById('STOP_123')!;
1228
-
1229
- // Use filter types for better type safety
1230
- const filters: TripFilters = {
1231
- routeId: 'ROUTE_1',
1232
- directionId: 0,
1233
- includeRealtime: true
1234
- };
1235
- const trips = gtfs.getTrips(filters);
1236
-
1237
- // RT types
1238
- const alerts: Alert[] = gtfs.getAlerts({ activeOnly: true });
1239
- const vehicles: VehiclePosition[] = gtfs.getVehiclePositions();
1240
-
1241
- // Progress callback with types
1242
- const handleProgress: ProgressCallback = (progress) => {
1243
- console.log(`${progress.percentComplete}% - ${progress.message}`);
1244
- };
1245
166
  ```
1246
167
 
1247
168
  ## GTFS Specification