UNPKG

@drfrost/bods-js

Version:

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

166 lines (165 loc) 5 kB
/** * Utility functions for working with BODS data */ /** * Create a bounding box from a center point and radius * * @param centerLat - Center latitude * @param centerLng - Center longitude * @param radiusKm - Radius in kilometers * @returns Bounding box coordinates [minLng, minLat, maxLng, maxLat] * * @example * ```typescript * import { createBoundingBox } from 'bods-js/utils'; * * // Manchester city center, 10km radius * const bbox = createBoundingBox(53.4808, -2.2426, 10); * const vehicles = await client.avl.getByArea(bbox); * ``` */ export function 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 ]; } /** * Check if a point is within a bounding box * * @param lat - Point latitude * @param lng - Point longitude * @param boundingBox - Bounding box to check against * @returns True if point is within the bounding box * * @example * ```typescript * import { isPointInBoundingBox } from 'bods-js/utils'; * * const manchester = createBoundingBox(53.4808, -2.2426, 10); * const isInManchester = isPointInBoundingBox(53.4808, -2.2426, manchester); // true * ``` */ export function isPointInBoundingBox(lat, lng, boundingBox) { const [minLng, minLat, maxLng, maxLat] = boundingBox; return lng >= minLng && lng <= maxLng && lat >= minLat && lat <= maxLat; } /** * Calculate distance between two points using Haversine formula * * @param lat1 - First point latitude * @param lng1 - First point longitude * @param lat2 - Second point latitude * @param lng2 - Second point longitude * @returns Distance in kilometers * * @example * ```typescript * import { calculateDistance } from 'bods-js/utils'; * * const distance = calculateDistance(53.4808, -2.2426, 51.5074, -0.1278); // Manchester to London * console.log(`Distance: ${distance.toFixed(2)}km`); * ``` */ export function calculateDistance(lat1, lng1, lat2, lng2) { const R = 6371; // Earth's radius in kilometers const dLat = (lat2 - lat1) * Math.PI / 180; const dLng = (lng2 - lng1) * Math.PI / 180; const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLng / 2) * Math.sin(dLng / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; } /** * Format a date for BODS API queries * * @param date - Date to format * @returns ISO string formatted for BODS API * * @example * ```typescript * import { formatDateForAPI } from 'bods-js/utils'; * * const formatted = formatDateForAPI(new Date()); * const timetables = await client.timetables.search({ * modifiedDate: formatted * }); * ``` */ export function formatDateForAPI(date) { return date.toISOString(); } /** * Parse an ISO date string to Date object * * @param dateString - ISO date string * @returns Date object * * @example * ```typescript * import { parseDateFromAPI } from 'bods-js/utils'; * * const date = parseDateFromAPI('2023-01-01T12:45:00+00:00'); * ``` */ export function parseDateFromAPI(dateString) { return new Date(dateString); } /** * Validate National Operator Code format * * @param noc - National Operator Code to validate * @returns True if NOC format appears valid * * @example * ```typescript * import { isValidNOC } from 'bods-js/utils'; * * console.log(isValidNOC('SCMN')); // true * console.log(isValidNOC('invalid')); // false * ``` */ export function isValidNOC(noc) { // NOCs are typically 4 characters, alphanumeric return /^[A-Z0-9]{4}$/.test(noc); } /** * Validate API key format * * @param apiKey - API key to validate * @returns True if API key format appears valid * * @example * ```typescript * import { isValidAPIKey } from 'bods-js/utils'; * * const isValid = isValidAPIKey('your-api-key'); * ``` */ export function isValidAPIKey(apiKey) { // API keys are typically 40 character hex strings return /^[a-f0-9]{40}$/.test(apiKey); } /** * Get common UK city bounding boxes */ export const UK_CITIES = { LONDON: createBoundingBox(51.5074, -0.1278, 30), MANCHESTER: createBoundingBox(53.4808, -2.2426, 15), BIRMINGHAM: createBoundingBox(52.4862, -1.8904, 15), LEEDS: createBoundingBox(53.8008, -1.5491, 15), LIVERPOOL: createBoundingBox(53.4084, -2.9916, 15), BRISTOL: createBoundingBox(51.4545, -2.5879, 15), SHEFFIELD: createBoundingBox(53.3811, -1.4701, 15), LEICESTER: createBoundingBox(52.6369, -1.1398, 10), COVENTRY: createBoundingBox(52.4068, -1.5197, 10), BRADFORD: createBoundingBox(53.7959, -1.7594, 10) };