2.5
Data table compact hours 1h, 2h, 3h or 4h. Calc max/min or average
This commit is contained in:
@@ -231,6 +231,80 @@ export function buildHourlyRows({ forecast, airQuality, location, vehicleType, v
|
||||
return { hourlyRows, days, utcOffsetMs };
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// aggregateRows(rows, interval) - Compress a day's hourly rows into
|
||||
// multi-hour buckets for the table view (1h / 2h / 3h / 4h).
|
||||
//
|
||||
// Buckets are CLOCK-ALIGNED from midnight, so a 2h view groups 00-01,
|
||||
// 02-03, ...; 3h groups 00-02, 03-05, ...; etc. Each output row keeps the
|
||||
// LANDING (first) hour's positional fields - iso, dt, solar elevation,
|
||||
// global radiation, wind direction, sky/cloud category - so the little
|
||||
// scope, clock label and wind vane all show the time the column lands on.
|
||||
//
|
||||
// Every other column is aggregated with the rule that fits its meaning:
|
||||
// - SUM : precipitation totals (1mm/h x 3h = 3mm)
|
||||
// - MAX : risk/peak columns (rain %, UV, gusts, cabin/surface/indoor)
|
||||
// - MEAN : smooth continuous quantities (air temp, RH, wind, cloud, ...)
|
||||
// - FELT : worst-case felt temp - the warmest hour if it reaches >=20C
|
||||
// (heat is the concern), otherwise the coldest (cold concern)
|
||||
//
|
||||
// Derived render values (Delta, Burn) are recomputed downstream from the
|
||||
// aggregated Air / UTCI / UV, so they stay consistent automatically.
|
||||
//
|
||||
// interval <= 1 returns the input untouched (zero behaviour change).
|
||||
// ------------------------------------------------------------------------
|
||||
const AGG_MEAN = [
|
||||
'Ta', 'RH', 'dew', 'va', 'dir', 'dif', 'cc', 'ccLow', 'ccMid', 'ccHigh',
|
||||
'soilT0', 'soilT6', 'soilM', 'Tmrt', 'eh', 'visKm', 'aqi', 'effectiveRad',
|
||||
'grassPollen', 'birchPollen', 'alderPollen', 'mugwortPollen', 'olivePollen', 'ragweedPollen',
|
||||
];
|
||||
const AGG_MAX = [
|
||||
'precipProb', 'uv', 'uvA', 'uvB', 'gust',
|
||||
'vehicleT', 'concreteT', 'indoorT', 'managedT',
|
||||
];
|
||||
const AGG_SUM = ['precip', 'snow'];
|
||||
const AGG_FELT = ['utci', 'utciAdj'];
|
||||
|
||||
export function aggregateRows(rows, interval) {
|
||||
if (!rows || rows.length === 0 || !interval || interval <= 1) return rows;
|
||||
|
||||
// Numbers for a field across the group, skipping null / NaN.
|
||||
const nums = (group, f) => group.map(r => r[f]).filter(v => v != null && !isNaN(v));
|
||||
const mean = (vals) => vals.length ? vals.reduce((a, b) => a + b, 0) / vals.length : null;
|
||||
const maxV = (vals) => vals.length ? Math.max(...vals) : null;
|
||||
const felt = (vals) => {
|
||||
if (!vals.length) return null;
|
||||
const hi = Math.max(...vals);
|
||||
return hi >= 20 ? hi : Math.min(...vals);
|
||||
};
|
||||
|
||||
// Split rows into clock-aligned buckets by floor(localHour / interval).
|
||||
const buckets = [];
|
||||
let cur = null, curKey = null;
|
||||
for (const r of rows) {
|
||||
const hour = parseInt(r.iso.slice(11, 13), 10);
|
||||
const key = Math.floor(hour / interval);
|
||||
if (key !== curKey) { cur = []; buckets.push(cur); curKey = key; }
|
||||
cur.push(r);
|
||||
}
|
||||
|
||||
return buckets.map(group => {
|
||||
// Start from the landing row so positional fields (iso, dt, elev, glob,
|
||||
// wd, compass, cloudCat) carry through unchanged, then overwrite the
|
||||
// aggregatable columns.
|
||||
const out = { ...group[0] };
|
||||
AGG_MEAN.forEach(f => { out[f] = mean(nums(group, f)); });
|
||||
AGG_MAX.forEach(f => { out[f] = maxV(nums(group, f)); });
|
||||
AGG_SUM.forEach(f => { out[f] = group.reduce((a, r) => a + (r[f] || 0), 0); });
|
||||
AGG_FELT.forEach(f => { out[f] = felt(nums(group, f)); });
|
||||
// Bucket span markers used by the table for "now" highlighting and for
|
||||
// matching event cell-tags that fall on any hour within the bucket.
|
||||
out.isoHours = group.map(r => r.iso.slice(0, 13));
|
||||
out.isoEnd = group[group.length - 1].iso;
|
||||
return out;
|
||||
});
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// computeWhyFeelsLike(row, env) - Break down the felt-temp delta into its
|
||||
// contributing factors for the "Why it feels like this" panel.
|
||||
|
||||
Reference in New Issue
Block a user