79 lines
2.6 KiB
TypeScript
79 lines
2.6 KiB
TypeScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import axios from 'axios';
|
|
import { lakesConfig } from './lakesConfig';
|
|
|
|
const DATA_DIR = path.resolve(process.cwd(), 'public/data');
|
|
|
|
async function backfill() {
|
|
console.log('Starting weather backfill for past 7 days...');
|
|
|
|
for (const lake of lakesConfig) {
|
|
const internalId = lake.id.split('|')[0];
|
|
const filePath = path.join(DATA_DIR, `${internalId}.json`);
|
|
|
|
if (!fs.existsSync(filePath)) {
|
|
console.log(`Skipping ${internalId}, no data file.`);
|
|
continue;
|
|
}
|
|
|
|
if (!lake.coords) {
|
|
console.log(`Skipping ${internalId}, no coordinates.`);
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
const lat = lake.coords[0];
|
|
const lon = lake.coords[1];
|
|
// Fetch maximum past days supported by the forecast API (92 days)
|
|
const url = `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&past_days=92&hourly=temperature_2m,precipitation&timezone=GMT`;
|
|
|
|
const res = await axios.get(url, { timeout: 15000 });
|
|
const hourly = res.data.hourly;
|
|
|
|
// Build lookup map for O(1) matching: '2026-06-02T04:00' -> { temp, precip }
|
|
const weatherMap = new Map();
|
|
for (let i = 0; i < hourly.time.length; i++) {
|
|
weatherMap.set(hourly.time[i], {
|
|
temperature: hourly.temperature_2m[i],
|
|
precipitation: hourly.precipitation[i]
|
|
});
|
|
}
|
|
|
|
const data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
let updatedCount = 0;
|
|
|
|
for (const record of data) {
|
|
// record.timestamp is like "2026-06-02T04:10:00.000Z"
|
|
// Open-Meteo time is like "2026-06-02T04:00"
|
|
// Convert to hourly key to match weatherMap
|
|
const hourKey = record.timestamp.substring(0, 13) + ':00';
|
|
|
|
if (weatherMap.has(hourKey)) {
|
|
const w = weatherMap.get(hourKey);
|
|
if (w.temperature !== null && w.temperature !== undefined) {
|
|
record.temperature = w.temperature;
|
|
updatedCount++;
|
|
}
|
|
if (w.precipitation !== null && w.precipitation !== undefined) {
|
|
record.precipitation = w.precipitation;
|
|
}
|
|
}
|
|
}
|
|
|
|
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
console.log(`[${internalId}] Backfilled ${updatedCount} records with historical Open-Meteo data.`);
|
|
|
|
// small delay to prevent rate limit
|
|
await new Promise(r => setTimeout(r, 200));
|
|
|
|
} catch (e: any) {
|
|
console.error(`Error processing ${internalId}:`, e.message);
|
|
}
|
|
}
|
|
|
|
console.log('Backfill complete!');
|
|
}
|
|
|
|
backfill();
|