@drfrost/bods-js
Version:
JavaScript client for the UK's Bus Open Data Service (BODS) API
210 lines (209 loc) ⢠8.47 kB
JavaScript
/**
* Example usage of the BODS JavaScript Client
*
* This file demonstrates various ways to use the BODS API client
* to access UK bus data including timetables, fares, real-time locations,
* and service disruptions.
*/
import { BODSClient, createBoundingBox, UK_CITIES } from './index.js';
/**
* Initialize the BODS client with your API key
* Get your API key from: https://data.bus-data.dft.gov.uk/account/
*/
const client = new BODSClient({
apiKey: 'your-api-key-here', // Replace with your actual API key
timeout: 30000 // Optional: 30 second timeout
});
/**
* Example 1: Search for timetables
*/
async function searchTimetables() {
try {
// Find all published timetables for Stagecoach Manchester
const timetables = await client.timetables.search({
noc: ['SCMN'], // Stagecoach Manchester
status: 'published',
limit: 10
});
console.log(`Found ${timetables.count} timetables`);
timetables.results.forEach(timetable => {
console.log(`- ${timetable.operatorName}: ${timetable.name}`);
console.log(` Lines: ${timetable.lines.join(', ')}`);
console.log(` Quality: ${timetable.dqRag} (${timetable.dqScore})`);
});
// Get a specific timetable by ID
if (timetables.results.length > 0) {
const firstTimetable = await client.timetables.getById(timetables.results[0].id);
console.log('Detailed timetable:', firstTimetable);
}
}
catch (error) {
console.error('Error fetching timetables:', error);
}
}
/**
* Example 2: Search for fares by geographic area
*/
async function searchFaresByArea() {
try {
// Get fares for the Manchester area
const manchesterBox = UK_CITIES.MANCHESTER;
const fares = await client.fares.getByArea(manchesterBox);
console.log(`Found ${fares.count} fare datasets in Manchester area`);
fares.results.forEach(fare => {
console.log(`- ${fare.operatorName}: ${fare.name}`);
console.log(` Fare zones: ${fare.numOfFareZones}`);
console.log(` Products: ${fare.numOfFareProducts}`);
});
// Create custom bounding box for Liverpool city center (5km radius)
const liverpoolBox = createBoundingBox(53.4084, -2.9916, 5);
const liverpoolFares = await client.fares.getByArea(liverpoolBox);
console.log(`Liverpool fares: ${liverpoolFares.count}`);
}
catch (error) {
console.error('Error fetching fares:', error);
}
}
/**
* Example 3: Get real-time vehicle locations
*/
async function getVehicleLocations() {
try {
// Get real-time locations for specific operators in SIRI-VM format
const vehicles = await client.avl.getSIRIVM({
operatorRef: ['SCMN', 'SCGH'], // Stagecoach operators
boundingBox: UK_CITIES.MANCHESTER
});
console.log('Vehicle locations (SIRI-VM XML):');
console.log(vehicles.xmlData.substring(0, 500) + '...');
// Get vehicles for a specific bus line
const lineVehicles = await client.avl.getByLine('85A');
console.log('Line 85A vehicles:', lineVehicles.xmlData.length > 0 ? 'Data received' : 'No data');
// Get vehicles in GTFS-RT format (binary protobuf)
const gtfsVehicles = await client.avl.getGTFSRT({
boundingBox: createBoundingBox(53.4808, -2.2426, 10) // Manchester, 10km radius
});
console.log('GTFS-RT data size:', gtfsVehicles.protobufData.byteLength, 'bytes');
}
catch (error) {
console.error('Error fetching vehicle locations:', error);
}
}
/**
* Example 4: Get service disruptions
*/
async function getDisruptions() {
try {
// Get all current disruptions
const disruptions = await client.disruptions.getCurrent();
console.log('Raw disruptions XML length:', disruptions.xmlData.length);
// Parse disruptions to extract useful information
const parsed = await client.disruptions.getCurrentParsed();
console.log(`Found ${parsed.length} parsed disruptions`);
parsed.forEach(disruption => {
console.log(`\n${disruption.planned ? 'PLANNED' : 'UNPLANNED'} DISRUPTION:`);
console.log(`Summary: ${disruption.summary}`);
console.log(`Authority: ${disruption.participantRef}`);
console.log(`Severity: ${disruption.severity}`);
if (disruption.startTime) {
console.log(`Start: ${new Date(disruption.startTime).toLocaleString()}`);
}
if (disruption.description) {
console.log(`Description: ${disruption.description.substring(0, 100)}...`);
}
});
// Filter for unplanned disruptions only
const unplanned = client.disruptions.filterDisruptions(parsed, { planned: false });
console.log(`\nUnplanned disruptions: ${unplanned.length}`);
}
catch (error) {
console.error('Error fetching disruptions:', error);
}
}
/**
* Example 5: Advanced searches and filtering
*/
async function advancedExamples() {
try {
// Get high-quality, BODS-compliant timetables
const qualityTimetables = await client.timetables.getHighQuality({
bodsCompliance: true,
adminArea: ['060'] // Cheshire East
});
console.log(`High-quality timetables in Cheshire East: ${qualityTimetables.count}`);
// Get recently modified timetables (last 7 days)
const lastWeek = new Date();
lastWeek.setDate(lastWeek.getDate() - 7);
const recentTimetables = await client.timetables.getRecentlyModified(lastWeek);
console.log(`Recently modified timetables: ${recentTimetables.count}`);
// Get published fares only
const publishedFares = await client.fares.getPublished({
noc: ['SCMN'],
limit: 5
});
console.log(`Published Stagecoach Manchester fares: ${publishedFares.count}`);
// Test API connectivity
const isConnected = await client.testConnection();
console.log(`API connection test: ${isConnected ? 'SUCCESS' : 'FAILED'}`);
}
catch (error) {
console.error('Error in advanced examples:', error);
}
}
/**
* Example 6: Error handling and validation
*/
async function errorHandlingExample() {
try {
// This will fail if API key is invalid
await client.timetables.search({ limit: 1 });
console.log('API key is valid');
}
catch (error) {
if (error instanceof Error) {
console.error('API Error:', error.message);
// Check if it's an HTTP error with specific status
if ('status' in error) {
const httpError = error;
switch (httpError.status) {
case 401:
console.error('Unauthorized - check your API key');
break;
case 403:
console.error('Forbidden - API key may not have required permissions');
break;
case 429:
console.error('Rate limited - too many requests');
break;
default:
console.error(`HTTP ${httpError.status}: ${httpError.message}`);
}
}
}
}
}
/**
* Run all examples
*/
async function runAllExamples() {
console.log('š BODS JavaScript Client Examples\n');
console.log('='.repeat(50));
console.log('\nš
Example 1: Timetables');
await searchTimetables();
console.log('\nšļø Example 2: Fares by Area');
await searchFaresByArea();
console.log('\nš Example 3: Real-time Vehicle Locations');
await getVehicleLocations();
console.log('\nā ļø Example 4: Service Disruptions');
await getDisruptions();
console.log('\nš Example 5: Advanced Searches');
await advancedExamples();
console.log('\nā Example 6: Error Handling');
await errorHandlingExample();
console.log('\nā
Examples completed!');
}
// Run examples if this file is executed directly
if (import.meta.main) {
runAllExamples().catch(console.error);
}
export { searchTimetables, searchFaresByArea, getVehicleLocations, getDisruptions, advancedExamples, errorHandlingExample, runAllExamples };