Fix the Why It Feels calcs
This commit is contained in:
fraxle
2026-06-13 15:20:45 +01:00
parent 0532ed58a2
commit d109316aa1
4 changed files with 40 additions and 15 deletions
+1
View File
@@ -242,6 +242,7 @@
gap: 4px;
width: 100%;
max-width: 270px; /* matches .scope-ring desktop size */
padding-bottom: 32px; /* compensates for the extra Air temp row in the Why It Feels panel */
}
/* Unified pill: icon on left, bar fills the right */
+10
View File
@@ -1607,6 +1607,16 @@
color: #3a78a8; /* cool blue - cooling contribution */
}
.delta-base {
color: #4a3420; /* neutral — absolute air temperature, not a delta */
}
.insight-row--base {
opacity: 0.75;
border-bottom: 1px dashed rgba(74,52,32,0.2);
margin-bottom: 2px;
}
/* Values for "Today at a glance" */
.insight-value {
font-family: JetBrains Mono, monospace;
+9 -7
View File
@@ -742,6 +742,7 @@ export function UTCIForecast() {
</div>
${(() => {
const _wflItems = [
{ label: 'Air temperature', icon: '🌡', value: currentRow.Ta, isBase: true },
{ label: 'Sun and sky', icon: '☀', value: whyFeelsLike.sunAndSky },
{ label: 'Wind', icon: '🌬', value: whyFeelsLike.wind },
{ label: 'Humidity', icon: '💧', value: whyFeelsLike.humidity },
@@ -752,19 +753,20 @@ export function UTCIForecast() {
{ label: 'Precipitation', icon: '🌧', value: whyFeelsLike.precipitation }
] : []),
];
const _wflMax = Math.max(..._wflItems.map(i => Math.abs(i.value)), 0.1);
return _wflItems.map(({ label, icon, value }) => html`
<div class="insight-row" key=${label}>
const _wflDeltaItems = _wflItems.filter(i => !i.isBase);
const _wflMax = Math.max(..._wflDeltaItems.map(i => Math.abs(i.value)), 0.1);
return _wflItems.map(({ label, icon, value, isBase }) => html`
<div class=${'insight-row' + (isBase ? ' insight-row--base' : '')} key=${label}>
<span class="insight-icon">${icon}</span>
<span class="insight-label">
${label}
<span class="insight-bar-track">
${!isBase && html`<span class="insight-bar-track">
<span class=${'insight-bar ' + (value >= 0 ? 'bar-pos' : 'bar-neg')}
style=${{ width: `${Math.round(Math.abs(value) / _wflMax * 100)}%` }}></span>
</span>`}
</span>
</span>
<span class=${`insight-delta ${value >= 0 ? 'delta-pos' : 'delta-neg'}`}>
${value >= 0 ? '+' : ''}${value.toFixed(1)}°
<span class=${`insight-delta ${isBase ? 'delta-base' : value >= 0 ? 'delta-pos' : 'delta-neg'}`}>
${isBase ? '' : value >= 0 ? '+' : ''}${value.toFixed(1)}°
</span>
</div>
`);
+20 -8
View File
@@ -330,15 +330,27 @@ export function computeWhyFeelsLike(row, env) {
const { Ta, Tmrt, va, eh, precip, snow } = row;
const r1 = (v) => Math.round(v * 10) / 10;
// Radiation - mean radiant temp above or below air temp.
const sunAndSky = r1(Tmrt - Ta);
// Environment-adjusted air temp (same as used in buildHourlyRows).
const TaEnv = Ta + (env ? env.taOffset : 0);
// Wind - UTCI with actual wind vs calm, holding Tmrt = Ta and RH neutral.
const ehNeutral = vaporPressureHpa(Ta, 50);
const wind = r1(utciApprox(Ta, Ta, va, ehNeutral) - utciApprox(Ta, Ta, 0, ehNeutral));
// Sequential UTCI isolation — all deltas are in the same UTCI-polynomial
// space so they add up: Ta + environment + sunAndSky + wind + humidity
// + precipitation ≈ SunSoak (utciAdj), within rounding.
//
// Tmrt already has env radiation scaling (dirFactor/difFactor/globFactor)
// baked in from buildHourlyRows, so each delta automatically reflects
// the active Solar Model without any extra work here.
const ehNeutral = vaporPressureHpa(TaEnv, 50);
const base = utciApprox(TaEnv, TaEnv, 0, ehNeutral); // ≈ TaEnv
// Humidity - actual vapour pressure vs neutral RH 50%, no wind or sun.
const humidity = r1(utciApprox(Ta, Ta, 0, eh) - utciApprox(Ta, Ta, 0, ehNeutral));
// Radiation: effect of actual Tmrt vs no-radiation baseline (wind=0, RH=50%).
const sunAndSky = r1(utciApprox(TaEnv, Tmrt, 0, ehNeutral) - base);
// Wind: cooling effect of actual wind with solar present (RH still neutral).
const wind = r1(utciApprox(TaEnv, Tmrt, va, ehNeutral) - utciApprox(TaEnv, Tmrt, 0, ehNeutral));
// Humidity: actual vapour pressure vs neutral RH 50%, with solar and wind.
const humidity = r1(utciApprox(TaEnv, Tmrt, va, eh) - utciApprox(TaEnv, Tmrt, va, ehNeutral));
// Environment modifier - explicit temperature offset from the env config.
const environment = env ? r1(env.taOffset) : 0;
@@ -346,7 +358,7 @@ export function computeWhyFeelsLike(row, env) {
// Precipitation soak penalty - negative or zero.
const precipitation = precipPenalty(precip, snow, va);
return { sunAndSky, wind, humidity, environment, precipitation };
return { base: r1(base), sunAndSky, wind, humidity, environment, precipitation };
}
// ------------------------------------------------------------------------