5.0.5
Fix historic highs
This commit is contained in:
+4
-4
@@ -1579,19 +1579,19 @@ export function UTCIForecast() {
|
||||
${(() => {
|
||||
const heatEvents = eventGlanceItems.filter(e => /heat caution|heat warning|extreme heat/i.test(e.label)).map(e => ({ ...e, alert: true }));
|
||||
const otherEvents = eventGlanceItems.filter(e => !/heat caution|heat warning|extreme heat/i.test(e.label));
|
||||
const renderRow = ({ icon, label, value, alert, grp }, keyPrefix = '') => html`
|
||||
const renderRow = ({ icon, label, value, sub, alert, grp }, keyPrefix = '') => html`
|
||||
<div class=${`insight-row${alert ? ' insight-row--alert' : ''}${grp ? ` insight-row--${grp}` : ''}`} key=${`${keyPrefix}${label}`}>
|
||||
<span class="insight-icon">${icon}</span>
|
||||
<span class="insight-label">${titleCaseText(label)}</span>
|
||||
<span class="insight-value">${titleCaseText(value)}</span>
|
||||
<span class="insight-value">${titleCaseText(value)}${sub ? html`<span class="insight-sub">${titleCaseText(sub)}</span>` : ''}</span>
|
||||
</div>`;
|
||||
// Only the outdoors profile reorders items around the heat block
|
||||
// (it has a 'Peak felt temp' anchor). Other profiles render in order.
|
||||
const hasFeltAnchor = glanceSummary.some(i => i.label === 'Peak felt temp');
|
||||
const comfortItem = hasFeltAnchor ? glanceSummary.find(i => i.label === 'Comfortable window') : null;
|
||||
const drivingItem = hasFeltAnchor ? glanceSummary.find(i => i.label === 'Good driving time') : null;
|
||||
const climateRows = glanceSummary.filter(i => i.label === '1991-2020 High' || i.label === '1991-2020 Average');
|
||||
const restSummary = glanceSummary.filter(i => i.label !== '1991-2020 High' && i.label !== '1991-2020 Average' && !(hasFeltAnchor && (i.label === 'Comfortable window' || i.label === 'Good driving time')));
|
||||
const climateRows = glanceSummary.filter(i => i.label === '1991-2020 Average');
|
||||
const restSummary = glanceSummary.filter(i => i.label !== '1991-2020 Average' && !(hasFeltAnchor && (i.label === 'Comfortable window' || i.label === 'Good driving time')));
|
||||
return [
|
||||
...restSummary.flatMap(item => [
|
||||
renderRow(item),
|
||||
|
||||
+33
-17
@@ -548,10 +548,12 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
|
||||
};
|
||||
|
||||
// ── Vs seasonal norms (climate anomalies) ──────────────────────────────
|
||||
// Shown across every profile: two rows comparing this day against the
|
||||
// 1991-2020 norm for the date — the day's HIGH vs the normal high, and the
|
||||
// day's 24h AVERAGE vs the normal mean. High tracks the daytime peak people
|
||||
// notice; the average is the standard climate anomaly. An honest climate cue.
|
||||
// One row comparing this day against the 1991-2020 norm for the date. The
|
||||
// headline is the standard climate anomaly — the day's 24h AVERAGE vs the
|
||||
// normal mean — with the day's HIGH vs the normal high carried alongside as
|
||||
// the peak people actually notice. Kept in a single row because two rows
|
||||
// sharing the icon and "1991-2020 … warmer" phrasing read as duplicates
|
||||
// giving contradictory answers. An honest climate cue.
|
||||
const climateItem = (() => {
|
||||
if (!normals) return [];
|
||||
const key = dayKey || todayRows[0]?.iso?.slice(0, 10);
|
||||
@@ -562,22 +564,36 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
|
||||
if (taVals.length === 0) return [];
|
||||
const dayHigh = Math.max(...taVals);
|
||||
const dayMean = taVals.reduce((a, b) => a + b, 0) / taVals.length;
|
||||
const out = [];
|
||||
// Either day-of-year table can have null slots — reduceNormals keeps the
|
||||
// { high, mean } shape as long as one series survived — so treat each as
|
||||
// independently optional.
|
||||
const nHigh = normals.high?.[doy];
|
||||
if (nHigh != null) {
|
||||
const a = dayHigh - nHigh;
|
||||
if (Math.abs(a) >= 3) {
|
||||
out.push({ icon: '🌡', label: '1991-2020 High', value: `${a >= 0 ? '+' : '−'}${Math.abs(a).toFixed(1)}° ${a >= 0 ? 'warmer' : 'cooler'}`, alert: a >= 5, grp: 'ambient' });
|
||||
}
|
||||
}
|
||||
const nMean = normals.mean?.[doy];
|
||||
if (nMean != null) {
|
||||
const a = dayMean - nMean;
|
||||
if (Math.abs(a) >= 3) {
|
||||
out.push({ icon: '🌡', label: '1991-2020 Average', value: `${a >= 0 ? '+' : '−'}${Math.abs(a).toFixed(1)}° ${a >= 0 ? 'warmer' : 'cooler'}`, alert: a >= 5, grp: 'ambient' });
|
||||
}
|
||||
const aHigh = nHigh != null ? dayHigh - nHigh : null;
|
||||
const aMean = nMean != null ? dayMean - nMean : null;
|
||||
// Show the row when EITHER anomaly is notable. The mean anomaly is usually
|
||||
// the smaller of the two (a clear night pulls it back towards normal), so
|
||||
// gating on it alone would hide days with a dramatic peak.
|
||||
const notable = v => v != null && Math.abs(v) >= 3;
|
||||
if (!notable(aHigh) && !notable(aMean)) return [];
|
||||
const fmt = a => `${a >= 0 ? '+' : '−'}${Math.abs(a).toFixed(1)}°`;
|
||||
const word = a => (a >= 0 ? 'warmer' : 'cooler');
|
||||
// Each half keeps its own sign and word, so a day whose peak and mean fall
|
||||
// on opposite sides of the norm still reads correctly.
|
||||
// `sub` is the secondary figure — rendered inline on mobile, on its own
|
||||
// line in the narrow desktop rail (see .insight-sub in ui.css).
|
||||
let value, sub = null;
|
||||
if (aMean != null && aHigh != null) {
|
||||
value = `${fmt(aMean)} ${word(aMean)}`;
|
||||
sub = `(${fmt(aHigh)} peak)`;
|
||||
} else if (aMean != null) {
|
||||
value = `${fmt(aMean)} ${word(aMean)}`;
|
||||
} else {
|
||||
value = `${fmt(aHigh)} peak ${word(aHigh)}`;
|
||||
}
|
||||
return out;
|
||||
// Flagged off the high, matching the "5 degrees against the normal high" rule.
|
||||
const alert = (aHigh != null ? aHigh : aMean) >= 5;
|
||||
return [{ icon: '🌡', label: '1991-2020 Average', value, sub, alert, grp: 'ambient' }];
|
||||
})();
|
||||
|
||||
// ── Good Driving Time ──────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user