mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
Upstream's #641 (bikeshare Toronto, city failures scoped to range with partial outages read DEGRADED/STALE) replaced the layer-level GBFS error strings this branch had msg()-marked, in the two paths that conflicted. - ingestion.js/viewport.js taken as upstream's per-city _failedCityIds tracking; the layer-level error assignments are gone. - 'GBFS fetch error' still reaches the panel via stats.error from controls.js, so its mark moved there (translateFeedError keeps translating it). - 'GBFS status update failed' no longer exists anywhere: pruned from es/ru and from the layerStatusText SITES table, which now pins the controls.js site instead of the removed ones. Also brings #581 (upstream-read caps, OpenSky 429 cooldown) - no i18n surface. npm run i18n: complete (2553), no unmarked strings. npm test: only the pre-existing environmental OPENAI_API_KEY leak in devFreshDotenv fails. Build passes.
97 lines
3.4 KiB
JavaScript
97 lines
3.4 KiB
JavaScript
import { msg, t } from '../../i18n/index.js';
|
|
import { STATUS_POLL_MS } from './policy.js';
|
|
|
|
export function createControls({ state: layerState, services, parts, source }) {
|
|
const methods = {
|
|
id: 'bikeshare',
|
|
|
|
name: 'Bikeshare',
|
|
|
|
icon: '🚲',
|
|
|
|
source: 'GBFS',
|
|
|
|
updateInterval: STATUS_POLL_MS,
|
|
|
|
/**
|
|
* Return a sampled array of detectable station objects for HUD overlay rendering.
|
|
* @param {Object} [options] - Sampling options (maxCount, seed).
|
|
* @returns {Array<{ position: Cesium.Cartesian3, id: string, type: string, skipLabel: boolean }>}
|
|
*/
|
|
getDetectableObjects(options = {}) {
|
|
return parts.queries.collectDetectableStations(options);
|
|
},
|
|
|
|
/**
|
|
* Snapshot loaded bikeshare stations as plain JSON-safe records for the analyst
|
|
* query engine. On demand only (once per spoken query); [] while the
|
|
* layer is off.
|
|
* @param {number} [maxCount=Infinity] Most records to return.
|
|
* @returns {Array<Object>} {id, name, city, lat, lon, bikesAvailable, docksAvailable, capacity, renting}.
|
|
*/
|
|
getAnalystRecords(maxCount = Infinity) {
|
|
if (!layerState._enabled) return [];
|
|
const num = (v) => (Number.isFinite(v) ? v : null);
|
|
const result = [];
|
|
for (const record of layerState._stationRenderMap.values()) {
|
|
if (result.length >= maxCount) break;
|
|
const info = layerState._stationInfoCache
|
|
.get(record.cityId)
|
|
?.get(record.stationId);
|
|
if (!Number.isFinite(info?.lat) || !Number.isFinite(info?.lon))
|
|
continue;
|
|
result.push({
|
|
id: record.stationName || `Dock ${record.stationId}`,
|
|
name: record.stationName || null,
|
|
city: record.cityId || null,
|
|
lat: info.lat,
|
|
lon: info.lon,
|
|
bikesAvailable: num(record.bikesAvailable),
|
|
docksAvailable: num(record.docksAvailable),
|
|
capacity: num(record.capacity),
|
|
renting: record.isRenting !== false,
|
|
});
|
|
}
|
|
return result;
|
|
},
|
|
|
|
/**
|
|
* Return current layer statistics for the UI status display.
|
|
* @returns {{ count: number, lastUpdate: number|null, loading: boolean, loadingLabel?: string, error?: string, degraded?: boolean, stale?: boolean }}
|
|
*/
|
|
getStats() {
|
|
const stats = {
|
|
count: layerState._count,
|
|
lastUpdate: layerState._lastUpdate,
|
|
loading: layerState._loading,
|
|
};
|
|
if (layerState._loading) {
|
|
stats.loadingLabel =
|
|
layerState._activeCityIds.size > 0
|
|
? t('syncing {count} city feeds...', {
|
|
count: layerState._activeCityIds.size,
|
|
})
|
|
: t('scanning nearby systems...');
|
|
}
|
|
// A failed city is only the layer's failure when it left nothing on the
|
|
// map. While other cities still render, the partial failure reads
|
|
// DEGRADED; when every in-range city failed and only previously drawn
|
|
// points remain, they read STALE.
|
|
if (layerState._failedCityIds.size > 0) {
|
|
if (layerState._stationRenderMap.size === 0) {
|
|
stats.error = msg('GBFS fetch error');
|
|
} else {
|
|
stats.degraded = true;
|
|
const allFailed = Array.from(layerState._activeCityIds).every(
|
|
(cityId) => layerState._failedCityIds.has(cityId),
|
|
);
|
|
if (allFailed) stats.stale = true;
|
|
}
|
|
}
|
|
return stats;
|
|
},
|
|
};
|
|
|
|
return { methods };
|
|
}
|