Uniform temperature shades
This commit is contained in:
Fraxle
2026-05-25 16:40:12 +01:00
parent 31e293027e
commit c8d529392e
+50 -23
View File
@@ -570,20 +570,47 @@ ${(isPro ? (activeProfile === 'custom' || activeProfile === 'showall' || activeC
const fmt = (v, dp = 1) => v == null ? '—' : (showDecimals ? v.toFixed(dp) : String(Math.round(v))); const fmt = (v, dp = 1) => v == null ? '—' : (showDecimals ? v.toFixed(dp) : String(Math.round(v)));
const u = (unit) => showUnits ? unit : ''; const u = (unit) => showUnits ? unit : '';
// Returns a background tint based on temperature value - all share the same // Returns a background tint based on temperature value - all share the same
// Thermal Stress Bands - solid colours matching the legend exactly. // Continuous temperature colour scale — used for all temp columns
// All band-coloured cells use the full bg/fg from UTCI_BANDS. // (air, dew, indoor, vehicle, concrete, soil, UTCI, SunSoak…).
const tempBg = (t) => { // Fully opaque, richly saturated stops so every degree is distinct.
const airTempBg = (t) => {
if (t == null) return 'transparent'; if (t == null) return 'transparent';
const band = utciCategory(t); // [temp °C, [r,g,b]]
return band ? bandGradient(band.bg, band.darkenAmt) : 'transparent'; const stops = [
[-10, [ 90, 155, 220]],
[ 0, [140, 195, 235]],
[ 10, [155, 215, 195]],
[ 16, [140, 210, 140]],
[ 20, [195, 225, 110]],
[ 24, [240, 225, 80]],
[ 28, [250, 175, 65]],
[ 32, [240, 120, 55]],
[ 36, [220, 70, 50]],
[ 40, [185, 30, 30]],
[ 50, [130, 0, 20]],
];
const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
let i = stops.findIndex(s => t < s[0]);
if (i === -1) i = stops.length;
const baseRgb = i === 0 ? stops[0][1]
: i >= stops.length ? stops[stops.length-1][1]
: (() => {
const [t0, c0] = stops[i-1];
const [t1, c1] = stops[i];
const x = clamp((t - t0) / (t1 - t0), 0, 1);
const lerp = (a, b) => Math.round(a + (b - a) * x);
return [lerp(c0[0],c1[0]), lerp(c0[1],c1[1]), lerp(c0[2],c1[2])];
})();
// Blend base colour 25% toward white before applying the sweep.
const [r,g,b] = baseRgb.map(c => Math.min(255, Math.round(c + (255 - c) * 0.66)));
const lighten = (c) => Math.min(255, Math.round(c + (255 - c) * 0.30));
const darken = (c) => Math.round(c * 0.93);
const light = `rgb(${lighten(r)},${lighten(g)},${lighten(b)})`;
const mid = `rgb(${r},${g},${b})`;
const dark = `rgb(${darken(r)},${darken(g)},${darken(b)})`;
return `linear-gradient(135deg, ${light} 0%, ${mid} 60%, ${dark} 100%)`;
}; };
const tempBgStrong = tempBg; const airTempFontColor = () => '#1a1a1a';
const tempFontColor = (t) => {
if (t == null) return '#1a1200';
const band = utciCategory(t);
return band ? band.fg : '#1a1200';
};
const tempFontColorStrong = tempFontColor;
// Returns any extra band styles (fontWeight, textShadow) for a given temp. // Returns any extra band styles (fontWeight, textShadow) for a given temp.
const bandExtras = (t) => { const bandExtras = (t) => {
const band = utciCategory(t); const band = utciCategory(t);
@@ -638,19 +665,19 @@ ${(isPro ? (activeProfile === 'custom' || activeProfile === 'showall' || activeC
</span> </span>
</td> </td>
${visibleCols.utciP && html` ${visibleCols.utciP && html`
<td class=${groupStart('utciP')} style=${{ background: bandGradient(adjCat.bg, adjCat.darkenAmt), color: adjCat.fg, fontSize: '13px', ...bandExtras(r.utciAdj) }}> <td class=${groupStart('utciP')} style=${{ background: airTempBg(r.utciAdj), color: airTempFontColor(r.utciAdj), fontSize: '13px' }}>
${fmt(r.utciAdj)}${u('°C')} ${fmt(r.utciAdj)}${u('°C')}
</td>`} </td>`}
${visibleCols.vehicleT && html` ${visibleCols.vehicleT && html`
<td class=${groupStart('vehicleT')} style=${{ color: tempFontColorStrong(r.vehicleT), background: tempBgStrong(r.vehicleT), ...bandExtras(r.vehicleT) }}> <td class=${groupStart('vehicleT')} style=${{ color: airTempFontColor(r.vehicleT), background: airTempBg(r.vehicleT) }}>
${fmt(r.vehicleT)}${u('°C')} ${fmt(r.vehicleT)}${u('°C')}
</td>`} </td>`}
${indoorMode === 'on' && !indoorManaged && html` ${indoorMode === 'on' && !indoorManaged && html`
<td class=${groupStart('indoorT')} style=${{ color: tempFontColorStrong(r.indoorT), background: tempBgStrong(r.indoorT), ...bandExtras(r.indoorT) }}> <td class=${groupStart('indoorT')} style=${{ color: airTempFontColor(r.indoorT), background: airTempBg(r.indoorT) }}>
${fmt(r.indoorT)}${u('°C')} ${fmt(r.indoorT)}${u('°C')}
</td>`} </td>`}
${indoorMode === 'on' && indoorManaged && html` ${indoorMode === 'on' && indoorManaged && html`
<td class=${groupStart('managedT')} style=${{ color: tempFontColorStrong(r.managedT), background: tempBgStrong(r.managedT), ...bandExtras(r.managedT) }}> <td class=${groupStart('managedT')} style=${{ color: airTempFontColor(r.managedT), background: airTempBg(r.managedT) }}>
${fmt(r.managedT)}${u('°C')} ${fmt(r.managedT)}${u('°C')}
</td>`} </td>`}
${visibleCols.burn && html` ${visibleCols.burn && html`
@@ -658,7 +685,7 @@ ${(isPro ? (activeProfile === 'custom' || activeProfile === 'showall' || activeC
${burnLabel(burnMins)} ${burnLabel(burnMins)}
</td>`} </td>`}
${visibleCols.utci && html` ${visibleCols.utci && html`
<td class=${groupStart('utci')} style=${{ background: bandGradient(cat.bg, cat.darkenAmt), color: cat.fg, ...bandExtras(r.utci) }}> <td class=${groupStart('utci')} style=${{ background: airTempBg(r.utci), color: airTempFontColor(r.utci) }}>
${fmt(r.utci)}${u('°C')} ${fmt(r.utci)}${u('°C')}
</td>`} </td>`}
${visibleCols.delta && html` ${visibleCols.delta && html`
@@ -668,20 +695,20 @@ ${(isPro ? (activeProfile === 'custom' || activeProfile === 'showall' || activeC
}}> }}>
${delta > 0 ? '+' : ''}${fmt(delta)}${u('°C')} ${delta > 0 ? '+' : ''}${fmt(delta)}${u('°C')}
</td>`} </td>`}
${visibleCols.tmrt && html`<td class=${groupStart('tmrt')} style=${{ background: tempBgStrong(r.Tmrt), color: tempFontColorStrong(r.Tmrt), ...bandExtras(r.Tmrt) }}>${fmt(r.Tmrt)}${u('°C')}</td>`} ${visibleCols.tmrt && html`<td class=${groupStart('tmrt')} style=${{ background: airTempBg(r.Tmrt), color: airTempFontColor(r.Tmrt) }}>${fmt(r.Tmrt)}${u('°C')}</td>`}
${visibleCols.concreteT && html` ${visibleCols.concreteT && html`
<td class=${groupStart('concreteT')} style=${{ color: tempFontColorStrong(r.concreteT), background: tempBgStrong(r.concreteT), ...bandExtras(r.concreteT) }}> <td class=${groupStart('concreteT')} style=${{ color: airTempFontColor(r.concreteT), background: airTempBg(r.concreteT) }}>
${fmt(r.concreteT)}${u('°C')} ${fmt(r.concreteT)}${u('°C')}
</td>`} </td>`}
${visibleCols.soilT && html` ${visibleCols.soilT && html`
<td class=${groupStart('soilT')} style=${{ color: tempFontColorStrong(r.soilT0), background: tempBgStrong(r.soilT0), ...bandExtras(r.soilT0) }}>${fmt(r.soilT0)}${u('°C')}</td>`} <td class=${groupStart('soilT')} style=${{ color: airTempFontColor(r.soilT0), background: airTempBg(r.soilT0) }}>${fmt(r.soilT0)}${u('°C')}</td>`}
${visibleCols.soilT6 && html` ${visibleCols.soilT6 && html`
<td class=${groupStart('soilT6')} style=${{ color: tempFontColorStrong(r.soilT6), background: tempBgStrong(r.soilT6), ...bandExtras(r.soilT6) }}>${fmt(r.soilT6)}${u('°C')}</td>`} <td class=${groupStart('soilT6')} style=${{ color: airTempFontColor(r.soilT6), background: airTempBg(r.soilT6) }}>${fmt(r.soilT6)}${u('°C')}</td>`}
${visibleCols.soilM && html` ${visibleCols.soilM && html`
<td class=${groupStart('soilM')} style=${{ color: '#2a6a90', background: scaleBg(r.soilM, 0.12, 0.45, '50,125,190') }}>${r.soilM != null ? fmt(r.soilM * 100, 1) + u('%') : '—'}</td>`} <td class=${groupStart('soilM')} style=${{ color: '#2a6a90', background: scaleBg(r.soilM, 0.12, 0.45, '50,125,190') }}>${r.soilM != null ? fmt(r.soilM * 100, 1) + u('%') : '—'}</td>`}
${visibleCols.air && html`<td class=${groupStart('air')} style=${{ background: tempBg(r.Ta), color: tempFontColorStrong(r.Ta), ...bandExtras(r.Ta) }}>${fmt(r.Ta)}${u('°C')}</td>`} ${visibleCols.air && html`<td class=${groupStart('air')} style=${{ background: airTempBg(r.Ta), color: airTempFontColor(r.Ta) }}>${fmt(r.Ta)}${u('°C')}</td>`}
${visibleCols.rh && html`<td class=${groupStart('rh')} style=${{ background: scaleBg(r.RH, 30, 100, '70,145,200') }}>${Math.round(r.RH)}${u('%')}</td>`} ${visibleCols.rh && html`<td class=${groupStart('rh')} style=${{ background: scaleBg(r.RH, 30, 100, '70,145,200') }}>${Math.round(r.RH)}${u('%')}</td>`}
${visibleCols.dew && html`<td class=${groupStart('dew')} style=${{ background: tempBgStrong(r.dew), color: tempFontColorStrong(r.dew), ...bandExtras(r.dew) }}>${fmt(r.dew)}${u('°C')}</td>`} ${visibleCols.dew && html`<td class=${groupStart('dew')} style=${{ background: airTempBg(r.dew), color: airTempFontColor(r.dew) }}>${fmt(r.dew)}${u('°C')}</td>`}
${visibleCols.precip && html` ${visibleCols.precip && html`
<td class=${groupStart('precip')} style=${{ background: r.snow > 0 ? scaleBg(r.snow, 0, 4, '90,140,210') : scaleBg(r.precip, 0, 8, '70,145,200') }}> <td class=${groupStart('precip')} style=${{ background: r.snow > 0 ? scaleBg(r.snow, 0, 4, '90,140,210') : scaleBg(r.precip, 0, 8, '70,145,200') }}>
<span style=${{ display: 'inline-flex', alignItems: 'center', gap: '5px', verticalAlign: 'middle' }}> <span style=${{ display: 'inline-flex', alignItems: 'center', gap: '5px', verticalAlign: 'middle' }}>