This guide covers all usage patterns for gtfs-sqljs, from basic setup to advanced features like GTFS Realtime and smart caching.
sql.js requires a WASM file to be loaded. There are several ways to handle this:
In Node.js, sql.js will automatically locate the WASM file from the installed package:
import { GtfsSqlJs } from 'gtfs-sqljs';
// The WASM file is loaded automatically
const gtfs = await GtfsSqlJs.fromZip('path/to/gtfs.zip');
You can use a CDN to serve the WASM file:
import initSqlJs from 'sql.js';
import { GtfsSqlJs } from 'gtfs-sqljs';
// Initialize sql.js with CDN WASM file
const SQL = await initSqlJs({
locateFile: (filename) => `https://sql.js.org/dist/${filename}`
});
// Pass the SQL instance to GtfsSqlJs
const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip', { SQL });
If you're using a bundler, you need to configure it to handle the WASM file:
import initSqlJs from 'sql.js';
import { GtfsSqlJs } from 'gtfs-sqljs';
import sqlWasmUrl from 'sql.js/dist/sql-wasm.wasm?url';
const SQL = await initSqlJs({
locateFile: () => sqlWasmUrl
});
const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip', { SQL });
import initSqlJs from 'sql.js';
import { GtfsSqlJs } from 'gtfs-sqljs';
const SQL = await initSqlJs({
locateFile: (filename) => `/path/to/public/${filename}`
});
const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip', { SQL });
Make sure to copy sql-wasm.wasm from node_modules/sql.js/dist/ to your public directory.
import { GtfsSqlJs } from 'gtfs-sqljs';
// From URL
const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip');
// From local file (Node.js)
const gtfs = await GtfsSqlJs.fromZip('./path/to/gtfs.zip');
// Skip importing specific files to reduce memory usage
// Tables will be created but data won't be imported
const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip', {
skipFiles: ['shapes.txt', 'frequencies.txt']
});
import { GtfsSqlJs } from 'gtfs-sqljs';
// Load from ArrayBuffer
const dbBuffer = await fetch('https://example.com/gtfs.db').then(r => r.arrayBuffer());
const gtfs = await GtfsSqlJs.fromDatabase(dbBuffer);
Track loading progress with a callback function - perfect for displaying progress bars or updating UI:
import { GtfsSqlJs, type ProgressInfo } from 'gtfs-sqljs';
const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip', {
onProgress: (progress: ProgressInfo) => {
console.log(`${progress.percentComplete}% - ${progress.message}`);
// Progress information available:
console.log('Phase:', progress.phase); // Current phase
console.log('File:', progress.currentFile); // Current file being processed
console.log('Files:', progress.filesCompleted, '/', progress.totalFiles);
console.log('Rows:', progress.rowsProcessed, '/', progress.totalRows);
}
});
The loading process goes through these phases:
checking_cache - Checking if cached database exists (0%)loading_from_cache - Loading from cache (if found, jumps to 100%)downloading - Downloading GTFS ZIP file (1-30%)extracting - Extracting GTFS ZIP file (35%)creating_schema - Creating database tables (40%)inserting_data - Importing data from CSV files (40-75%)creating_indexes - Building database indexes (75-85%)analyzing - Optimizing query performance (85-90%)loading_realtime - Loading realtime data from feeds (90-95%) (if configured)saving_cache - Saving to cache (95-98%)complete - Load complete (100%)Note: When a cached database is found, phases 3-10 are skipped, and loading completes in <1 second.
Note: The loading_realtime phase only occurs if realtimeFeedUrls are configured during initialization.
The progress callback is especially useful for web workers:
// In your web worker
import { GtfsSqlJs } from 'gtfs-sqljs';
self.onmessage = async (event) => {
if (event.data.type === 'load') {
const gtfs = await GtfsSqlJs.fromZip(event.data.url, {
onProgress: (progress) => {
// Send progress updates to main thread
self.postMessage({
type: 'progress',
data: progress
});
}
});
self.postMessage({ type: 'complete' });
}
};
// In your main thread
const worker = new Worker('gtfs-worker.js');
worker.onmessage = (event) => {
if (event.data.type === 'progress') {
const progress = event.data.data;
updateProgressBar(progress.percentComplete);
updateStatusText(progress.message);
}
};
worker.postMessage({ type: 'load', url: 'https://example.com/gtfs.zip' });
interface ProgressInfo {
phase: 'checking_cache' | 'loading_from_cache' | 'downloading' | 'extracting' |
'creating_schema' | 'inserting_data' | 'creating_indexes' | 'analyzing' |
'loading_realtime' | 'saving_cache' | 'complete';
currentFile: string | null; // e.g., "stop_times.txt"
filesCompleted: number; // Files processed so far
totalFiles: number; // Total number of files
rowsProcessed: number; // CSV rows imported so far
totalRows: number; // Total CSV rows to import
bytesDownloaded?: number; // Bytes downloaded (during 'downloading' phase)
totalBytes?: number; // Total bytes to download (during 'downloading' phase)
percentComplete: number; // 0-100
message: string; // Human-readable status message
}
The library provides flexible filter-based methods for querying GTFS data. Pass an object with optional filters to combine multiple criteria:
// Get stops - combine any filters
const stops = gtfs.getStops({
name: 'Station', // Search by name
limit: 10 // Limit results
});
// Get routes - with or without filters
const allRoutes = gtfs.getRoutes();
const agencyRoutes = gtfs.getRoutes({ agencyId: 'AGENCY_1' });
// Get trips - combine multiple filters
const trips = gtfs.getTrips({
routeId: 'ROUTE_1', // Filter by route
date: '20240115', // Filter by date (gets active services)
directionId: 0, // Filter by direction
limit: 50 // Limit results
});
// Get stop times - flexible filtering
const stopTimes = gtfs.getStopTimes({
stopId: 'STOP_123', // At a specific stop
routeId: 'ROUTE_1', // For a specific route
date: '20240115', // On a specific date
directionId: 0 // In a specific direction
});
stopId: string - Filter by stop IDstopCode: string - Filter by stop codename: string - Search by stop name (partial match)tripId: string - Get stops for a triplimit: number - Limit resultsrouteId: string - Filter by route IDagencyId: string - Filter by agencylimit: number - Limit resultstripId: string - Filter by trip IDrouteId: string - Filter by routedate: string - Filter by date (YYYYMMDD format)directionId: number - Filter by directionlimit: number - Limit resultstripId: string - Filter by tripstopId: string - Filter by stoprouteId: string - Filter by routedate: string - Filter by date (YYYYMMDD format)directionId: number - Filter by directionlimit: number - Limit resultsshapeId: string | string[] - Filter by shape IDrouteId: string | string[] - Filter by route (via trips table)tripId: string | string[] - Filter by triplimit: number - Limit resultsgetShapesToGeojson(filters?, precision?):
getShapesprecision: number - Decimal places for coordinates (default: 6)// Get stop by ID
const stops = gtfs.getStops({ stopId: 'STOP_123' });
const stop = stops.length > 0 ? stops[0] : null;
console.log(stop?.stop_name);
// Get stop by code (using filters)
const stops = gtfs.getStops({ stopCode: 'ABC' });
const stop = stops[0];
// Search stops by name (using filters)
const stops = gtfs.getStops({ name: 'Main Street' });
// Get all stops (using filters with no parameters)
const allStops = gtfs.getStops();
// Get stops with limit
const stops = gtfs.getStops({ limit: 10 });
// Get stops for a specific trip
const stops = gtfs.getStops({ tripId: 'TRIP_123' });
// Get route by ID
const routes = gtfs.getRoutes({ routeId: 'ROUTE_1' });
const route = routes.length > 0 ? routes[0] : null;
// Get all routes (using filters with no parameters)
const routes = gtfs.getRoutes();
// Get routes by agency (using filters)
const agencyRoutes = gtfs.getRoutes({ agencyId: 'AGENCY_1' });
// Get routes with limit
const routes = gtfs.getRoutes({ limit: 10 });
// Get agency by ID
const agencies = gtfs.getAgencies({ agencyId: 'AGENCY_1' });
const agency = agencies.length > 0 ? agencies[0] : null;
// Get all agencies
const allAgencies = gtfs.getAgencies();
// Get agencies with limit
const agencies = gtfs.getAgencies({ limit: 5 });
// Get active services for a date (YYYYMMDD format)
const serviceIds = gtfs.getActiveServiceIds('20240115');
// Get calendar by service ID
const calendars = gtfs.getCalendars({ serviceId: 'WEEKDAY' });
// Get calendar date exceptions
const exceptions = gtfs.getCalendarDates('WEEKDAY');
// Get trip by ID
const trips = gtfs.getTrips({ tripId: 'TRIP_123' });
const trip = trips.length > 0 ? trips[0] : null;
// Get trips by route (using filters)
const trips = gtfs.getTrips({ routeId: 'ROUTE_1' });
// Get trips by route and date (using filters)
const trips = gtfs.getTrips({ routeId: 'ROUTE_1', date: '20240115' });
// Get trips by route, date, and direction (using filters)
const trips = gtfs.getTrips({
routeId: 'ROUTE_1',
date: '20240115',
directionId: 0
});
// Get all trips for a date
const trips = gtfs.getTrips({ date: '20240115' });
// Get trips by agency
const trips = gtfs.getTrips({ agencyId: 'AGENCY_1' });
// Get stop times for a trip (ordered by stop_sequence)
const stopTimes = gtfs.getStopTimes({ tripId: 'TRIP_123' });
// Get stop times for a stop (using filters)
const stopTimes = gtfs.getStopTimes({ stopId: 'STOP_123' });
// Get stop times for a stop and route (using filters)
const stopTimes = gtfs.getStopTimes({
stopId: 'STOP_123',
routeId: 'ROUTE_1'
});
// Get stop times for a stop, route, and date (using filters)
const stopTimes = gtfs.getStopTimes({
stopId: 'STOP_123',
routeId: 'ROUTE_1',
date: '20240115'
});
// Get stop times with direction filter (using filters)
const stopTimes = gtfs.getStopTimes({
stopId: 'STOP_123',
routeId: 'ROUTE_1',
date: '20240115',
directionId: 0
});
// Get stop times by agency
const stopTimes = gtfs.getStopTimes({
agencyId: 'AGENCY_1',
date: '20240115'
});
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:
// Get all trips for a route in one direction
const trips = gtfs.getTrips({
routeId: 'ROUTE_1',
directionId: 0,
date: '20240115'
});
// Build ordered list of all stops served by these trips
const tripIds = trips.map(t => t.trip_id);
const orderedStops = gtfs.buildOrderedStopList(tripIds);
// Now display a timetable with all possible stops
console.log('Route stops:');
orderedStops.forEach(stop => {
console.log(`- ${stop.stop_name}`);
});
// For each trip, you can now show which stops it serves
for (const trip of trips) {
const tripStopTimes = gtfs.getStopTimes({ tripId: trip.trip_id });
console.log(`\nTrip ${trip.trip_headsign}:`);
// Show all stops, marking which ones this trip serves
orderedStops.forEach(stop => {
const stopTime = tripStopTimes.find(st => st.stop_id === stop.stop_id);
if (stopTime) {
console.log(` ${stopTime.arrival_time} - ${stop.stop_name}`);
} else {
console.log(` --- (not served) - ${stop.stop_name}`);
}
});
}
Use Cases:
How it works: The method intelligently merges stop sequences from all provided trips:
Example - Real-world scenario:
// You have a bus route with:
// - Local trips: A -> B -> C -> D -> E -> F
// - Express trips: A -> C -> E -> F (skips B and D)
// - Short trips: B -> C -> D (doesn't go to end of line)
const allTrips = gtfs.getTrips({ routeId: 'BUS_42', directionId: 0 });
const tripIds = allTrips.map(t => t.trip_id);
const stops = gtfs.buildOrderedStopList(tripIds);
// Result: [A, B, C, D, E, F] - all stops in correct order
// Now you can create a timetable showing all stops with departure times
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.
// Get all shape points for a specific shape
const shapePoints = gtfs.getShapes({ shapeId: 'SHAPE_1' });
console.log(`Shape has ${shapePoints.length} points`);
// Get shapes for a specific route
const routeShapes = gtfs.getShapes({ routeId: 'ROUTE_1' });
// Get shapes for multiple trips
const tripShapes = gtfs.getShapes({ tripId: ['TRIP_1', 'TRIP_2'] });
// Each shape point contains:
// - shape_id: string
// - shape_pt_lat: number
// - shape_pt_lon: number
// - shape_pt_sequence: number
// - shape_dist_traveled?: number (optional)
Convert shapes to GeoJSON format for use with mapping libraries (Leaflet, Mapbox, etc.):
// Get all shapes as GeoJSON FeatureCollection
const geojson = gtfs.getShapesToGeojson();
// Get shapes for a specific route
const routeGeojson = gtfs.getShapesToGeojson({ routeId: 'ROUTE_1' });
// Customize coordinate precision (default: 6 decimals = ~10cm)
const lowPrecision = gtfs.getShapesToGeojson({ routeId: 'ROUTE_1' }, 4); // ~11m precision
// GeoJSON structure:
// {
// type: 'FeatureCollection',
// features: [{
// type: 'Feature',
// properties: {
// shape_id: 'SHAPE_1',
// route_id: 'ROUTE_1',
// route_short_name: '1',
// route_long_name: 'Main Street',
// route_type: 3,
// route_color: 'FF0000',
// route_text_color: 'FFFFFF',
// agency_id: 'AGENCY_1'
// },
// geometry: {
// type: 'LineString',
// coordinates: [[-122.123456, 37.123456], [-122.234567, 37.234567], ...]
// }
// }]
// }
// Use with Leaflet
const geoJsonLayer = L.geoJSON(geojson, {
style: (feature) => ({
color: `#${feature.properties.route_color || '000000'}`,
weight: 3
})
}).addTo(map);
Precision values:
6 decimals: ~10cm precision (default)5 decimals: ~1m precision4 decimals: ~11m precision3 decimals: ~111m precisionThis library supports GTFS Realtime data (alerts, trip updates, and vehicle positions) with automatic merging into static schedule data.
// Configure RT feed URLs - data will be fetched automatically after GTFS load
const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip', {
realtimeFeedUrls: [
'https://example.com/gtfs-rt/alerts',
'https://example.com/gtfs-rt/trip-updates',
'https://example.com/gtfs-rt/vehicle-positions'
],
stalenessThreshold: 120 // seconds (default: 120)
});
// RT data is already loaded and ready to use!
// Or manually fetch RT data later (uses configured URLs or pass custom URLs)
await gtfs.fetchRealtimeData();
// Or fetch from specific URLs
await gtfs.fetchRealtimeData([
'https://example.com/gtfs-rt/combined-feed'
]);
// Support local files in Node.js
await gtfs.fetchRealtimeData(['./path/to/feed.pb']);
// Update configuration
gtfs.setRealtimeFeedUrls(['https://example.com/new-feed']);
gtfs.setStalenessThreshold(60); // 60 seconds
// Check when realtime data was last fetched
const lastFetch = gtfs.getLastRealtimeFetchTimestamp();
if (lastFetch) {
const ageSeconds = Math.floor(Date.now() / 1000) - lastFetch;
console.log(`RT data is ${ageSeconds} seconds old`);
} else {
console.log('No RT data has been fetched yet');
}
// Get all active alerts
const activeAlerts = gtfs.getAlerts({ activeOnly: true });
// Filter alerts by route
const routeAlerts = gtfs.getAlerts({
routeId: 'ROUTE_1',
activeOnly: true
});
// Filter alerts by stop
const stopAlerts = gtfs.getAlerts({
stopId: 'STOP_123',
activeOnly: true
});
// Filter alerts by trip
const tripAlerts = gtfs.getAlerts({
tripId: 'TRIP_456'
});
// Get alert by ID
const alerts = gtfs.getAlerts({ alertId: 'alert:12345' });
const alert = alerts.length > 0 ? alerts[0] : null;
// Alert structure
console.log(alert.header_text); // TranslatedString
console.log(alert.description_text); // TranslatedString
console.log(alert.cause); // AlertCause enum
console.log(alert.effect); // AlertEffect enum
console.log(alert.active_period); // TimeRange[]
console.log(alert.informed_entity); // EntitySelector[]
// Get all vehicle positions
const vehicles = gtfs.getVehiclePositions();
// Filter by route
const routeVehicles = gtfs.getVehiclePositions({
routeId: 'ROUTE_1'
});
// Filter by trip
const tripVehicles = gtfs.getVehiclePositions({
tripId: 'TRIP_123'
});
const vehicle = tripVehicles.length > 0 ? tripVehicles[0] : null;
// Vehicle structure
console.log(vehicle.position); // { latitude, longitude, bearing, speed }
console.log(vehicle.current_stop_sequence);
console.log(vehicle.current_status); // VehicleStopStatus enum
console.log(vehicle.timestamp);
The library automatically merges realtime data with static schedules when requested:
// Get trips with realtime data
const tripsWithRT = gtfs.getTrips({
routeId: 'ROUTE_1',
date: '20240115',
includeRealtime: true // Include RT data
});
for (const trip of tripsWithRT) {
if (trip.realtime?.vehicle_position) {
console.log('Vehicle location:', trip.realtime.vehicle_position.position);
}
if (trip.realtime?.trip_update) {
console.log('Trip delay:', trip.realtime.trip_update.delay, 'seconds');
}
}
// Get stop times with realtime delays
const stopTimesWithRT = gtfs.getStopTimes({
tripId: 'TRIP_123',
includeRealtime: true // Include RT data
});
for (const st of stopTimesWithRT) {
console.log(`Stop: ${st.stop_id}`);
console.log(`Scheduled: ${st.arrival_time}`);
if (st.realtime?.arrival_delay) {
console.log(`Delay: ${st.realtime.arrival_delay} seconds`);
}
}
// Clear all realtime data
gtfs.clearRealtimeData();
// Then fetch fresh data
await gtfs.fetchRealtimeData();
The library exports all GTFS-RT enums for type checking:
import {
AlertCause,
AlertEffect,
ScheduleRelationship,
VehicleStopStatus,
CongestionLevel,
OccupancyStatus
} from 'gtfs-sqljs';
// Use enums for filtering or comparison
if (alert.cause === AlertCause.ACCIDENT) {
console.log('Alert is due to an accident');
}
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).
Cache store implementations are available in examples/cache/. Copy the appropriate implementation to your project:
Browser - IndexedDB:
// Copy examples/cache/IndexedDBCacheStore.ts to your project
import { GtfsSqlJs } from 'gtfs-sqljs';
import { IndexedDBCacheStore } from './IndexedDBCacheStore';
const cache = new IndexedDBCacheStore();
// First load: processes GTFS zip file and caches the result
const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { cache });
// Second load: uses cached database (much faster!)
const gtfs2 = await GtfsSqlJs.fromZip('gtfs.zip', { cache });
Node.js - FileSystem:
// Copy examples/cache/FileSystemCacheStore.ts to your project
import { GtfsSqlJs } from 'gtfs-sqljs';
import { FileSystemCacheStore } from './FileSystemCacheStore';
const cache = new FileSystemCacheStore({ dir: './.cache/gtfs' });
const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { cache });
Note: FileSystemCacheStore uses Node.js built-in modules (fs, path, os) and is NOT compatible with browser or React Native environments.
The cache is automatically invalidated when any of these change:
skipFiles optionsUse cacheVersion to control cache invalidation:
// Load with version 1.0
const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', {
cacheVersion: '1.0'
});
// Load with version 2.0 - will reprocess and create new cache
const gtfs2 = await GtfsSqlJs.fromZip('gtfs.zip', {
cacheVersion: '2.0'
});
When to increment version:
IndexedDBCacheStore options:
import { IndexedDBCacheStore } from './IndexedDBCacheStore';
const cache = new IndexedDBCacheStore({
dbName: 'my-app-gtfs-cache' // Custom database name
});
FileSystemCacheStore options:
import { FileSystemCacheStore } from './FileSystemCacheStore';
const cache = new FileSystemCacheStore({
dir: './my-cache-dir' // Custom cache directory
});
Cache management methods require passing the cache store instance:
Get cache statistics:
import { IndexedDBCacheStore } from './IndexedDBCacheStore';
const cache = new IndexedDBCacheStore();
const stats = await GtfsSqlJs.getCacheStats(cache);
console.log(`Total entries: ${stats.totalEntries}`);
console.log(`Active entries: ${stats.activeEntries}`);
console.log(`Expired entries: ${stats.expiredEntries}`);
console.log(`Total size: ${stats.totalSizeMB} MB`);
List cache entries:
const entries = await GtfsSqlJs.listCache(cache);
entries.forEach(entry => {
console.log(`Key: ${entry.key}`);
console.log(`Source: ${entry.metadata.source}`);
console.log(`Size: ${(entry.metadata.size / 1024 / 1024).toFixed(2)} MB`);
console.log(`Age: ${((Date.now() - entry.metadata.timestamp) / 1000 / 60 / 60).toFixed(1)} hours`);
});
Clean expired entries:
// Remove entries older than 7 days (default)
const deletedCount = await GtfsSqlJs.cleanExpiredCache(cache);
console.log(`Deleted ${deletedCount} expired entries`);
// Custom expiration time (3 days)
const threeDays = 3 * 24 * 60 * 60 * 1000;
await GtfsSqlJs.cleanExpiredCache(cache, threeDays);
Clear all cache:
await GtfsSqlJs.clearCache(cache);
By default, caching is disabled. Simply omit the cache option:
const gtfs = await GtfsSqlJs.fromZip('gtfs.zip');
// No caching - GTFS is processed fresh each time
Change the default expiration time (default: 7 days):
const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', {
cacheExpirationMs: 3 * 24 * 60 * 60 * 1000 // 3 days
});
Implement your own cache store (e.g., Redis, S3):
import type { CacheStore, CacheMetadata } from 'gtfs-sqljs';
class RedisCacheStore implements CacheStore {
async get(key: string): Promise<ArrayBuffer | null> {
// Implement Redis get
}
async set(key: string, data: ArrayBuffer, metadata: CacheMetadata): Promise<void> {
// Implement Redis set
}
async has(key: string): Promise<boolean> {
// Implement Redis exists check
}
async delete(key: string): Promise<void> {
// Implement Redis delete
}
async clear(): Promise<void> {
// Implement Redis clear
}
async list(): Promise<CacheEntry[]> {
// Optional: Implement list
}
}
const cache = new RedisCacheStore();
const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { cache });
// Export to ArrayBuffer for storage (includes RT data)
const buffer = gtfs.export();
// Save to file (Node.js)
import fs from 'fs';
fs.writeFileSync('gtfs.db', Buffer.from(buffer));
// Store in IndexedDB (Browser)
// ... use IndexedDB API to store the ArrayBuffer
For advanced queries not covered by the API:
const db = gtfs.getDatabase();
const stmt = db.prepare('SELECT * FROM stops WHERE stop_lat > ? AND stop_lon < ?');
stmt.bind([40.7, -74.0]);
while (stmt.step()) {
const row = stmt.getAsObject();
console.log(row);
}
stmt.free();
// Close the database when done
gtfs.close();
import { GtfsSqlJs } from 'gtfs-sqljs';
async function example() {
// Load GTFS data (skip shapes.txt to reduce memory usage)
const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip', {
skipFiles: ['shapes.txt']
});
// Find a stop using flexible filters
const stops = gtfs.getStops({ name: 'Central Station' });
const stop = stops[0];
console.log(`Found stop: ${stop.stop_name}`);
// Find routes serving this stop (via stop_times and trips)
const allStopTimes = gtfs.getStopTimes({ stopId: stop.stop_id });
const routeIds = new Set(
allStopTimes.map(st => {
const trips = gtfs.getTrips({ tripId: st.trip_id });
return trips.length > 0 ? trips[0].route_id : null;
})
);
// Get route details
for (const routeId of routeIds) {
if (!routeId) continue;
const routes = gtfs.getRoutes({ routeId });
const route = routes.length > 0 ? routes[0] : null;
console.log(`Route: ${route?.route_short_name} - ${route?.route_long_name}`);
}
// Get trips for a specific route on a date using flexible filters
const today = '20240115'; // YYYYMMDD format
const trips = gtfs.getTrips({
routeId: Array.from(routeIds)[0]!,
date: today
});
console.log(`Found ${trips.length} trips for today`);
// Get stop times for a specific trip
const stopTimes = gtfs.getStopTimes({ tripId: trips[0].trip_id });
console.log('Trip schedule:');
for (const st of stopTimes) {
const stops = gtfs.getStops({ stopId: st.stop_id });
const stop = stops.length > 0 ? stops[0] : null;
console.log(` ${st.arrival_time} - ${stop?.stop_name}`);
}
// Export database for later use
const buffer = gtfs.export();
// ... save buffer to file or storage
// Clean up
gtfs.close();
}
example();