Environment 'Shade' temperatures.
Add shade to profiles
Add shade to table column
This commit is contained in:
fraxle
2026-06-27 15:15:59 +01:00
parent 05ab4cf76b
commit df3256462a
6 changed files with 146 additions and 83 deletions
+39 -37
View File
@@ -53,8 +53,9 @@ const COLS = [
['UTCI', '°C', 'right', 7, '0.0'],
['Δ UTCIAir', '°C', 'right', 9, '0.0'],
['Tmrt', '°C', 'right', 7, '0.0'],
// Air (3)
// Air (4)
['Air Temp', '°C', 'right', 8, '0.0'],
['Shade Air', '°C', 'right', 9, '0.0'],
['Dew Point', '°C', 'right', 9, '0.0'],
['Humidity', '%', 'right', 8, '0' ],
// UV & Solar (7)
@@ -95,13 +96,13 @@ const COLS = [
['Ragweed', 'g/m³', 'right', 10, '0' ],
];
const TOTAL_COLS = COLS.length; // 41
const TOTAL_COLS = COLS.length; // 42
// Groups: [label, span, bgKey]
const GROUPS = [
['Time & Sun', 2, 'timeSun'],
['Felt Temperature', 5, 'felt' ],
['Air', 3, 'air' ],
['Air', 4, 'air' ],
['UV & Solar', 7, 'uvSolar'],
['Surfaces', 6, 'surf' ],
['Wind', 3, 'wind' ],
@@ -213,54 +214,55 @@ function buildForecastSheet(X, rows, locationName, dateLabel, skinType) {
setNum(X, ws, rowIdx, 6, r.Tmrt, { bg: tempBg(r.Tmrt), fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
// Air
setNum(X, ws, rowIdx, 7, r.Ta, { bg: tempBg(r.Ta), fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
setNum(X, ws, rowIdx, 8, r.dew, { ...def(), numFmt: '0.0' });
setNum(X, ws, rowIdx, 9, r.RH, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 7, r.Ta, { bg: tempBg(r.Ta), fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
setNum(X, ws, rowIdx, 8, r.shadeT, { bg: tempBg(r.shadeT), fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
setNum(X, ws, rowIdx, 9, r.dew, { ...def(), numFmt: '0.0' });
setNum(X, ws, rowIdx, 10, r.RH, { ...def(), numFmt: '0' });
// UV & Solar
const uvFg = r.uv >= 8 ? P.alert : r.uv >= 3 ? P.warn : P.warmDark;
setNum(X, ws, rowIdx, 10, r.uv, { ...def(), fg: uvFg, numFmt: '0.0' });
setNum(X, ws, rowIdx, 11, r.uvA, { ...def(), numFmt: '0.0' });
setNum(X, ws, rowIdx, 12, r.uvB, { ...def(), numFmt: '0.00' });
setNum(X, ws, rowIdx, 11, r.uv, { ...def(), fg: uvFg, numFmt: '0.0' });
setNum(X, ws, rowIdx, 12, r.uvA, { ...def(), numFmt: '0.0' });
setNum(X, ws, rowIdx, 13, r.uvB, { ...def(), numFmt: '0.00' });
const burnMins = sunburnMinutes(r.uv, skinType);
const burnStr = r.uv > 0 ? burnLabel(burnMins) : '—';
const burnFg = r.uv > 0 ? (burnMins < 30 ? P.alert : P.warn) : P.muted;
setStr(X, ws, rowIdx, 13, burnStr, { ...def(), fg: burnFg, align: 'right' });
setNum(X, ws, rowIdx, 14, r.dir, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 15, r.dif, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 16, r.glob, { ...def(), numFmt: '0' });
setStr(X, ws, rowIdx, 14, burnStr, { ...def(), fg: burnFg, align: 'right' });
setNum(X, ws, rowIdx, 15, r.dir, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 16, r.dif, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 17, r.glob, { ...def(), numFmt: '0' });
// Surfaces
setNum(X, ws, rowIdx, 17, r.vehicleT, { bg: tempBg(r.vehicleT), fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
setNum(X, ws, rowIdx, 18, r.indoorT, { bg: tempBg(r.indoorT), fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
setNum(X, ws, rowIdx, 19, r.concreteT, { bg: tempBg(r.concreteT), fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
setNum(X, ws, rowIdx, 20, r.soilT0, { ...def(), numFmt: '0.0' });
setNum(X, ws, rowIdx, 21, r.soilT6, { ...def(), numFmt: '0.0' });
setNum(X, ws, rowIdx, 22, r.soilM != null ? Math.round(r.soilM * 100) : null, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 18, r.vehicleT, { bg: tempBg(r.vehicleT), fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
setNum(X, ws, rowIdx, 19, r.indoorT, { bg: tempBg(r.indoorT), fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
setNum(X, ws, rowIdx, 20, r.concreteT, { bg: tempBg(r.concreteT), fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
setNum(X, ws, rowIdx, 21, r.soilT0, { ...def(), numFmt: '0.0' });
setNum(X, ws, rowIdx, 22, r.soilT6, { ...def(), numFmt: '0.0' });
setNum(X, ws, rowIdx, 23, r.soilM != null ? Math.round(r.soilM * 100) : null, { ...def(), numFmt: '0' });
// Wind
setNum(X, ws, rowIdx, 23, r.va, { ...def(), numFmt: '0.0' });
setNum(X, ws, rowIdx, 24, r.gust, { ...def(), numFmt: '0.0' });
setStr(X, ws, rowIdx, 25, r.compass?.label ?? (r.wd != null ? `${Math.round(r.wd)}°` : '—'), { ...def(), align: 'center' });
setNum(X, ws, rowIdx, 24, r.va, { ...def(), numFmt: '0.0' });
setNum(X, ws, rowIdx, 25, r.gust, { ...def(), numFmt: '0.0' });
setStr(X, ws, rowIdx, 26, r.compass?.label ?? (r.wd != null ? `${Math.round(r.wd)}°` : '—'), { ...def(), align: 'center' });
// Sky & Precip
setNum(X, ws, rowIdx, 26, r.cc, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 27, r.ccLow, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 28, r.ccMid, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 29, r.ccHigh, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 30, r.precip > 0 ? r.precip : null, { ...def(), fg: r.precip > 2 ? P.blue : P.warmDark, numFmt: '0.00' });
setNum(X, ws, rowIdx, 31, r.precipProb, { ...def(), fg: r.precipProb >= 70 ? P.blue : P.warmDark, numFmt: '0' });
setNum(X, ws, rowIdx, 32, r.snow > 0 ? r.snow : null, { ...def(), numFmt: '0.00' });
setNum(X, ws, rowIdx, 33, r.visKm, { ...def(), numFmt: '0.0' });
setNum(X, ws, rowIdx, 27, r.cc, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 28, r.ccLow, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 29, r.ccMid, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 30, r.ccHigh, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 31, r.precip > 0 ? r.precip : null, { ...def(), fg: r.precip > 2 ? P.blue : P.warmDark, numFmt: '0.00' });
setNum(X, ws, rowIdx, 32, r.precipProb, { ...def(), fg: r.precipProb >= 70 ? P.blue : P.warmDark, numFmt: '0' });
setNum(X, ws, rowIdx, 33, r.snow > 0 ? r.snow : null, { ...def(), numFmt: '0.00' });
setNum(X, ws, rowIdx, 34, r.visKm, { ...def(), numFmt: '0.0' });
// Air Quality
setNum(X, ws, rowIdx, 34, r.aqi, { ...def(), fg: r.aqi >= 50 ? P.alert : P.warmDark, numFmt: '0' });
setNum(X, ws, rowIdx, 35, r.grassPollen, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 36, r.birchPollen, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 37, r.alderPollen, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 38, r.mugwortPollen, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 39, r.olivePollen, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 40, r.ragweedPollen, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 35, r.aqi, { ...def(), fg: r.aqi >= 50 ? P.alert : P.warmDark, numFmt: '0' });
setNum(X, ws, rowIdx, 36, r.grassPollen, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 37, r.birchPollen, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 38, r.alderPollen, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 39, r.mugwortPollen, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 40, r.olivePollen, { ...def(), numFmt: '0' });
setNum(X, ws, rowIdx, 41, r.ragweedPollen, { ...def(), numFmt: '0' });
});
// ── Sheet metadata ────────────────────────────────────────────────────