UNPKG

tfl-ts

Version:

🚇 Fully-typed TypeScript client for Transport for London (TfL) API • Zero dependencies • Auto-generated types • Real-time arrivals • Journey planning • Universal compatibility

341 lines (340 loc) 14.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BikePoint = void 0; const stripTypes_1 = require("./utils/stripTypes"); const bikePoint_1 = require("./utils/bikePoint"); /** * Bike point class for interacting with TfL Bike Point API endpoints * @example * // Get all bike points * const allBikePoints = await client.bikePoint.get(); * * // Get specific bike point by ID * const bikePoint = await client.bikePoint.getById('BikePoints_1'); * * // Get bike point with original additional properties preserved * const bikePointWithTypes = await client.bikePoint.getById('BikePoints_1', { keepTflTypes: true }); * console.log('Original properties:', bikePointWithTypes.additionalProperties); * * // Search for bike points * const searchResults = await client.bikePoint.search({ query: 'St. James' }); * * // Get bike points within radius * const nearbyBikePoints = await client.bikePoint.getByRadius({ * lat: 51.508418, * lon: -0.067048, * radius: 500 * }); * * // Get bike points within bounding box * const areaBikePoints = await client.bikePoint.getByBounds({ * point1: { lat: 51.516027, lon: -0.119842 }, * point2: { lat: 51.513089, lon: -0.115669 } * }); * * // Access static metadata (no HTTP request) * const endpoints = client.bikePoint.ENDPOINTS; * const totalEndpoints = client.bikePoint.TOTAL_ENDPOINTS; * * // Validate user input before making API calls * const userInput = ['BikePoints_1', 'invalid-id']; * const validIds = userInput.filter(id => id.startsWith('BikePoints_')); * if (validIds.length !== userInput.length) { * throw new Error(`Invalid bike point IDs: ${userInput.filter(id => !id.startsWith('BikePoints_')).join(', ')}`); * } */ class BikePoint { constructor(api) { this.api = api; /** Transport mode for bike points */ this.MODE = 'cycle-hire'; /** Bike point property categories */ this.PROPERTY_CATEGORIES = [ 'Description' ]; /** Bike point property keys */ this.PROPERTY_KEYS = [ 'NbBikes', 'NbDocks', 'NbEmptyDocks' ]; } /** * Gets all bike point locations with their current status * * This method returns all bike point locations in London with their current status. * The response includes structured status information with bikes, docks, spaces, and broken docks. * * @param options - Options for the request * @returns Promise resolving to an array of bike point status information * @example * // Get all bike points with status * const allBikePoints = await client.bikePoint.get(); * * // Process bike point data * allBikePoints.forEach(status => { * console.log(`${status.name}: ${status.bikes} bikes, ${status.spaces} spaces available`); * * if (status.brokenDocks > 0) { * console.log(`⚠️ ${status.brokenDocks} broken docks detected`); * } * }); * * // Find bike points with available bikes * const availableBikePoints = allBikePoints.filter(status => status.bikes > 0); * * // Find bike points with available spaces * const availableSpaces = allBikePoints.filter(status => status.spaces > 0); * * // Find bike points with electric bikes * const eBikePoints = allBikePoints.filter(status => status.eBikes > 0); * * // Get all bike points with original additional properties preserved * const allBikePointsWithTypes = await client.bikePoint.get({ keepTflTypes: true }); * * // Access original properties for debugging or advanced processing * allBikePointsWithTypes.forEach(status => { * if (status.additionalProperties) { * console.log(`${status.name} has ${status.additionalProperties.length} original properties`); * status.additionalProperties.forEach(prop => { * console.log(` ${prop.key}: ${prop.value} (${prop.category})`); * }); * } * }); */ async get(options = {}) { const rawData = await this.api.bikePoint.bikePointGetAll() .then((response) => (0, stripTypes_1.stripTypeFields)(response.data, options.keepTflTypes)); return rawData.map((bikePoint) => (0, bikePoint_1.extractStatus)(bikePoint, options.keepTflTypes)); } /** * Gets the bike point with the given id * * This method returns detailed information about a specific bike point, * including its current status (number of bikes, docks, and spaces). * * @param id - A bike point id (a list of ids can be obtained from the get() method) * @param options - Options for the request * @returns Promise resolving to bike point status information * @example * // Get specific bike point by ID * const bikePoint = await client.bikePoint.getById('BikePoints_1'); * * // Display status information * console.log(`Bike Point: ${bikePoint.name}`); * console.log(`Available bikes: ${bikePoint.bikes}`); * console.log(`Available spaces: ${bikePoint.spaces}`); * console.log(`Total docks: ${bikePoint.docks}`); * * if (bikePoint.brokenDocks > 0) { * console.log(`Broken docks: ${bikePoint.brokenDocks}`); * } * * if (bikePoint.eBikes > 0) { * console.log(`Electric bikes: ${bikePoint.eBikes}`); * } * * // Get bike point with original additional properties preserved * const bikePointWithTypes = await client.bikePoint.getById('BikePoints_1', { keepTflTypes: true }); * * // Access all original properties for advanced processing * if (bikePointWithTypes.additionalProperties) { * console.log('All original properties:'); * bikePointWithTypes.additionalProperties.forEach(prop => { * console.log(` ${prop.key}: ${prop.value} (${prop.category}) - Modified: ${prop.modified}`); * }); * } */ async getById(id, options = {}) { const rawData = await this.api.bikePoint.bikePointGet(id) .then((response) => (0, stripTypes_1.stripTypeFields)(response.data, options.keepTflTypes)); return (0, bikePoint_1.extractStatus)(rawData, options.keepTflTypes); } /** * Search for bike stations by their name * * This method searches for bike stations by their name. A bike point's name often * contains information about the name of the street or nearby landmarks. * Note that the search result does not contain the PlaceProperties i.e. the status * or occupancy of the BikePoint. To get that information, you should retrieve * the BikePoint by its id using getById(). * * @param options - Query options for bike point search * @returns Promise resolving to an array of bike point information * @example * // Search for bike points by name * const searchResults = await client.bikePoint.search({ query: 'St. James' }); * * // Search with type fields preserved * const searchResults = await client.bikePoint.search({ * query: 'River Street', * keepTflTypes: true * }); * * // Process search results * searchResults.forEach(bikePoint => { * console.log(`Found: ${bikePoint.commonName} (${bikePoint.id})`); * console.log(`Location: ${bikePoint.lat}, ${bikePoint.lon}`); * * // Get detailed status for each result * client.bikePoint.getById(bikePoint.id!).then(detailedBikePoint => { * console.log(`Status: ${detailedBikePoint.bikes} bikes, ${detailedBikePoint.spaces} spaces`); * }); * }); * * // Search for bike points near landmarks * const nearLandmarks = await client.bikePoint.search({ query: 'Tower Bridge' }); * const nearStations = await client.bikePoint.search({ query: 'Kings Cross' }); */ async search(options) { const { query, keepTflTypes } = options; return this.api.bikePoint.bikePointSearch({ query }) .then((response) => (0, stripTypes_1.stripTypeFields)(response.data, keepTflTypes)); } /** * Gets bike points within a radius of a location * * This method returns bike points within a specified radius of a given location. * Uses the TfL API endpoint: /BikePoint?lat={lat}&lon={lon}&radius={radius} * * @param options - Query options for radius-based bike point search * @returns Promise resolving to bike point radius response * @example * // Get bike points within 500m of a location * const nearbyBikePoints = await client.bikePoint.getByRadius({ * lat: 51.508418, * lon: -0.067048, * radius: 500 * }); * * console.log(`Found ${nearbyBikePoints.places.length} bike points within ${options.radius}m`); * console.log(`Center point: ${nearbyBikePoints.centrePoint[0]}, ${nearbyBikePoints.centrePoint[1]}`); * * // Process each bike point * nearbyBikePoints.places.forEach(bikePoint => { * console.log(`${bikePoint.name}: ${bikePoint.bikes} bikes, ${bikePoint.spaces} spaces (${bikePoint.distance?.toFixed(0)}m away)`); * }); * * // Find closest bike point with available bikes * const closestWithBikes = nearbyBikePoints.places * .filter(bikePoint => bikePoint.bikes > 0) * .sort((a, b) => (a.distance || 0) - (b.distance || 0))[0]; * * if (closestWithBikes) { * console.log(`Closest bike point with bikes: ${closestWithBikes.name} (${closestWithBikes.distance?.toFixed(0)}m)`); * } * * // Get bike points with original additional properties preserved * const nearbyBikePointsWithTypes = await client.bikePoint.getByRadius({ * lat: 51.508418, * lon: -0.067048, * radius: 500, * keepTflTypes: true * }); * * // Access original properties for each bike point * nearbyBikePointsWithTypes.places.forEach(bikePoint => { * if (bikePoint.additionalProperties) { * console.log(`${bikePoint.name} has ${bikePoint.additionalProperties.length} original properties`); * } * }); */ async getByRadius(options) { const { lat, lon, radius = 200, keepTflTypes } = options; // Build query parameters const queryParams = new URLSearchParams(); queryParams.append('lat', lat.toString()); queryParams.append('lon', lon.toString()); if (radius !== 200) { queryParams.append('radius', radius.toString()); } // Make direct API call to the radius endpoint const response = await this.api.request({ path: `/BikePoint?${queryParams.toString()}`, method: 'GET', format: 'json' }); const rawData = (0, stripTypes_1.stripTypeFields)(response.data, keepTflTypes); // Transform the data to include status information return { centrePoint: rawData.centrePoint, places: rawData.places.map((bikePoint) => ({ ...(0, bikePoint_1.extractStatus)(bikePoint, keepTflTypes), distance: bikePoint.distance })) }; } /** * Gets bike points within a bounding box * * This method returns bike points within a bounding box defined by two points. * Uses the TfL API endpoint: /BikePoint?swLat={swLat}&swLon={swLon}&neLat={neLat}&neLon={neLon} * * @param options - Query options for bounding box bike point search * @returns Promise resolving to an array of bike point status information * @example * // Get bike points within a bounding box * const areaBikePoints = await client.bikePoint.getByBounds({ * point1: { lat: 51.516027, lon: -0.119842 }, * point2: { lat: 51.513089, lon: -0.115669 } * }); * * console.log(`Found ${areaBikePoints.length} bike points in the area`); * * // Process each bike point * areaBikePoints.forEach(bikePoint => { * console.log(`${bikePoint.name}: ${bikePoint.bikes} bikes, ${bikePoint.spaces} spaces`); * console.log(`Location: ${bikePoint.lat}, ${bikePoint.lon}`); * }); * * // Find bike points with most available bikes in the area * const topBikePoints = areaBikePoints * .sort((a, b) => b.bikes - a.bikes) * .slice(0, 3); * * console.log('Top 3 bike points with most bikes:'); * topBikePoints.forEach((bikePoint, index) => { * console.log(`${index + 1}. ${bikePoint.name}: ${bikePoint.bikes} bikes`); * }); * * // Get bike points with original additional properties preserved * const areaBikePointsWithTypes = await client.bikePoint.getByBounds({ * point1: { lat: 51.516027, lon: -0.119842 }, * point2: { lat: 51.513089, lon: -0.115669 }, * keepTflTypes: true * }); * * // Access original properties for debugging * areaBikePointsWithTypes.forEach(bikePoint => { * if (bikePoint.additionalProperties) { * const lastModified = bikePoint.additionalProperties * .map(prop => new Date(prop.modified || '')) * .sort((a, b) => b.getTime() - a.getTime())[0]; * console.log(`${bikePoint.name} last updated: ${lastModified}`); * } * }); */ async getByBounds(options) { const { point1, point2, keepTflTypes } = options; // Determine southwest and northeast corners const swLat = Math.min(point1.lat, point2.lat); const swLon = Math.min(point1.lon, point2.lon); const neLat = Math.max(point1.lat, point2.lat); const neLon = Math.max(point1.lon, point2.lon); // Build query parameters const queryParams = new URLSearchParams(); queryParams.append('swLat', swLat.toString()); queryParams.append('swLon', swLon.toString()); queryParams.append('neLat', neLat.toString()); queryParams.append('neLon', neLon.toString()); // Make direct API call to the bounds endpoint const response = await this.api.request({ path: `/BikePoint?${queryParams.toString()}`, method: 'GET', format: 'json' }); const rawData = (0, stripTypes_1.stripTypeFields)(response.data, keepTflTypes); return rawData.map((bikePoint) => (0, bikePoint_1.extractStatus)(bikePoint, keepTflTypes)); } } exports.BikePoint = BikePoint;