@drfrost/bods-js
Version:
JavaScript client for the UK's Bus Open Data Service (BODS) API
197 lines (196 loc) • 6.34 kB
JavaScript
/**
* Client for interacting with the BODS Automatic Vehicle Location (AVL) API
*
* The AVL API provides real-time bus location data in two formats:
* - SIRI-VM (XML format)
* - GTFS-RT (Protocol Buffers format)
*
* Data is updated every 10 seconds.
*/
export class AVLClient {
constructor(httpClient) {
this.httpClient = httpClient;
}
/**
* Get real-time vehicle locations in SIRI-VM format (XML)
*
* @param params - Search parameters to filter vehicle data
* @returns Promise resolving to SIRI-VM XML data
*
* @example
* ```typescript
* // Get all vehicles from specific operators
* const vehicles = await client.avl.getSIRIVM({
* operatorRef: ['SCGH', 'SCLI']
* });
*
* // Get vehicles in a specific area (Liverpool)
* const areaVehicles = await client.avl.getSIRIVM({
* boundingBox: [-2.930, 53.374, -3.085, 53.453]
* });
*
* // Get vehicles on a specific line
* const lineVehicles = await client.avl.getSIRIVM({
* lineRef: '85A'
* });
* ```
*/
async getSIRIVM(params = {}) {
const response = await this.httpClient.get('/api/v1/datafeed', params);
return { xmlData: response.data };
}
/**
* Get a specific SIRI-VM datafeed by ID
*
* @param datafeedId - The unique datafeed identifier
* @returns Promise resolving to SIRI-VM XML data
*
* @example
* ```typescript
* const datafeed = await client.avl.getSIRIVMById(123);
* ```
*/
async getSIRIVMById(datafeedId) {
const response = await this.httpClient.get(`/api/v1/datafeed/${datafeedId}/`);
return { xmlData: response.data };
}
/**
* Get real-time vehicle locations in GTFS-RT format (Protocol Buffers)
*
* @param params - Search parameters to filter vehicle data
* @returns Promise resolving to GTFS-RT protobuf data
*
* @example
* ```typescript
* // Get all vehicles in GTFS-RT format
* const vehicles = await client.avl.getGTFSRT({
* boundingBox: [-2.930, 53.374, -3.085, 53.453]
* });
*
* // Get vehicles for a specific route
* const routeVehicles = await client.avl.getGTFSRT({
* routeId: 'route_123'
* });
* ```
*/
async getGTFSRT(params = {}) {
const response = await this.httpClient.get('/api/v1/gtfsrtdatafeed/', params);
return { protobufData: response.data };
}
/**
* Get vehicles by operator in SIRI-VM format
*
* @param operatorRef - Operator reference(s) (often National Operator Code)
* @param additionalParams - Additional search parameters
* @returns Promise resolving to SIRI-VM XML data
*
* @example
* ```typescript
* const stagecoachVehicles = await client.avl.getByOperator(['SCMN', 'SCGH']);
* ```
*/
async getByOperator(operatorRef, additionalParams = {}) {
return this.getSIRIVM({
...additionalParams,
operatorRef: Array.isArray(operatorRef) ? operatorRef : [operatorRef]
});
}
/**
* Get vehicles by line reference in SIRI-VM format
*
* @param lineRef - Line reference (route number/identifier)
* @param additionalParams - Additional search parameters
* @returns Promise resolving to SIRI-VM XML data
*
* @example
* ```typescript
* const line85AVehicles = await client.avl.getByLine('85A');
* ```
*/
async getByLine(lineRef, additionalParams = {}) {
return this.getSIRIVM({
...additionalParams,
lineRef
});
}
/**
* Get vehicles by vehicle reference in SIRI-VM format
*
* @param vehicleRef - Vehicle reference/identifier
* @param additionalParams - Additional search parameters
* @returns Promise resolving to SIRI-VM XML data
*
* @example
* ```typescript
* const specificVehicle = await client.avl.getByVehicle('BUSC-001');
* ```
*/
async getByVehicle(vehicleRef, additionalParams = {}) {
return this.getSIRIVM({
...additionalParams,
vehicleRef
});
}
/**
* Get vehicles in a specific geographic area
*
* @param boundingBox - Geographic bounding box [minLng, minLat, maxLng, maxLat]
* @param format - Data format ('siri-vm' or 'gtfs-rt')
* @param additionalParams - Additional search parameters
* @returns Promise resolving to location data in specified format
*
* @example
* ```typescript
* // Liverpool area in SIRI-VM format
* const liverpoolVehicles = await client.avl.getByArea(
* [-2.930, 53.374, -3.085, 53.453],
* 'siri-vm'
* );
*
* // Manchester area in GTFS-RT format
* const manchesterVehicles = await client.avl.getByArea(
* [-2.3, 53.4, -2.2, 53.5],
* 'gtfs-rt'
* );
* ```
*/
async getByArea(boundingBox, format = 'siri-vm', additionalParams = {}) {
if (format === 'gtfs-rt') {
return this.getGTFSRT({
...additionalParams,
boundingBox
});
}
return this.getSIRIVM({
...additionalParams,
boundingBox
});
}
/**
* Helper method to create bounding box from center point and radius
*
* @param centerLat - Center latitude
* @param centerLng - Center longitude
* @param radiusKm - Radius in kilometers
* @returns Bounding box coordinates
*
* @example
* ```typescript
* const bbox = client.avl.createBoundingBox(53.4808, -2.2426, 5); // Manchester, 5km radius
* const nearbyVehicles = await client.avl.getByArea(bbox);
* ```
*/
createBoundingBox(centerLat, centerLng, radiusKm) {
// Approximate degrees per kilometer
const latDegPerKm = 1 / 111;
const lngDegPerKm = 1 / (111 * Math.cos(centerLat * Math.PI / 180));
const latOffset = radiusKm * latDegPerKm;
const lngOffset = radiusKm * lngDegPerKm;
return [
centerLng - lngOffset, // minLng
centerLat - latOffset, // minLat
centerLng + lngOffset, // maxLng
centerLat + latOffset // maxLat
];
}
}