Added the in-memory TTL caching for RainViewer and OpenMeteo

This commit is contained in:
mrkmntal 2026-04-07 18:32:07 -04:00
commit bb87c836b6
2 changed files with 52 additions and 8 deletions

View file

@ -11,9 +11,37 @@ import {
clearMarkers,
} from './utils/leaflet-weather-map.mjs';
class Radar extends WeatherDisplay {
static metadataUrl = 'https://api.rainviewer.com/public/weather-maps.json';
const RADAR_METADATA_URL = 'https://api.rainviewer.com/public/weather-maps.json';
const RADAR_METADATA_CACHE_TTL_MS = 2 * 60 * 1000;
let radarMetadataCache = null;
const getRadarMetadataCached = async (stillWaiting) => {
const now = Date.now();
if (radarMetadataCache && (now - radarMetadataCache.fetchedAt) < RADAR_METADATA_CACHE_TTL_MS) {
return radarMetadataCache.data;
}
const radarMetadata = await safeJson(RADAR_METADATA_URL, {
retryCount: 2,
stillWaiting,
});
if (radarMetadata?.host && radarMetadata?.radar?.past?.length) {
radarMetadataCache = {
data: radarMetadata,
fetchedAt: now,
};
return radarMetadata;
}
if (radarMetadataCache) {
return radarMetadataCache.data;
}
return null;
};
class Radar extends WeatherDisplay {
constructor(navId, elemId) {
super(navId, elemId, 'Local Radar');
@ -48,10 +76,7 @@ class Radar extends WeatherDisplay {
this.updateLocationMarker();
await this.updateNearbyMarkers();
const radarMetadata = await safeJson(Radar.metadataUrl, {
retryCount: 2,
stillWaiting: () => this.stillWaiting(),
});
const radarMetadata = await getRadarMetadataCached(() => this.stillWaiting());
const frames = radarMetadata?.radar?.past?.slice(-this.maxFrames) ?? [];
if (!frames.length || !radarMetadata?.host) {

View file

@ -15,6 +15,9 @@ const OPEN_METEO_RADAR_OBSERVATION_PARAMETERS = [
'models=best_match',
].join('&');
const OPEN_METEO_OBSERVATION_CACHE_TTL_MS = 10 * 60 * 1000;
const openMeteoObservationCache = new Map();
const getPoint = async (lat, lon) => {
const point = await safeJson(`https://api.weather.gov/points/${lat.toFixed(4)},${lon.toFixed(4)}`);
if (!point) {
@ -56,15 +59,24 @@ const getAggregatedOpenMeteoForecast = async (lat, lon) => {
};
const getOpenMeteoObservationSnapshot = async (lat, lon) => {
const cacheKey = `${lat.toFixed(4)},${lon.toFixed(4)}`;
const cachedEntry = openMeteoObservationCache.get(cacheKey);
const now = Date.now();
if (cachedEntry && (now - cachedEntry.fetchedAt) < OPEN_METEO_OBSERVATION_CACHE_TTL_MS) {
return cachedEntry.data;
}
const forecast = await safeJson(`https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&${OPEN_METEO_RADAR_OBSERVATION_PARAMETERS}`);
if (!forecast?.hourly?.time?.length) {
if (debugFlag('verbose-failures')) {
console.warn(`Unable to get Open-Meteo radar observation snapshot for ${lat},${lon}`);
}
if (cachedEntry) {
return cachedEntry.data;
}
return false;
}
const now = Date.now();
let nearestIndex = 0;
let nearestDelta = Number.POSITIVE_INFINITY;
@ -76,13 +88,20 @@ const getOpenMeteoObservationSnapshot = async (lat, lon) => {
}
});
return {
const snapshot = {
time: forecast.hourly.time[nearestIndex],
temperature: forecast.hourly.temperature_2m?.[nearestIndex] ?? null,
weatherCode: forecast.hourly.weather_code?.[nearestIndex] ?? 0,
isDay: Boolean(forecast.hourly.is_day?.[nearestIndex] ?? 1),
timezone: forecast.timezone,
};
openMeteoObservationCache.set(cacheKey, {
data: snapshot,
fetchedAt: now,
});
return snapshot;
};
const weatherConditions = [