mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
395 lines
11 KiB
JavaScript
395 lines
11 KiB
JavaScript
function buildBcycleUrls(systemId) {
|
|
return {
|
|
stationInformationUrl: `https://gbfs.bcycle.com/${systemId}/station_information.json`,
|
|
stationStatusUrl: `https://gbfs.bcycle.com/${systemId}/station_status.json`,
|
|
};
|
|
}
|
|
function bcycleEntry({
|
|
id,
|
|
city,
|
|
centerLat,
|
|
centerLon,
|
|
systemId,
|
|
loadRadiusKm = 100,
|
|
provider = 'BCycle',
|
|
}) {
|
|
return {
|
|
id,
|
|
city,
|
|
centerLat,
|
|
centerLon,
|
|
loadRadiusKm,
|
|
provider,
|
|
...buildBcycleUrls(systemId),
|
|
};
|
|
}
|
|
const RAW_GBFS_CITY_REGISTRY = [
|
|
{
|
|
id: 'nyc-citibike',
|
|
city: 'New York, NY',
|
|
centerLat: 40.7484,
|
|
centerLon: -73.9967,
|
|
loadRadiusKm: 140,
|
|
stationInformationUrl:
|
|
'https://gbfs.lyft.com/gbfs/2.3/bkn/en/station_information.json',
|
|
stationStatusUrl:
|
|
'https://gbfs.lyft.com/gbfs/2.3/bkn/en/station_status.json',
|
|
provider: 'Citi Bike',
|
|
},
|
|
{
|
|
id: 'chicago-divvy',
|
|
city: 'Chicago, IL',
|
|
centerLat: 41.8781,
|
|
centerLon: -87.6298,
|
|
loadRadiusKm: 120,
|
|
stationInformationUrl:
|
|
'https://gbfs.lyft.com/gbfs/2.3/chi/en/station_information.json',
|
|
stationStatusUrl:
|
|
'https://gbfs.lyft.com/gbfs/2.3/chi/en/station_status.json',
|
|
provider: 'Divvy',
|
|
},
|
|
{
|
|
id: 'dc-capital-bikeshare',
|
|
city: 'Washington, DC',
|
|
centerLat: 38.9072,
|
|
centerLon: -77.0369,
|
|
loadRadiusKm: 120,
|
|
stationInformationUrl:
|
|
'https://gbfs.lyft.com/gbfs/2.3/dca-cabi/en/station_information.json',
|
|
stationStatusUrl:
|
|
'https://gbfs.lyft.com/gbfs/2.3/dca-cabi/en/station_status.json',
|
|
provider: 'Capital Bikeshare',
|
|
},
|
|
{
|
|
id: 'sf-bay-wheels',
|
|
city: 'San Francisco, CA',
|
|
centerLat: 37.7749,
|
|
centerLon: -122.4194,
|
|
loadRadiusKm: 110,
|
|
stationInformationUrl:
|
|
'https://gbfs.lyft.com/gbfs/2.3/bay/en/station_information.json',
|
|
stationStatusUrl:
|
|
'https://gbfs.lyft.com/gbfs/2.3/bay/en/station_status.json',
|
|
provider: 'Bay Wheels',
|
|
},
|
|
{
|
|
id: 'boston-bluebikes',
|
|
city: 'Boston, MA',
|
|
centerLat: 42.3601,
|
|
centerLon: -71.0589,
|
|
loadRadiusKm: 100,
|
|
stationInformationUrl:
|
|
'https://gbfs.bluebikes.com/gbfs/en/station_information.json',
|
|
stationStatusUrl: 'https://gbfs.bluebikes.com/gbfs/en/station_status.json',
|
|
provider: 'Blue Bikes',
|
|
},
|
|
{
|
|
id: 'philadelphia-indego',
|
|
city: 'Philadelphia, PA',
|
|
centerLat: 39.9526,
|
|
centerLon: -75.1652,
|
|
loadRadiusKm: 100,
|
|
stationInformationUrl:
|
|
'https://gbfs.bcycle.com/bcycle_indego/station_information.json',
|
|
stationStatusUrl:
|
|
'https://gbfs.bcycle.com/bcycle_indego/station_status.json',
|
|
provider: 'Indego',
|
|
},
|
|
{
|
|
id: 'portland-biketown',
|
|
city: 'Portland, OR',
|
|
centerLat: 45.5152,
|
|
centerLon: -122.6784,
|
|
loadRadiusKm: 95,
|
|
stationInformationUrl:
|
|
'https://gbfs.biketownpdx.com/gbfs/2.3/en/station_information.json',
|
|
stationStatusUrl:
|
|
'https://gbfs.biketownpdx.com/gbfs/2.3/en/station_status.json',
|
|
provider: 'BIKETOWN',
|
|
},
|
|
{
|
|
id: 'la-metro-bike',
|
|
city: 'Los Angeles, CA',
|
|
centerLat: 34.0522,
|
|
centerLon: -118.2437,
|
|
loadRadiusKm: 120,
|
|
stationInformationUrl:
|
|
'https://gbfs.bcycle.com/bcycle_lametro/station_information.json',
|
|
stationStatusUrl:
|
|
'https://gbfs.bcycle.com/bcycle_lametro/station_status.json',
|
|
provider: 'Metro Bike',
|
|
},
|
|
{
|
|
id: 'austin-capmetro',
|
|
city: 'Austin, TX',
|
|
centerLat: 30.2672,
|
|
centerLon: -97.7431,
|
|
loadRadiusKm: 90,
|
|
stationInformationUrl:
|
|
'https://austin.publicbikesystem.net/customer/gbfs/v2/en/station_information.json',
|
|
stationStatusUrl:
|
|
'https://austin.publicbikesystem.net/customer/gbfs/v2/en/station_status.json',
|
|
provider: 'CapMetro',
|
|
},
|
|
{
|
|
id: 'honolulu-biki',
|
|
city: 'Honolulu, HI',
|
|
centerLat: 21.3069,
|
|
centerLon: -157.8583,
|
|
loadRadiusKm: 90,
|
|
stationInformationUrl:
|
|
'https://hon.publicbikesystem.net/customer/gbfs/v2/en/station_information.json',
|
|
stationStatusUrl:
|
|
'https://hon.publicbikesystem.net/customer/gbfs/v2/en/station_status.json',
|
|
provider: 'Biki',
|
|
},
|
|
{
|
|
id: 'columbus-cogo',
|
|
city: 'Columbus, OH',
|
|
centerLat: 39.9612,
|
|
centerLon: -82.9988,
|
|
loadRadiusKm: 90,
|
|
stationInformationUrl:
|
|
'https://gbfs.cogobikeshare.com/gbfs/2.3/en/station_information.json',
|
|
stationStatusUrl:
|
|
'https://gbfs.cogobikeshare.com/gbfs/2.3/en/station_status.json',
|
|
provider: 'CoGo',
|
|
},
|
|
{
|
|
id: 'chattanooga-bikechatt',
|
|
city: 'Chattanooga, TN',
|
|
centerLat: 35.0456,
|
|
centerLon: -85.3097,
|
|
loadRadiusKm: 90,
|
|
stationInformationUrl:
|
|
'https://chat.publicbikesystem.net/customer/gbfs/v2/en/station_information.json',
|
|
stationStatusUrl:
|
|
'https://chat.publicbikesystem.net/customer/gbfs/v2/en/station_status.json',
|
|
provider: 'Bike Chattanooga',
|
|
},
|
|
bcycleEntry({
|
|
id: 'boulder-bcycle',
|
|
city: 'Boulder, CO',
|
|
centerLat: 40.015,
|
|
centerLon: -105.2705,
|
|
systemId: 'bcycle_boulder',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'milwaukee-bublr',
|
|
city: 'Milwaukee, WI',
|
|
centerLat: 43.0389,
|
|
centerLon: -87.9065,
|
|
systemId: 'bcycle_bublr',
|
|
provider: 'Bublr',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'madison-bcycle',
|
|
city: 'Madison, WI',
|
|
centerLat: 43.0731,
|
|
centerLon: -89.4012,
|
|
systemId: 'bcycle_madison',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'nashville-bcycle',
|
|
city: 'Nashville, TN',
|
|
centerLat: 36.1627,
|
|
centerLon: -86.7816,
|
|
systemId: 'bcycle_nashville',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'salt-lake-greenbike',
|
|
city: 'Salt Lake City, UT',
|
|
centerLat: 40.7608,
|
|
centerLon: -111.891,
|
|
systemId: 'bcycle_greenbikeslc',
|
|
provider: 'GREENbike',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'san-antonio-bcycle',
|
|
city: 'San Antonio, TX',
|
|
centerLat: 29.4241,
|
|
centerLon: -98.4936,
|
|
systemId: 'bcycle_sanantonio',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'cincinnati-red-bike',
|
|
city: 'Cincinnati, OH',
|
|
centerLat: 39.1031,
|
|
centerLon: -84.512,
|
|
systemId: 'bcycle_cincyredbike',
|
|
provider: 'Red Bike',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'el-paso-bcycle',
|
|
city: 'El Paso, TX',
|
|
centerLat: 31.7619,
|
|
centerLon: -106.485,
|
|
systemId: 'bcycle_elpaso',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'indianapolis-pacers',
|
|
city: 'Indianapolis, IN',
|
|
centerLat: 39.7684,
|
|
centerLon: -86.1581,
|
|
systemId: 'bcycle_pacersbikeshare',
|
|
provider: 'Pacers Bikeshare',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'fort-lauderdale-broward',
|
|
city: 'Fort Lauderdale, FL',
|
|
centerLat: 26.1224,
|
|
centerLon: -80.1373,
|
|
systemId: 'bcycle_broward',
|
|
provider: 'Broward B-cycle',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'memphis-bcycle',
|
|
city: 'Memphis, TN',
|
|
centerLat: 35.1495,
|
|
centerLon: -90.049,
|
|
systemId: 'bcycle_memphis',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'des-moines-bcycle',
|
|
city: 'Des Moines, IA',
|
|
centerLat: 41.5868,
|
|
centerLon: -93.625,
|
|
systemId: 'bcycle_desmoines',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'tucson-tugo',
|
|
city: 'Tucson, AZ',
|
|
centerLat: 32.2226,
|
|
centerLon: -110.9747,
|
|
systemId: 'bcycle_tugo',
|
|
provider: 'Tugo',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'fort-worth-trinity',
|
|
city: 'Fort Worth, TX',
|
|
centerLat: 32.7555,
|
|
centerLon: -97.3308,
|
|
systemId: 'bcycle_fortworth',
|
|
provider: 'Trinity Metro',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'omaha-heartland',
|
|
city: 'Omaha, NE',
|
|
centerLat: 41.2565,
|
|
centerLon: -95.9345,
|
|
systemId: 'bcycle_heartland',
|
|
provider: 'Heartland B-cycle',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'lincoln-bikelnk',
|
|
city: 'Lincoln, NE',
|
|
centerLat: 40.8136,
|
|
centerLon: -96.7026,
|
|
systemId: 'bcycle_bikelnk',
|
|
provider: 'BikeLNK',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'greenville-sc-bcycle',
|
|
city: 'Greenville, SC',
|
|
centerLat: 34.8526,
|
|
centerLon: -82.394,
|
|
systemId: 'bcycle_greenville',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'buffalo-reddy',
|
|
city: 'Buffalo, NY',
|
|
centerLat: 42.8864,
|
|
centerLon: -78.8784,
|
|
systemId: 'bcycle_reddy',
|
|
provider: 'Reddy Bikeshare',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'las-vegas-rtc-bike-share',
|
|
city: 'Las Vegas, NV',
|
|
centerLat: 36.1699,
|
|
centerLon: -115.1398,
|
|
systemId: 'bcycle_rtcbikeshare',
|
|
provider: 'RTC Bike Share',
|
|
}),
|
|
bcycleEntry({
|
|
id: 'santa-barbara-bcycle',
|
|
city: 'Santa Barbara, CA',
|
|
centerLat: 34.4208,
|
|
centerLon: -119.6982,
|
|
systemId: 'bcycle_santabarbara',
|
|
}),
|
|
];
|
|
function normalizeRegistryEntry(entry) {
|
|
const id = String(entry?.id || '')
|
|
.trim()
|
|
.toLowerCase();
|
|
const city = String(entry?.city || '').trim();
|
|
const centerLat = Number(entry?.centerLat);
|
|
const centerLon = Number(entry?.centerLon);
|
|
const loadRadiusKm = Number(entry?.loadRadiusKm);
|
|
const stationInformationUrl = new URL(
|
|
String(entry?.stationInformationUrl || '').trim(),
|
|
);
|
|
const stationStatusUrl = new URL(
|
|
String(entry?.stationStatusUrl || '').trim(),
|
|
);
|
|
|
|
if (!id) throw new Error('GBFS entry id is required');
|
|
if (!city) throw new Error(`GBFS entry "${id}" city is required`);
|
|
if (!Number.isFinite(centerLat) || !Number.isFinite(centerLon)) {
|
|
throw new Error(`GBFS entry "${id}" has invalid center coordinates`);
|
|
}
|
|
// Enforce HTTPS-only for GBFS feeds
|
|
if (
|
|
stationInformationUrl.protocol !== 'https:' ||
|
|
stationStatusUrl.protocol !== 'https:'
|
|
) {
|
|
throw new Error(`GBFS entry "${id}" must use https URLs`);
|
|
}
|
|
// Validate that URLs end with expected GBFS endpoint filenames
|
|
if (!/\/station_information\.json$/i.test(stationInformationUrl.pathname)) {
|
|
throw new Error(`GBFS entry "${id}" station_information URL is invalid`);
|
|
}
|
|
if (!/\/station_status\.json$/i.test(stationStatusUrl.pathname)) {
|
|
throw new Error(`GBFS entry "${id}" station_status URL is invalid`);
|
|
}
|
|
|
|
// Deduplicate hostnames across both feed URLs for proxy allowlisting
|
|
const hosts = Array.from(
|
|
new Set([
|
|
stationInformationUrl.hostname.toLowerCase(),
|
|
stationStatusUrl.hostname.toLowerCase(),
|
|
]),
|
|
);
|
|
|
|
return {
|
|
id,
|
|
city,
|
|
centerLat,
|
|
centerLon,
|
|
loadRadiusKm:
|
|
Number.isFinite(loadRadiusKm) && loadRadiusKm > 0
|
|
? loadRadiusKm
|
|
: CITY_RANGE_BASE_KM,
|
|
stationInformationUrl: stationInformationUrl.toString(),
|
|
stationStatusUrl: stationStatusUrl.toString(),
|
|
provider: String(entry?.provider || 'GBFS').trim() || 'GBFS',
|
|
hosts,
|
|
};
|
|
}
|
|
const GBFS_CITY_REGISTRY = (() => {
|
|
const seen = new Set();
|
|
return RAW_GBFS_CITY_REGISTRY.map((entry) => {
|
|
const normalized = normalizeRegistryEntry(entry);
|
|
if (seen.has(normalized.id)) {
|
|
throw new Error(`Duplicate GBFS city id: ${normalized.id}`);
|
|
}
|
|
seen.add(normalized.id);
|
|
return normalized;
|
|
});
|
|
})();
|
|
const CITY_BY_ID = new Map(
|
|
GBFS_CITY_REGISTRY.map((entry) => [entry.id, entry]),
|
|
);
|
|
export { GBFS_CITY_REGISTRY, CITY_BY_ID };
|