UNPKG

@drfrost/bods-js

Version:

JavaScript client for the UK's Bus Open Data Service (BODS) API

159 lines (149 loc) 4.54 kB
import type { Fares, FaresResponse, FaresSearchParams, BoundingBox } from '../types/index.js'; import { HttpClient } from './http-client.js'; /** * Client for interacting with the BODS Fares API * * The Fares API provides access to bus fare information. * Data is updated every 24 hours around 06:00 GMT. */ export class FaresClient { constructor(private httpClient: HttpClient) {} /** * Search for fares datasets * * @param params - Search parameters to filter fares * @returns Promise resolving to paginated fares results * * @example * ```typescript * // Find all fares for a specific operator * const fares = await client.fares.search({ * noc: ['SCMN'], * status: 'published' * }); * * // Search by geographic area (Liverpool area example) * const areaFares = await client.fares.search({ * boundingBox: [-2.930, 53.374, -3.085, 53.453] * }); * ``` */ async search(params: FaresSearchParams = {}): Promise<FaresResponse> { const response = await this.httpClient.get<FaresResponse>('/api/v1/fares/dataset', params); return response.data; } /** * Get a specific fares dataset by ID * * @param datasetId - The unique dataset identifier * @returns Promise resolving to the fares dataset * * @example * ```typescript * const fares = await client.fares.getById(86); * console.log(fares.operatorName, fares.numOfFareZones); * ``` */ async getById(datasetId: number): Promise<Fares> { const response = await this.httpClient.get<Fares>(`/api/v1/fares/dataset/${datasetId}`); return response.data; } /** * Get all fares for a specific operator * * @param noc - National Operator Code(s) * @param additionalParams - Additional search parameters * @returns Promise resolving to paginated fares results * * @example * ```typescript * const operatorFares = await client.fares.getByOperator(['SCMN', 'SCGH']); * ``` */ async getByOperator( noc: string | string[], additionalParams: Omit<FaresSearchParams, 'noc'> = {} ): Promise<FaresResponse> { return this.search({ ...additionalParams, noc: Array.isArray(noc) ? noc : [noc] }); } /** * Get fares for a specific geographic area * * @param boundingBox - Geographic bounding box [minLng, minLat, maxLng, maxLat] * @param additionalParams - Additional search parameters * @returns Promise resolving to paginated fares results * * @example * ```typescript * // Liverpool area * const liverpoolFares = await client.fares.getByArea( * [-2.930, 53.374, -3.085, 53.453] * ); * * // Manchester area * const manchesterFares = await client.fares.getByArea( * [-2.3, 53.4, -2.2, 53.5] * ); * ``` */ async getByArea( boundingBox: BoundingBox, additionalParams: Omit<FaresSearchParams, 'boundingBox'> = {} ): Promise<FaresResponse> { return this.search({ ...additionalParams, boundingBox }); } /** * Get published fares datasets only * * @param additionalParams - Additional search parameters * @returns Promise resolving to paginated fares results * * @example * ```typescript * const publishedFares = await client.fares.getPublished({ * limit: 100 * }); * ``` */ async getPublished( additionalParams: Omit<FaresSearchParams, 'status'> = {} ): Promise<FaresResponse> { return this.search({ ...additionalParams, status: 'published' }); } /** * 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.fares.createBoundingBox(53.4808, -2.2426, 10); // Manchester, 10km radius * const nearbyFares = await client.fares.getByArea(bbox); * ``` */ createBoundingBox(centerLat: number, centerLng: number, radiusKm: number): BoundingBox { // 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 ]; } }