gbfs-system
Version:
A Node.js package that enables real-time data retrieval from GBFS (General Bikeshare Feed Specification) using the auto-discovery URLs provided by MobilityData. It allows developers to easily access and utilize live bikeshare system information, station i
210 lines • 8.05 kB
JavaScript
import axios from "axios";
/**
* Get realtime feed data for a given GBFS (General Bikeshare Feed Specification) compliant system.
* The feed must be already provided by the operator. exp : station_information, station_status, system_info, etc.
* Visit MobiltyData website or github repo for more informations : https://github.com/MobilityData/gbfs
*/
export class Gbfs {
autoDiscoveryURL;
preferredFeedLanguage;
gbfsData;
gbfsSupportedLanguages;
/**
* Instanciate the Gbfs class.
* @param autoDiscoveryURL - The URL of the GBFS auto-discovery endpoint.
* @param preferredFeedLanguage - [Optional] The preferred language code for fetching GBFS data.
* @returns A new instance of the Gbfs class.
*/
constructor(autoDiscoveryURL, preferredFeedLanguage) {
this.autoDiscoveryURL = autoDiscoveryURL;
this.preferredFeedLanguage = preferredFeedLanguage;
}
/**
* Create an instance of Gbfs and retrieve all found feeds URLs
* @param {string} autoDiscoveryURL - The URL of the GBFS system.
* That URL can be found in the operator API documentation, or by checking the MobilityData maintained system.csv.
* The {Systems} module of this library also provide a way to get the autoDiscoveryURL for a registered operator.
* @param {string} preferredFeedLanguage - [Optional] - The language code you want to get the feeds data from.
* Some operators provide their gbfs system in multiple languages depending on their public's needs.
* @
*/
static async initialize(autoDiscoveryURL, preferredFeedLanguage) {
const instance = new Gbfs(autoDiscoveryURL, preferredFeedLanguage);
await instance.ensureInitialized();
return instance;
}
/**
* Get the current defined preferred feed language.
* @param language - The language code to set as the preferred feed language.
*/
set setPreferredFeedLanguage(language) {
this.preferredFeedLanguage = language;
}
/**
* Getter for the preferred feed language.
*
* @returns The currently set preferred language code.
*/
get getPreferredFeedLanguage() {
return this.preferredFeedLanguage;
}
/**
* Get all supported feed languages.
* @returns An array of all feeds languages found for the current gbfs system. // ['en', 'fr']
*/
getSupportedLanguages() {
return this.gbfsSupportedLanguages;
}
/**
* Checks if a given language is supported by the GBFS system.
* @param language - The language code to check for support. exp: 'en'
* @returns True if the language is supported.
*/
isLanguageSupported(language) {
return this.gbfsSupportedLanguages?.includes(language) ?? false;
}
/**
* Ensures the GBFS data is properly initialized and all required data stored.
* @returns {void}
*/
async ensureInitialized() {
if (this.gbfsData && this.gbfsSupportedLanguages)
return;
try {
const { data } = await axios.get(this.autoDiscoveryURL, {
headers: { "Content-Type": "application/json" },
});
this.gbfsData = data.data;
this.gbfsSupportedLanguages = Object.keys(this.gbfsData);
}
catch (error) {
throw new Error(`Failed to initialize URLs: ${error}`);
}
}
/**
* Finds the URL of a specific feed.
* @param {string} feedName - The name of the feed to find the URL for.
* @returns The URL of the specified feed or undefined if not found.
* If there's no defined preferred feed language, it uses the first found feed object to process.
*/
async findFeedUrl(feedName) {
try {
await this.ensureInitialized();
if (this.preferredFeedLanguage &&
!this.gbfsSupportedLanguages.includes(this.preferredFeedLanguage)) {
throw new Error(`The specified feed language '${this.preferredFeedLanguage}' doesn't exist in that gbfs system. The available languages are : ${this.gbfsSupportedLanguages.join(", ")}`);
}
const feeds = this.preferredFeedLanguage
? this.gbfsData[this.preferredFeedLanguage].feeds
: Object.values(this.gbfsData)[0].feeds;
return feeds?.find((feed) => feed.name === feedName)?.url;
}
catch (error) {
throw error;
}
}
/**
* Fetches data from a specified URL.
* @param {string} url - The URL to fetch data from.
* @returns {Promise<unknown>} The data fetched from the specified URL.
*/
async fetchData(url) {
try {
const { data } = await axios.get(url, {
headers: { "Content-Type": "application/json" },
});
return data;
}
catch (error) {
throw new Error(`Failed to retrieve data from ${url}: ${error}`);
}
}
/**
* Gets data for a specified feed.
* @param {string} feedName - The name of the feed to get data for.
* @returns {Promise<unknown>} The data for the specified feed.
*/
async getFeedData(feedName) {
const feedURL = await this.findFeedUrl(feedName);
return this.fetchData(feedURL);
}
async stationInfo(stationId) {
try {
const { data: { stations }, } = (await this.getFeedData("station_information"));
if (stationId) {
const station = stations.find((station) => station.station_id === stationId);
if (!station) {
throw new Error(`Station with ID ${stationId} not found.`);
}
return station;
}
return stations;
}
catch (error) {
throw error;
}
}
async stationStatus(stationId) {
try {
const { data: { stations }, } = (await this.getFeedData("station_status"));
if (stationId) {
const station = stations.find((station) => station.station_id === stationId);
if (!station) {
throw new Error(`Station with ID ${stationId} not found.`);
}
return station;
}
return stations;
}
catch (error) {
throw error;
}
}
/**
* Fetches system_information feed data.
* @returns The system_information feed data as an object.
*/
async systemInfo() {
try {
const { data } = (await this.getFeedData("system_information"));
if (!data.system_id)
throw new Error("Unable to retrieve the system informations");
return data;
}
catch (error) {
throw error;
}
}
async stationUnified(stationId) {
try {
let stationInfoData;
let stationStatusData;
if (typeof stationId === 'string') {
stationInfoData = await this.stationInfo(stationId);
stationStatusData = await this.stationStatus(stationId);
return {
...stationStatusData,
...stationInfoData,
};
}
else {
stationInfoData = await this.stationInfo();
stationStatusData = await this.stationStatus();
const combinedData = stationInfoData.map((info) => {
const status = Array.isArray(stationStatusData)
? stationStatusData.find((status) => status.station_id === info.station_id)
: stationStatusData;
return {
...status,
...info,
};
});
return combinedData;
}
}
catch (error) {
throw error;
}
}
}
//# sourceMappingURL=Gbfs.js.map