1.9.1.4
Update scope with new thermal colours
This commit is contained in:
+51
-34
@@ -134,6 +134,38 @@ export function UTCIForecast() {
|
||||
return html`<tr class="grp-label-row">${cells}</tr>`;
|
||||
};
|
||||
|
||||
// Continuous temperature colour scale - hoisted so both the table columns
|
||||
// and the thermal stress legend can share the same function.
|
||||
const TEMP_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 airTempRgb = (t) => {
|
||||
if (t == null) return null;
|
||||
const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
||||
let i = TEMP_STOPS.findIndex(s => t < s[0]);
|
||||
if (i === -1) i = TEMP_STOPS.length;
|
||||
const raw = i === 0 ? TEMP_STOPS[0][1]
|
||||
: i >= TEMP_STOPS.length ? TEMP_STOPS[TEMP_STOPS.length-1][1]
|
||||
: (() => {
|
||||
const [t0, c0] = TEMP_STOPS[i-1];
|
||||
const [t1, c1] = TEMP_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])];
|
||||
})();
|
||||
return raw.map(c => Math.min(255, Math.round(c + (255 - c) * 0.66)));
|
||||
};
|
||||
|
||||
// ─── 4. JSX RETURN ───────────────────────────────────────────────────
|
||||
// Everything below is the actual page markup, written as one big HTM
|
||||
// template. Search tips:
|
||||
@@ -577,36 +609,7 @@ ${(isPro ? (activeProfile === 'custom' || activeProfile === 'showall' || activeC
|
||||
// airTempBg(t, tPrev, tNext) returns a top→bottom gradient that
|
||||
// flows from the previous row's colour through this row's colour to
|
||||
// the next row's colour, making the whole column one seamless heatmap.
|
||||
const TEMP_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 airTempRgb = (t) => {
|
||||
if (t == null) return null;
|
||||
const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
||||
let i = TEMP_STOPS.findIndex(s => t < s[0]);
|
||||
if (i === -1) i = TEMP_STOPS.length;
|
||||
const raw = i === 0 ? TEMP_STOPS[0][1]
|
||||
: i >= TEMP_STOPS.length ? TEMP_STOPS[TEMP_STOPS.length-1][1]
|
||||
: (() => {
|
||||
const [t0, c0] = TEMP_STOPS[i-1];
|
||||
const [t1, c1] = TEMP_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 66% toward white for the pastel look.
|
||||
return raw.map(c => Math.min(255, Math.round(c + (255 - c) * 0.66)));
|
||||
};
|
||||
// airTempRgb and TEMP_STOPS hoisted to component scope above.
|
||||
const toRgb = (rgb) => rgb ? `rgb(${rgb[0]},${rgb[1]},${rgb[2]})` : 'transparent';
|
||||
const airTempBg = (t, tPrev, tNext) => {
|
||||
if (t == null) return 'transparent';
|
||||
@@ -880,10 +883,24 @@ ${(isPro ? (activeProfile === 'custom' || activeProfile === 'showall' || activeC
|
||||
<div class="utci-legend">
|
||||
<span class="utci-legend-label">Thermal stress bands</span>
|
||||
<div class="utci-legend-row">
|
||||
${UTCI_BANDS.map((b, i) => html`
|
||||
<span key=${i} class="utci-legend-item" style=${{ background: bandGradient(b.bg), color: b.fg, ...(b.fontWeight ? { fontWeight: b.fontWeight } : {}), ...(b.textShadow ? { textShadow: b.textShadow } : {}) }}>
|
||||
${b.label}
|
||||
</span>`)}
|
||||
${[
|
||||
{ t: -9, label: 'Freezing' },
|
||||
{ t: 1, label: 'Cold' },
|
||||
{ t: 11, label: 'Cool' },
|
||||
{ t: 21, label: 'Comfortable', bold: true },
|
||||
{ t: 31, label: 'Hot' },
|
||||
{ t: 41, label: 'Very hot' },
|
||||
{ t: 51, label: 'Danger' },
|
||||
].map((b, i) => {
|
||||
const rgb = airTempRgb(b.t) || [200, 200, 200];
|
||||
const mid = `rgb(${rgb[0]},${rgb[1]},${rgb[2]})`;
|
||||
const light = `rgb(${rgb.map(c => Math.min(255, Math.round(c + (255-c)*0.30))).join(',')})`;
|
||||
const dark = `rgb(${rgb.map(c => Math.round(c * 0.96)).join(',')})`;
|
||||
const bg = `linear-gradient(135deg, ${light} 0%, ${mid} 60%, ${dark} 100%)`;
|
||||
const lum = (rgb[0]*299 + rgb[1]*587 + rgb[2]*114) / 1000;
|
||||
const fg = lum > 165 ? '#1a1a1a' : '#ffffff';
|
||||
return html`<span key=${i} class="utci-legend-item" style=${{ background: bg, color: fg, ...(b.bold ? { fontWeight: 700 } : {}) }}>${b.label}</span>`;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user