3.2.0.0
Environment 'Shade' temperatures. Add shade to profiles Add shade to table column
This commit is contained in:
+4
-1
@@ -339,7 +339,7 @@ export function UTCIForecast() {
|
|||||||
const GROUP_ORDER = {
|
const GROUP_ORDER = {
|
||||||
felt: ['utciP', 'vehicleT', 'indoorT', 'managedT', 'burn', 'utci', 'delta', 'tmrt'],
|
felt: ['utciP', 'vehicleT', 'indoorT', 'managedT', 'burn', 'utci', 'delta', 'tmrt'],
|
||||||
surface: ['concreteT', 'soilT', 'soilT6', 'soilM'],
|
surface: ['concreteT', 'soilT', 'soilT6', 'soilM'],
|
||||||
ambient: ['air', 'rh', 'dew'],
|
ambient: ['shadeT', 'air', 'rh', 'dew'],
|
||||||
precip: ['precip', 'precipProb', 'lightning'],
|
precip: ['precip', 'precipProb', 'lightning'],
|
||||||
sky: ['cloud', 'vis'],
|
sky: ['cloud', 'vis'],
|
||||||
wind: ['wind', 'dir'],
|
wind: ['wind', 'dir'],
|
||||||
@@ -1066,6 +1066,7 @@ export function UTCIForecast() {
|
|||||||
${isPro && (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['soilT']) && html`<button class=${`col-toggle grp-surface${visibleCols.soilT ? ' on' : ''}`} onClick=${() => toggleCol('soilT')}>Soil °C</button>`}
|
${isPro && (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['soilT']) && html`<button class=${`col-toggle grp-surface${visibleCols.soilT ? ' on' : ''}`} onClick=${() => toggleCol('soilT')}>Soil °C</button>`}
|
||||||
${isPro && (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['soilT6']) && html`<button class=${`col-toggle grp-surface${visibleCols.soilT6 ? ' on' : ''}`} onClick=${() => toggleCol('soilT6')}>Soil 6cm</button>`}
|
${isPro && (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['soilT6']) && html`<button class=${`col-toggle grp-surface${visibleCols.soilT6 ? ' on' : ''}`} onClick=${() => toggleCol('soilT6')}>Soil 6cm</button>`}
|
||||||
${isPro && (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['soilM']) && html`<button class=${`col-toggle grp-surface${visibleCols.soilM ? ' on' : ''}`} onClick=${() => toggleCol('soilM')}>Soil moist</button>`}
|
${isPro && (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['soilM']) && html`<button class=${`col-toggle grp-surface${visibleCols.soilM ? ' on' : ''}`} onClick=${() => toggleCol('soilM')}>Soil moist</button>`}
|
||||||
|
${isPro && (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['shadeT']) && html`<button class=${`col-toggle grp-ambient${visibleCols.shadeT ? ' on' : ''}`} onClick=${() => toggleCol('shadeT')}>Shade</button>`}
|
||||||
${isPro && (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['air']) && html`<button class=${`col-toggle grp-ambient${visibleCols.air ? ' on' : ''}`} onClick=${() => toggleCol('air')}>Air</button>`}
|
${isPro && (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['air']) && html`<button class=${`col-toggle grp-ambient${visibleCols.air ? ' on' : ''}`} onClick=${() => toggleCol('air')}>Air</button>`}
|
||||||
${isPro && (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['rh']) && html`<button class=${`col-toggle grp-ambient${visibleCols.rh ? ' on' : ''}`} onClick=${() => toggleCol('rh')}>RH</button>`}
|
${isPro && (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['rh']) && html`<button class=${`col-toggle grp-ambient${visibleCols.rh ? ' on' : ''}`} onClick=${() => toggleCol('rh')}>RH</button>`}
|
||||||
${isPro && (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['dew']) && html`<button class=${`col-toggle grp-ambient${visibleCols.dew ? ' on' : ''}`} onClick=${() => toggleCol('dew')}>Dew</button>`}
|
${isPro && (activeProfile === 'custom' || activeProfile === 'showall' || activeCols['dew']) && html`<button class=${`col-toggle grp-ambient${visibleCols.dew ? ' on' : ''}`} onClick=${() => toggleCol('dew')}>Dew</button>`}
|
||||||
@@ -1279,6 +1280,7 @@ export function UTCIForecast() {
|
|||||||
${visibleCols.soilT && html`<th class=${`col-info-th ${groupStart('soilT')} ${groupColor('soilT')}`} scope="col" onClick=${(e) => handleThClick('soilT', e)} onMouseEnter=${(e) => handleThEnter('soilT', e)} onMouseLeave=${handleThLeave}>Soil °C <span class="col-unit">surface</span></th>`}
|
${visibleCols.soilT && html`<th class=${`col-info-th ${groupStart('soilT')} ${groupColor('soilT')}`} scope="col" onClick=${(e) => handleThClick('soilT', e)} onMouseEnter=${(e) => handleThEnter('soilT', e)} onMouseLeave=${handleThLeave}>Soil °C <span class="col-unit">surface</span></th>`}
|
||||||
${visibleCols.soilT6 && html`<th class=${`col-info-th ${groupStart('soilT6')} ${groupColor('soilT6')}`} scope="col" onClick=${(e) => handleThClick('soilT6', e)} onMouseEnter=${(e) => handleThEnter('soilT6', e)} onMouseLeave=${handleThLeave}>Soil 6cm <span class="col-unit">°C root</span></th>`}
|
${visibleCols.soilT6 && html`<th class=${`col-info-th ${groupStart('soilT6')} ${groupColor('soilT6')}`} scope="col" onClick=${(e) => handleThClick('soilT6', e)} onMouseEnter=${(e) => handleThEnter('soilT6', e)} onMouseLeave=${handleThLeave}>Soil 6cm <span class="col-unit">°C root</span></th>`}
|
||||||
${visibleCols.soilM && html`<th class=${`col-info-th ${groupStart('soilM')} ${groupColor('soilM')}`} scope="col" onClick=${(e) => handleThClick('soilM', e)} onMouseEnter=${(e) => handleThEnter('soilM', e)} onMouseLeave=${handleThLeave}>Soil moist <span class="col-unit">%</span></th>`}
|
${visibleCols.soilM && html`<th class=${`col-info-th ${groupStart('soilM')} ${groupColor('soilM')}`} scope="col" onClick=${(e) => handleThClick('soilM', e)} onMouseEnter=${(e) => handleThEnter('soilM', e)} onMouseLeave=${handleThLeave}>Soil moist <span class="col-unit">%</span></th>`}
|
||||||
|
${visibleCols.shadeT && html`<th class=${`utci-tight-head col-info-th ${groupStart('shadeT')} ${groupColor('shadeT')}`} scope="col" onClick=${(e) => handleThClick('shadeT', e)} onMouseEnter=${(e) => handleThEnter('shadeT', e)} onMouseLeave=${handleThLeave}>Shade <span class="col-unit">°C</span></th>`}
|
||||||
${visibleCols.air && html`<th class=${`utci-tight-head col-info-th ${groupStart('air')} ${groupColor('air')}`} scope="col" onClick=${(e) => handleThClick('air', e)} onMouseEnter=${(e) => handleThEnter('air', e)} onMouseLeave=${handleThLeave}>Air <span class="col-unit">°C</span></th>`}
|
${visibleCols.air && html`<th class=${`utci-tight-head col-info-th ${groupStart('air')} ${groupColor('air')}`} scope="col" onClick=${(e) => handleThClick('air', e)} onMouseEnter=${(e) => handleThEnter('air', e)} onMouseLeave=${handleThLeave}>Air <span class="col-unit">°C</span></th>`}
|
||||||
${visibleCols.rh && html`<th class=${`col-info-th ${groupStart('rh')} ${groupColor('rh')}`} scope="col" onClick=${(e) => handleThClick('rh', e)} onMouseEnter=${(e) => handleThEnter('rh', e)} onMouseLeave=${handleThLeave}>RH <span class="col-unit">%</span></th>`}
|
${visibleCols.rh && html`<th class=${`col-info-th ${groupStart('rh')} ${groupColor('rh')}`} scope="col" onClick=${(e) => handleThClick('rh', e)} onMouseEnter=${(e) => handleThEnter('rh', e)} onMouseLeave=${handleThLeave}>RH <span class="col-unit">%</span></th>`}
|
||||||
${visibleCols.dew && html`<th class=${`col-info-th ${groupStart('dew')} ${groupColor('dew')}`} scope="col" onClick=${(e) => handleThClick('dew', e)} onMouseEnter=${(e) => handleThEnter('dew', e)} onMouseLeave=${handleThLeave}>Dew <span class="col-unit">°C</span></th>`}
|
${visibleCols.dew && html`<th class=${`col-info-th ${groupStart('dew')} ${groupColor('dew')}`} scope="col" onClick=${(e) => handleThClick('dew', e)} onMouseEnter=${(e) => handleThEnter('dew', e)} onMouseLeave=${handleThLeave}>Dew <span class="col-unit">°C</span></th>`}
|
||||||
@@ -1461,6 +1463,7 @@ export function UTCIForecast() {
|
|||||||
<td class=${groupStart('soilT6')} style=${{ color: airTempFontColor(), background: airTempBg(r.soilT6, rPrev?.soilT6, rNext?.soilT6) }}>${r.soilT6 != null ? fmt(r.soilT6) + u('°C') : '—'}</td>`}
|
<td class=${groupStart('soilT6')} style=${{ color: airTempFontColor(), background: airTempBg(r.soilT6, rPrev?.soilT6, rNext?.soilT6) }}>${r.soilT6 != null ? fmt(r.soilT6) + u('°C') : '—'}</td>`}
|
||||||
${visibleCols.soilM && html`
|
${visibleCols.soilM && html`
|
||||||
<td class=${groupStart('soilM')} style=${(() => { const sm = soilMoistureBg(r.soilM); return { color: sm.fg, background: sm.bg }; })()}>${r.soilM != null ? fmt(r.soilM * 100, 1) + u('%') : '—'}</td>`}
|
<td class=${groupStart('soilM')} style=${(() => { const sm = soilMoistureBg(r.soilM); return { color: sm.fg, background: sm.bg }; })()}>${r.soilM != null ? fmt(r.soilM * 100, 1) + u('%') : '—'}</td>`}
|
||||||
|
${visibleCols.shadeT && html`<td class=${groupStart('shadeT')} style=${{ background: airTempBg(r.shadeT, rPrev?.shadeT, rNext?.shadeT), color: airTempFontColor() }}>${r.shadeT != null ? fmt(r.shadeT) + u('°C') : '—'}</td>`}
|
||||||
${visibleCols.air && html`<td class=${groupStart('air')} style=${{ background: airTempBg(r.Ta, rPrev?.Ta, rNext?.Ta), color: airTempFontColor() }}>${fmt(r.Ta)}${u('°C')}</td>`}
|
${visibleCols.air && html`<td class=${groupStart('air')} style=${{ background: airTempBg(r.Ta, rPrev?.Ta, rNext?.Ta), color: airTempFontColor() }}>${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: airTempBg(r.dew, rPrev?.dew, rNext?.dew), color: airTempFontColor() }}>${fmt(r.dew)}${u('°C')}</td>`}
|
${visibleCols.dew && html`<td class=${groupStart('dew')} style=${{ background: airTempBg(r.dew, rPrev?.dew, rNext?.dew), color: airTempFontColor() }}>${fmt(r.dew)}${u('°C')}</td>`}
|
||||||
|
|||||||
@@ -313,9 +313,9 @@ export function DayTabs({
|
|||||||
const utcipVals = d.rows.map(r => r.utciAdj).filter(v => isFinite(v));
|
const utcipVals = d.rows.map(r => r.utciAdj).filter(v => isFinite(v));
|
||||||
const dayHi = utcipVals.length ? Math.round(Math.max(...utcipVals)) : null;
|
const dayHi = utcipVals.length ? Math.round(Math.max(...utcipVals)) : null;
|
||||||
const dayLo = utcipVals.length ? Math.round(Math.min(...utcipVals)) : null;
|
const dayLo = utcipVals.length ? Math.round(Math.min(...utcipVals)) : null;
|
||||||
const airVals = d.rows.map(r => r.Ta).filter(v => isFinite(v));
|
const shadeVals = d.rows.map(r => r.shadeT).filter(v => isFinite(v));
|
||||||
const airHi = airVals.length ? Math.round(Math.max(...airVals)) : null;
|
const shadeHi = shadeVals.length ? Math.round(Math.max(...shadeVals)) : null;
|
||||||
const airLo = airVals.length ? Math.round(Math.min(...airVals)) : null;
|
const shadeLo = shadeVals.length ? Math.round(Math.min(...shadeVals)) : null;
|
||||||
|
|
||||||
// Day-tab weather icon - use core daylight rows (elev > 10°) where
|
// Day-tab weather icon - use core daylight rows (elev > 10°) where
|
||||||
// available, falling back to any above-horizon rows, then all rows.
|
// available, falling back to any above-horizon rows, then all rows.
|
||||||
@@ -415,12 +415,12 @@ export function DayTabs({
|
|||||||
}}>
|
}}>
|
||||||
<span style=${{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '1px' }}>
|
<span style=${{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '1px' }}>
|
||||||
<span style=${{ color: '#c0622a', fontWeight: 800, fontSize: '14px', lineHeight: 1.1 }}>${dayHi}°</span>
|
<span style=${{ color: '#c0622a', fontWeight: 800, fontSize: '14px', lineHeight: 1.1 }}>${dayHi}°</span>
|
||||||
${airHi !== null && html`<span style=${{ color: '#c0622a', fontWeight: 600, fontSize: '11px', lineHeight: 1.1 }}>${airHi}°</span>`}
|
${shadeHi !== null && html`<span style=${{ color: '#c0622a', fontWeight: 600, fontSize: '11px', lineHeight: 1.1 }}>${shadeHi}°</span>`}
|
||||||
</span>
|
</span>
|
||||||
<span style=${{ width: '1px', background: 'rgba(0,0,0,0.15)', margin: '2px 0', alignSelf: 'stretch' }}></span>
|
<span style=${{ width: '1px', background: 'rgba(0,0,0,0.15)', margin: '2px 0', alignSelf: 'stretch' }}></span>
|
||||||
<span style=${{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '1px' }}>
|
<span style=${{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '1px' }}>
|
||||||
<span style=${{ color: '#3a6080', fontWeight: 800, fontSize: '14px', lineHeight: 1.1 }}>${dayLo}°</span>
|
<span style=${{ color: '#3a6080', fontWeight: 800, fontSize: '14px', lineHeight: 1.1 }}>${dayLo}°</span>
|
||||||
${airLo !== null && html`<span style=${{ color: '#3a6080', fontWeight: 600, fontSize: '11px', lineHeight: 1.1 }}>${airLo}°</span>`}
|
${shadeLo !== null && html`<span style=${{ color: '#3a6080', fontWeight: 600, fontSize: '11px', lineHeight: 1.1 }}>${shadeLo}°</span>`}
|
||||||
</span>
|
</span>
|
||||||
</span>`}
|
</span>`}
|
||||||
</button>`;
|
</button>`;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
import {
|
import {
|
||||||
vaporPressureHpa, solarElevationDeg, calcTmrt, utciApprox,
|
vaporPressureHpa, solarElevationDeg, calcTmrt, utciApprox,
|
||||||
calcConcreteTempPass, calcVehicleInteriorTemp,
|
calcConcreteTempPass, calcVehicleInteriorTemp,
|
||||||
calcIndoorTempPass, calcManagedIndoorTempPass,
|
calcIndoorTempPass, calcManagedIndoorTempPass, calcShadeAirTemp,
|
||||||
} from './physics.js';
|
} from './physics.js';
|
||||||
import { windCompass8, uvSplit, cloudCategory, precipPenalty, sunburnMinutes, burnLabel } from './utils.js';
|
import { windCompass8, uvSplit, cloudCategory, precipPenalty, sunburnMinutes, burnLabel } from './utils.js';
|
||||||
import { UTCI_ENVIRONMENTS } from './config.js';
|
import { UTCI_ENVIRONMENTS } from './config.js';
|
||||||
@@ -77,6 +77,9 @@ export function buildHourlyRows({ forecast, airQuality, location, vehicleType, v
|
|||||||
// a surface as direct beam, so we weight it accordingly.
|
// a surface as direct beam, so we weight it accordingly.
|
||||||
const effectiveRad = dir + dif * 0.2;
|
const effectiveRad = dir + dif * 0.2;
|
||||||
// concreteT is now stamped in the two-pass section below (thermal lag).
|
// concreteT is now stamped in the two-pass section below (thermal lag).
|
||||||
|
// Shade air temp: the air you'd feel in this Solar Model's typical shade
|
||||||
|
// (building shadow, beach umbrella, canopy...). Uses the env's shade block.
|
||||||
|
const shadeT = calcShadeAirTemp(Ta, effectiveRad, va, elev, env.shade);
|
||||||
const vehicleT = calcVehicleInteriorTemp(Ta, glob, elev, vehicleType, vehicleVent, vehicleSpeed);
|
const vehicleT = calcVehicleInteriorTemp(Ta, glob, elev, vehicleType, vehicleVent, vehicleSpeed);
|
||||||
const eh = vaporPressureHpa(Ta, RH);
|
const eh = vaporPressureHpa(Ta, RH);
|
||||||
// Apply environment modifier - adjust solar inputs and air temp for shaded environments.
|
// Apply environment modifier - adjust solar inputs and air temp for shaded environments.
|
||||||
@@ -147,7 +150,7 @@ export function buildHourlyRows({ forecast, airQuality, location, vehicleType, v
|
|||||||
cc, ccLow, ccMid, ccHigh, cloudCat,
|
cc, ccLow, ccMid, ccHigh, cloudCat,
|
||||||
uv, uvA, uvB,
|
uv, uvA, uvB,
|
||||||
precip, precipProb, lightning, cape, snow,
|
precip, precipProb, lightning, cape, snow,
|
||||||
soilT0, soilT6, soilM, vehicleT,
|
soilT0, soilT6, soilM, vehicleT, shadeT,
|
||||||
effectiveRad,
|
effectiveRad,
|
||||||
elev, Tmrt, utci, utciAdj, eh, compass,
|
elev, Tmrt, utci, utciAdj, eh, compass,
|
||||||
visKm, aqi,
|
visKm, aqi,
|
||||||
@@ -258,7 +261,7 @@ export function buildHourlyRows({ forecast, airQuality, location, vehicleType, v
|
|||||||
// interval <= 1 returns the input untouched (zero behaviour change).
|
// interval <= 1 returns the input untouched (zero behaviour change).
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
const AGG_MEAN = [
|
const AGG_MEAN = [
|
||||||
'Ta', 'RH', 'dew', 'va', 'dir', 'dif', 'cc', 'ccLow', 'ccMid', 'ccHigh',
|
'Ta', 'shadeT', 'RH', 'dew', 'va', 'dir', 'dif', 'cc', 'ccLow', 'ccMid', 'ccHigh',
|
||||||
'soilT0', 'soilT6', 'soilM', 'Tmrt', 'eh', 'visKm', 'aqi', 'effectiveRad',
|
'soilT0', 'soilT6', 'soilM', 'Tmrt', 'eh', 'visKm', 'aqi', 'effectiveRad',
|
||||||
'grassPollen', 'birchPollen', 'alderPollen', 'mugwortPollen', 'olivePollen', 'ragweedPollen',
|
'grassPollen', 'birchPollen', 'alderPollen', 'mugwortPollen', 'olivePollen', 'ragweedPollen',
|
||||||
];
|
];
|
||||||
|
|||||||
+44
-37
@@ -29,46 +29,46 @@ export const FILTER_PROFILES = {
|
|||||||
label: 'Basic',
|
label: 'Basic',
|
||||||
icon: '🌡️',
|
icon: '🌡️',
|
||||||
scene: "url('assets/images/profiles/basic.png') center / cover no-repeat, linear-gradient(160deg,#b8d8e8,#daeef8)",
|
scene: "url('assets/images/profiles/basic.png') center / cover no-repeat, linear-gradient(160deg,#b8d8e8,#daeef8)",
|
||||||
cols: { hour: true, air: true, rh: true, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: false, pollen: true },
|
cols: { hour: true, air: true, shadeT: true, rh: true, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: false, pollen: true },
|
||||||
},
|
},
|
||||||
home: {
|
home: {
|
||||||
label: 'Home',
|
label: 'Home',
|
||||||
icon: '🏠',
|
icon: '🏠',
|
||||||
scene: "url('assets/images/profiles/home.png') center / cover no-repeat, linear-gradient(160deg,#b0d890,#d0ecb8)",
|
scene: "url('assets/images/profiles/home.png') center / cover no-repeat, linear-gradient(160deg,#b0d890,#d0ecb8)",
|
||||||
cols: { hour: true, air: true, rh: true, dew: true, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: false, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: true, managedT: true, vis: false, aqi: true, pollen: true },
|
cols: { hour: true, air: true, shadeT: false, rh: true, dew: true, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: false, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: true, managedT: true, vis: false, aqi: true, pollen: true },
|
||||||
},
|
},
|
||||||
vehicle: {
|
vehicle: {
|
||||||
label: 'Vehicle',
|
label: 'Vehicle',
|
||||||
icon: '🚗',
|
icon: '🚗',
|
||||||
scene: "url('assets/images/profiles/vehicle.png') center / cover no-repeat, linear-gradient(160deg,#a8bcc8,#ccd8e4)",
|
scene: "url('assets/images/profiles/vehicle.png') center / cover no-repeat, linear-gradient(160deg,#a8bcc8,#ccd8e4)",
|
||||||
cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: true, concreteT: false, vehicleT: true, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false },
|
cols: { hour: true, air: true, shadeT: false, rh: false, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: true, concreteT: false, vehicleT: true, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false },
|
||||||
},
|
},
|
||||||
farming: {
|
farming: {
|
||||||
label: 'Farming',
|
label: 'Farming',
|
||||||
icon: '🌾',
|
icon: '🌾',
|
||||||
scene: "url('assets/images/profiles/farming.png') center / cover no-repeat, linear-gradient(160deg,#d8bc90,#ece0b8)",
|
scene: "url('assets/images/profiles/farming.png') center / cover no-repeat, linear-gradient(160deg,#d8bc90,#ece0b8)",
|
||||||
cols: { hour: true, air: true, rh: true, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: true, soilT6: true, soilM: true, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: true, pollen: true },
|
cols: { hour: true, air: true, shadeT: true, rh: true, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: true, soilT6: true, soilM: true, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: true, pollen: true },
|
||||||
},
|
},
|
||||||
outdoors: {
|
outdoors: {
|
||||||
label: 'Places',
|
label: 'Places',
|
||||||
icon: '🌤️',
|
icon: '🌤️',
|
||||||
scene: "url('assets/images/profiles/places.png') center / cover no-repeat, linear-gradient(160deg,#98ccc0,#c4e4dc)",
|
scene: "url('assets/images/profiles/places.png') center / cover no-repeat, linear-gradient(160deg,#98ccc0,#c4e4dc)",
|
||||||
// Default cols match the first variant (urban). Switching variant updates visibleCols.
|
// Default cols match the first variant (urban). Switching variant updates visibleCols.
|
||||||
cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: true, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: true, pollen: false },
|
cols: { hour: true, air: true, shadeT: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: true, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: true, pollen: false },
|
||||||
},
|
},
|
||||||
alltemps: {
|
alltemps: {
|
||||||
label: 'Temps',
|
label: 'Temps',
|
||||||
icon: '🌡️',
|
icon: '🌡️',
|
||||||
scene: "url('assets/images/profiles/temps.png') center / cover no-repeat, linear-gradient(160deg,#d8b0b0,#ecd0c8)",
|
scene: "url('assets/images/profiles/temps.png') center / cover no-repeat, linear-gradient(160deg,#d8b0b0,#ecd0c8)",
|
||||||
proOnly: true,
|
proOnly: true,
|
||||||
cols: { hour: true, air: true, rh: false, dew: false, wind: false, dir: false, cloud: false, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: false, precipProb: false, lightning: false, soilT: true, soilT6: false, soilM: false, concreteT: true, vehicleT: true, indoorT: true, managedT: true, vis: false, aqi: false, pollen: false },
|
cols: { hour: true, air: true, shadeT: true, rh: false, dew: false, wind: false, dir: false, cloud: false, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: false, precipProb: false, lightning: false, soilT: true, soilT6: false, soilM: false, concreteT: true, vehicleT: true, indoorT: true, managedT: true, vis: false, aqi: false, pollen: false },
|
||||||
},
|
},
|
||||||
showall: {
|
showall: {
|
||||||
label: 'Show All',
|
label: 'Show All',
|
||||||
icon: '⚙️',
|
icon: '⚙️',
|
||||||
scene: "url('assets/images/profiles/all.png') center / cover no-repeat, linear-gradient(160deg,#b4b4d4,#d4d4ec)",
|
scene: "url('assets/images/profiles/all.png') center / cover no-repeat, linear-gradient(160deg,#b4b4d4,#d4d4ec)",
|
||||||
proOnly: true,
|
proOnly: true,
|
||||||
cols: { hour: true, air: true, rh: true, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: true, diffuse: true, tmrt: true, delta: true, utci: true, uvA: true, uvB: true, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: true, soilT6: true, soilM: true, concreteT: true, vehicleT: true, indoorT: true, managedT: true, vis: true, aqi: true, pollen: true },
|
cols: { hour: true, air: true, shadeT: true, rh: true, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: true, diffuse: true, tmrt: true, delta: true, utci: true, uvA: true, uvB: true, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: true, soilT6: true, soilM: true, concreteT: true, vehicleT: true, indoorT: true, managedT: true, vis: true, aqi: true, pollen: true },
|
||||||
},
|
},
|
||||||
custom: {
|
custom: {
|
||||||
label: 'Custom',
|
label: 'Custom',
|
||||||
@@ -76,39 +76,39 @@ export const FILTER_PROFILES = {
|
|||||||
scene: "url('assets/images/profiles/custom.png') center / cover no-repeat, linear-gradient(160deg,#d8b0cc,#ecd0e4)",
|
scene: "url('assets/images/profiles/custom.png') center / cover no-repeat, linear-gradient(160deg,#d8b0cc,#ecd0e4)",
|
||||||
proOnly: true,
|
proOnly: true,
|
||||||
// Blank slate - only Hour is on. User enables whichever columns they want.
|
// Blank slate - only Hour is on. User enables whichever columns they want.
|
||||||
cols: { hour: true, air: false, rh: false, dew: false, wind: false, dir: false, cloud: false, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: false, precip: false, precipProb: false, lightning: false, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: false, pollen: false },
|
cols: { hour: true, air: false, shadeT: false, rh: false, dew: false, wind: false, dir: false, cloud: false, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: false, precip: false, precipProb: false, lightning: false, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: false, pollen: false },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Places sub-variants - each has its own column set.
|
// Places sub-variants - each has its own column set.
|
||||||
// Selecting a variant applies its cols to visibleCols.
|
// Selecting a variant applies its cols to visibleCols.
|
||||||
export const OUTDOORS_VARIANTS = {
|
export const OUTDOORS_VARIANTS = {
|
||||||
urban: { name: 'Urban', scene: "url('assets/images/profiles/urban.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: true, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: true, pollen: false } },
|
urban: { name: 'Urban', scene: "url('assets/images/profiles/urban.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: true, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: true, pollen: false } },
|
||||||
beach: { name: 'Beach', scene: "url('assets/images/profiles/beach.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: true, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
beach: { name: 'Beach', scene: "url('assets/images/profiles/beach.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: true, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
||||||
events: { name: 'Events', scene: "url('assets/images/profiles/events.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: true, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: false, pollen: true } },
|
events: { name: 'Events', scene: "url('assets/images/profiles/events.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: true, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: false, pollen: true } },
|
||||||
festival: { name: 'Festival', scene: "url('assets/images/profiles/festival.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: true, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: true, pollen: true } },
|
festival: { name: 'Festival', scene: "url('assets/images/profiles/festival.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: true, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: true, pollen: true } },
|
||||||
wintersports: { name: 'Winter Sports', proOnly: true, scene: "url('assets/images/profiles/winter.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: true, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
wintersports: { name: 'Winter Sports', proOnly: true, scene: "url('assets/images/profiles/winter.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: false, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: true, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
||||||
naturist: { name: 'Naturist', proOnly: true, scene: "url('assets/images/profiles/naturist.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: false, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: true, uvA: true, uvB: true, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: true, pollen: true } },
|
naturist: { name: 'Naturist', proOnly: true, scene: "url('assets/images/profiles/naturist.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: true, rh: false, dew: true, wind: true, dir: false, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: true, uvA: true, uvB: true, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: true, pollen: true } },
|
||||||
sailing: { name: 'Sailing', proOnly: true, scene: "url('assets/images/profiles/sailing.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
sailing: { name: 'Sailing', proOnly: true, scene: "url('assets/images/profiles/sailing.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: false, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
||||||
// Places - free
|
// Places - free
|
||||||
park: { name: 'Park / Picnic', scene: "url('assets/images/profiles/park.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: false, pollen: true } },
|
park: { name: 'Park / Picnic', scene: "url('assets/images/profiles/park.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: true, rh: false, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: false, pollen: true } },
|
||||||
forest: { name: 'Forest / Woodland', scene: "url('assets/images/profiles/forest.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: true, pollen: true } },
|
forest: { name: 'Forest / Woodland', scene: "url('assets/images/profiles/forest.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: false, rh: false, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: true, pollen: true } },
|
||||||
airport: { name: 'Airport / Travel', scene: "url('assets/images/profiles/airport.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: false, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
airport: { name: 'Airport / Travel', scene: "url('assets/images/profiles/airport.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: false, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: false, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
||||||
// Places - Pro
|
// Places - Pro
|
||||||
construction: { name: 'Construction', proOnly: true, scene: "url('assets/images/profiles/construction.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: true, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: true, pollen: false } },
|
construction: { name: 'Construction', proOnly: true, scene: "url('assets/images/profiles/construction.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: true, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: true, pollen: false } },
|
||||||
// Activities - free
|
// Activities - free
|
||||||
cycling: { name: 'Cycling', scene: "url('assets/images/profiles/cycling.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
cycling: { name: 'Cycling', scene: "url('assets/images/profiles/cycling.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: false, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
||||||
running: { name: 'Running', scene: "url('assets/images/profiles/running.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: true, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: true, uvA: false, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: false, pollen: true } },
|
running: { name: 'Running', scene: "url('assets/images/profiles/running.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: false, rh: true, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: true, uvA: false, uvB: false, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: false, pollen: true } },
|
||||||
dogwalk: { name: 'Dog Walking', scene: "url('assets/images/profiles/walking.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: false, pollen: true } },
|
dogwalk: { name: 'Dog Walking', scene: "url('assets/images/profiles/walking.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: false, rh: false, dew: false, wind: true, dir: false, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: false, aqi: false, pollen: true } },
|
||||||
// Activities - Pro
|
// Activities - Pro
|
||||||
hiking: { name: 'Hiking', proOnly: true, scene: "url('assets/images/profiles/hiking.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: true, uvA: true, uvB: true, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
hiking: { name: 'Hiking', proOnly: true, scene: "url('assets/images/profiles/hiking.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: false, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: true, direct: false, diffuse: false, tmrt: false, delta: false, utci: true, uvA: true, uvB: true, burn: true, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
||||||
photography: { name: 'Photography', proOnly: true, scene: "url('assets/images/profiles/photography.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: false, wind: false, dir: false, cloud: true, sun: true, direct: true, diffuse: true, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: false, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
photography: { name: 'Photography', proOnly: true, scene: "url('assets/images/profiles/photography.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: true, rh: false, dew: false, wind: false, dir: false, cloud: true, sun: true, direct: true, diffuse: true, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: false, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
||||||
fishing: { name: 'Fishing', proOnly: true, scene: "url('assets/images/profiles/fishing.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
fishing: { name: 'Fishing', proOnly: true, scene: "url('assets/images/profiles/fishing.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: true, rh: false, dew: true, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
||||||
// Work variants
|
// Work variants
|
||||||
market: { name: 'Market Trading', proOnly: true, scene: "url('assets/images/profiles/market.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
market: { name: 'Market Trading', proOnly: true, scene: "url('assets/images/profiles/market.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
||||||
windowcleaning: { name: 'Window Cleaning', proOnly: true, scene: "url('assets/images/profiles/window.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
windowcleaning: { name: 'Window Cleaning', proOnly: true, scene: "url('assets/images/profiles/window.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: false, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
||||||
office: { name: 'Office', proOnly: true, scene: "url('assets/images/profiles/office.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: true, dew: false, wind: false, dir: false, cloud: true, sun: true, direct: true, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: true, vis: false, aqi: true, pollen: false } },
|
office: { name: 'Office', proOnly: true, scene: "url('assets/images/profiles/office.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: false, rh: true, dew: false, wind: false, dir: false, cloud: true, sun: true, direct: true, diffuse: false, tmrt: false, delta: false, utci: false, uvA: false, uvB: false, burn: false, utciP: true, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: false, indoorT: false, managedT: true, vis: false, aqi: true, pollen: false } },
|
||||||
driver: { name: 'Driver / Trucker', proOnly: true, scene: "url('assets/images/profiles/vehicle.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: false, utciP: false, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: true, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
driver: { name: 'Driver / Trucker', proOnly: true, scene: "url('assets/images/profiles/vehicle.png') center / cover no-repeat, linear-gradient(160deg,#c2d4cc,#e2ece6)", cols: { hour: true, air: true, shadeT: false, rh: false, dew: false, wind: true, dir: true, cloud: true, sun: false, direct: false, diffuse: false, tmrt: false, delta: false, utci: false, uvA: true, uvB: false, burn: false, utciP: false, precip: true, precipProb: true, lightning: true, soilT: false, soilT6: false, soilM: false, concreteT: false, vehicleT: true, indoorT: false, managedT: false, vis: true, aqi: false, pollen: false } },
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pollen type options shown in the pulldown selector.
|
// Pollen type options shown in the pulldown selector.
|
||||||
@@ -130,15 +130,21 @@ export const POLLEN_TYPES = {
|
|||||||
// taOffset - offset applied to air temperature in degrees C (evapotranspiration etc.)
|
// taOffset - offset applied to air temperature in degrees C (evapotranspiration etc.)
|
||||||
// label - short display name
|
// label - short display name
|
||||||
// note - shown in column header tooltip to flag modified calculation
|
// note - shown in column header tooltip to flag modified calculation
|
||||||
|
// shade - per-environment microclimate inputs for the Shade air-temp
|
||||||
|
// column (see calcShadeAirTemp in physics.js):
|
||||||
|
// offset base air-temp delta sitting in this env's shade (-C)
|
||||||
|
// shelter 0-1 wind shelter (high = enclosed, calm pocket)
|
||||||
|
// sun 0-~1.2 how sun-baked the surroundings are
|
||||||
|
// type short label of the typical shade for the tooltip
|
||||||
export const UTCI_ENVIRONMENTS = {
|
export const UTCI_ENVIRONMENTS = {
|
||||||
open: { shortLabel: null, label: 'Grass / Open Field', dirFactor: 1.00, difFactor: 1.00, globFactor: 1.00, taOffset: 0.0, note: null },
|
open: { shortLabel: null, label: 'Grass / Open Field', dirFactor: 1.00, difFactor: 1.00, globFactor: 1.00, taOffset: 0.0, note: null, shade: { offset: 0.5, shelter: 0.45, sun: 0.50, type: 'hedge / scattered trees' } },
|
||||||
urban: { shortLabel: 'City', label: 'Town / City', dirFactor: 0.70, difFactor: 0.75, globFactor: 0.82, taOffset: 2.5, note: 'Urban heat island estimate - UHI raises air temp ~2–3 °C, building canyons reduce direct and diffuse solar load, reflections from concrete and glass partially offset sky-view reduction' },
|
urban: { shortLabel: 'City', label: 'Town / City', dirFactor: 0.70, difFactor: 0.75, globFactor: 0.82, taOffset: 2.5, note: 'Urban heat island estimate - UHI raises air temp ~2–3 °C, building canyons reduce direct and diffuse solar load, reflections from concrete and glass partially offset sky-view reduction', shade: { offset: 1.5, shelter: 0.75, sun: 0.85, type: 'building shadow' } },
|
||||||
beach: { shortLabel: 'Beach', label: 'Beach / Lakeside', dirFactor: 1.12, difFactor: 1.06, globFactor: 1.10, taOffset: 0.0, note: 'Reflected radiation from water and sand surfaces increases effective solar load - no shade, higher UV dose' },
|
beach: { shortLabel: 'Beach', label: 'Beach / Lakeside', dirFactor: 1.12, difFactor: 1.06, globFactor: 1.10, taOffset: 0.0, note: 'Reflected radiation from water and sand surfaces increases effective solar load - no shade, higher UV dose', shade: { offset: 0.5, shelter: 0.25, sun: 0.95, type: 'beach umbrella' } },
|
||||||
river: { shortLabel: 'Riverbank', label: 'River / Riverbank', dirFactor: 0.50, difFactor: 0.60, globFactor: 0.55, taOffset: -2.5, note: 'Shaded riverbank estimate - canopy and valley walls reduce direct sun, evaporative cooling from water surface lowers apparent air temp' },
|
river: { shortLabel: 'Riverbank', label: 'River / Riverbank', dirFactor: 0.50, difFactor: 0.60, globFactor: 0.55, taOffset: -2.5, note: 'Shaded riverbank estimate - canopy and valley walls reduce direct sun, evaporative cooling from water surface lowers apparent air temp', shade: { offset: -1.5, shelter: 0.55, sun: 0.40, type: 'trees & bushes' } },
|
||||||
forest: { shortLabel: 'Forest', label: 'Forest / Woodland', dirFactor: 0.15, difFactor: 0.40, globFactor: 0.20, taOffset: -2.0, note: 'Canopy-shaded estimate - direct radiation reduced 85%, air temp adjusted for evapotranspiration cooling' },
|
forest: { shortLabel: 'Forest', label: 'Forest / Woodland', dirFactor: 0.15, difFactor: 0.40, globFactor: 0.20, taOffset: -2.0, note: 'Canopy-shaded estimate - direct radiation reduced 85%, air temp adjusted for evapotranspiration cooling', shade: { offset: -1.0, shelter: 0.80, sun: 0.20, type: 'tree canopy' } },
|
||||||
openwater: { shortLabel: 'Open Water', label: 'Open Water', dirFactor: 1.14, difFactor: 1.08, globFactor: 1.12, taOffset: 0.0, note: 'Open water - full solar exposure with additional radiation reflected from the water surface, no shade or shelter, no ground heat contribution' },
|
openwater: { shortLabel: 'Open Water', label: 'Open Water', dirFactor: 1.14, difFactor: 1.08, globFactor: 1.12, taOffset: 0.0, note: 'Open water - full solar exposure with additional radiation reflected from the water surface, no shade or shelter, no ground heat contribution', shade: { offset: 0.0, shelter: 0.10, sun: 0.25, type: 'boat panels' } },
|
||||||
alpine: { shortLabel: 'Alpine', label: 'High Altitude / Alpine', dirFactor: 1.15, difFactor: 1.10, globFactor: 1.12, taOffset: -1.0, note: 'Alpine estimate assuming approx. 2000 m elevation - radiation enhanced ~15% through thinner atmosphere, adjusted for exposed ridge conditions' },
|
alpine: { shortLabel: 'Alpine', label: 'High Altitude / Alpine', dirFactor: 1.15, difFactor: 1.10, globFactor: 1.12, taOffset: -1.0, note: 'Alpine estimate assuming approx. 2000 m elevation - radiation enhanced ~15% through thinner atmosphere, adjusted for exposed ridge conditions', shade: { offset: -0.5, shelter: 0.35, sun: 0.40, type: 'trees & rocks' } },
|
||||||
desert: { shortLabel: 'Desert', label: 'Desert / Arid', dirFactor: 1.22, difFactor: 1.10, globFactor: 1.18, taOffset: 3.5, note: 'Extreme radiation load from high albedo sand and bare ground - ground-level heat significantly raises effective felt temperature' },
|
desert: { shortLabel: 'Desert', label: 'Desert / Arid', dirFactor: 1.22, difFactor: 1.10, globFactor: 1.18, taOffset: 3.5, note: 'Extreme radiation load from high albedo sand and bare ground - ground-level heat significantly raises effective felt temperature', shade: { offset: 2.5, shelter: 0.40, sun: 1.00, type: 'cactus / rockface' } },
|
||||||
};
|
};
|
||||||
|
|
||||||
// Maps OUTDOORS_VARIANTS keys to a default UTCI_ENVIRONMENTS key.
|
// Maps OUTDOORS_VARIANTS keys to a default UTCI_ENVIRONMENTS key.
|
||||||
@@ -203,6 +209,7 @@ export const workVariantKeys = ['farming', 'construction', 'market', 'windowclea
|
|||||||
export const COL_DESCRIPTIONS = {
|
export const COL_DESCRIPTIONS = {
|
||||||
hour: { title: 'Hour', desc: 'Local wall-clock time for this forecast row. Each row covers one hour.' },
|
hour: { title: 'Hour', desc: 'Local wall-clock time for this forecast row. Each row covers one hour.' },
|
||||||
air: { title: 'Air Temperature', desc: 'Measured air temperature at 2 m above the ground. This is the standard thermometer reading — it does not account for sun, wind, or humidity.' },
|
air: { title: 'Air Temperature', desc: 'Measured air temperature at 2 m above the ground. This is the standard thermometer reading — it does not account for sun, wind, or humidity.' },
|
||||||
|
shadeT: { title: 'Shade Air', desc: 'Estimated air temperature sitting in the typical shade of the selected Solar Model — building shadow, beach umbrella, tree canopy, boat panels, and so on. Sheltered, sun-warmed surroundings push it above the official open-ground Air figure on calm sunny hours; wind and cloud pull it back toward it. This is an estimate, not a measurement, and differs from the standardised 2 m Air reading.' },
|
||||||
rh: { title: 'Relative Humidity', desc: 'How much moisture the air holds relative to its maximum capacity at that temperature. High RH makes warm days feel stickier and cold days feel rawer.' },
|
rh: { title: 'Relative Humidity', desc: 'How much moisture the air holds relative to its maximum capacity at that temperature. High RH makes warm days feel stickier and cold days feel rawer.' },
|
||||||
dew: { title: 'Dew Point', desc: 'The temperature at which air becomes saturated and moisture begins to condense. A useful measure of absolute humidity — above 16 °C it starts to feel muggy; above 21 °C it feels oppressive.' },
|
dew: { title: 'Dew Point', desc: 'The temperature at which air becomes saturated and moisture begins to condense. A useful measure of absolute humidity — above 16 °C it starts to feel muggy; above 21 °C it feels oppressive.' },
|
||||||
wind: { title: 'Wind Speed', desc: 'Mean wind speed at 10 m in mph, with peak gust in brackets where significantly higher. Gust colour indicates Beaufort scale severity: amber = Near Gale (32+ mph), orange = Gale (39+ mph), red = Severe Gale or above (55+ mph). Banners above the table classify conditions from Strong Breeze through to Violent Storm using standard wind categories. Wind dramatically increases heat loss from exposed skin — the basis of wind chill.' },
|
wind: { title: 'Wind Speed', desc: 'Mean wind speed at 10 m in mph, with peak gust in brackets where significantly higher. Gust colour indicates Beaufort scale severity: amber = Near Gale (32+ mph), orange = Gale (39+ mph), red = Severe Gale or above (55+ mph). Banners above the table classify conditions from Strong Breeze through to Violent Storm using standard wind categories. Wind dramatically increases heat loss from exposed skin — the basis of wind chill.' },
|
||||||
|
|||||||
+38
-36
@@ -53,8 +53,9 @@ const COLS = [
|
|||||||
['UTCI', '°C', 'right', 7, '0.0'],
|
['UTCI', '°C', 'right', 7, '0.0'],
|
||||||
['Δ UTCI−Air', '°C', 'right', 9, '0.0'],
|
['Δ UTCI−Air', '°C', 'right', 9, '0.0'],
|
||||||
['Tmrt', '°C', 'right', 7, '0.0'],
|
['Tmrt', '°C', 'right', 7, '0.0'],
|
||||||
// Air (3)
|
// Air (4)
|
||||||
['Air Temp', '°C', 'right', 8, '0.0'],
|
['Air Temp', '°C', 'right', 8, '0.0'],
|
||||||
|
['Shade Air', '°C', 'right', 9, '0.0'],
|
||||||
['Dew Point', '°C', 'right', 9, '0.0'],
|
['Dew Point', '°C', 'right', 9, '0.0'],
|
||||||
['Humidity', '%', 'right', 8, '0' ],
|
['Humidity', '%', 'right', 8, '0' ],
|
||||||
// UV & Solar (7)
|
// UV & Solar (7)
|
||||||
@@ -95,13 +96,13 @@ const COLS = [
|
|||||||
['Ragweed', 'g/m³', 'right', 10, '0' ],
|
['Ragweed', 'g/m³', 'right', 10, '0' ],
|
||||||
];
|
];
|
||||||
|
|
||||||
const TOTAL_COLS = COLS.length; // 41
|
const TOTAL_COLS = COLS.length; // 42
|
||||||
|
|
||||||
// Groups: [label, span, bgKey]
|
// Groups: [label, span, bgKey]
|
||||||
const GROUPS = [
|
const GROUPS = [
|
||||||
['Time & Sun', 2, 'timeSun'],
|
['Time & Sun', 2, 'timeSun'],
|
||||||
['Felt Temperature', 5, 'felt' ],
|
['Felt Temperature', 5, 'felt' ],
|
||||||
['Air', 3, 'air' ],
|
['Air', 4, 'air' ],
|
||||||
['UV & Solar', 7, 'uvSolar'],
|
['UV & Solar', 7, 'uvSolar'],
|
||||||
['Surfaces', 6, 'surf' ],
|
['Surfaces', 6, 'surf' ],
|
||||||
['Wind', 3, 'wind' ],
|
['Wind', 3, 'wind' ],
|
||||||
@@ -214,53 +215,54 @@ function buildForecastSheet(X, rows, locationName, dateLabel, skinType) {
|
|||||||
|
|
||||||
// Air
|
// 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, 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, 8, r.shadeT, { bg: tempBg(r.shadeT), fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
|
||||||
setNum(X, ws, rowIdx, 9, r.RH, { ...def(), numFmt: '0' });
|
setNum(X, ws, rowIdx, 9, r.dew, { ...def(), numFmt: '0.0' });
|
||||||
|
setNum(X, ws, rowIdx, 10, r.RH, { ...def(), numFmt: '0' });
|
||||||
|
|
||||||
// UV & Solar
|
// UV & Solar
|
||||||
const uvFg = r.uv >= 8 ? P.alert : r.uv >= 3 ? P.warn : P.warmDark;
|
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.uv, { ...def(), fg: uvFg, numFmt: '0.0' });
|
||||||
setNum(X, ws, rowIdx, 11, r.uvA, { ...def(), numFmt: '0.0' });
|
setNum(X, ws, rowIdx, 12, r.uvA, { ...def(), numFmt: '0.0' });
|
||||||
setNum(X, ws, rowIdx, 12, r.uvB, { ...def(), numFmt: '0.00' });
|
setNum(X, ws, rowIdx, 13, r.uvB, { ...def(), numFmt: '0.00' });
|
||||||
const burnMins = sunburnMinutes(r.uv, skinType);
|
const burnMins = sunburnMinutes(r.uv, skinType);
|
||||||
const burnStr = r.uv > 0 ? burnLabel(burnMins) : '—';
|
const burnStr = r.uv > 0 ? burnLabel(burnMins) : '—';
|
||||||
const burnFg = r.uv > 0 ? (burnMins < 30 ? P.alert : P.warn) : P.muted;
|
const burnFg = r.uv > 0 ? (burnMins < 30 ? P.alert : P.warn) : P.muted;
|
||||||
setStr(X, ws, rowIdx, 13, burnStr, { ...def(), fg: burnFg, align: 'right' });
|
setStr(X, ws, rowIdx, 14, burnStr, { ...def(), fg: burnFg, align: 'right' });
|
||||||
setNum(X, ws, rowIdx, 14, r.dir, { ...def(), numFmt: '0' });
|
setNum(X, ws, rowIdx, 15, r.dir, { ...def(), numFmt: '0' });
|
||||||
setNum(X, ws, rowIdx, 15, r.dif, { ...def(), numFmt: '0' });
|
setNum(X, ws, rowIdx, 16, r.dif, { ...def(), numFmt: '0' });
|
||||||
setNum(X, ws, rowIdx, 16, r.glob, { ...def(), numFmt: '0' });
|
setNum(X, ws, rowIdx, 17, r.glob, { ...def(), numFmt: '0' });
|
||||||
|
|
||||||
// Surfaces
|
// 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.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.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.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.soilT0, { ...def(), numFmt: '0.0' });
|
||||||
setNum(X, ws, rowIdx, 21, r.soilT6, { ...def(), numFmt: '0.0' });
|
setNum(X, ws, rowIdx, 22, 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, 23, r.soilM != null ? Math.round(r.soilM * 100) : null, { ...def(), numFmt: '0' });
|
||||||
|
|
||||||
// Wind
|
// Wind
|
||||||
setNum(X, ws, rowIdx, 23, r.va, { ...def(), numFmt: '0.0' });
|
setNum(X, ws, rowIdx, 24, r.va, { ...def(), numFmt: '0.0' });
|
||||||
setNum(X, ws, rowIdx, 24, r.gust, { ...def(), numFmt: '0.0' });
|
setNum(X, ws, rowIdx, 25, r.gust, { ...def(), numFmt: '0.0' });
|
||||||
setStr(X, ws, rowIdx, 25, r.compass?.label ?? (r.wd != null ? `${Math.round(r.wd)}°` : '—'), { ...def(), align: 'center' });
|
setStr(X, ws, rowIdx, 26, r.compass?.label ?? (r.wd != null ? `${Math.round(r.wd)}°` : '—'), { ...def(), align: 'center' });
|
||||||
|
|
||||||
// Sky & Precip
|
// Sky & Precip
|
||||||
setNum(X, ws, rowIdx, 26, r.cc, { ...def(), numFmt: '0' });
|
setNum(X, ws, rowIdx, 27, r.cc, { ...def(), numFmt: '0' });
|
||||||
setNum(X, ws, rowIdx, 27, r.ccLow, { ...def(), numFmt: '0' });
|
setNum(X, ws, rowIdx, 28, r.ccLow, { ...def(), numFmt: '0' });
|
||||||
setNum(X, ws, rowIdx, 28, r.ccMid, { ...def(), numFmt: '0' });
|
setNum(X, ws, rowIdx, 29, r.ccMid, { ...def(), numFmt: '0' });
|
||||||
setNum(X, ws, rowIdx, 29, r.ccHigh, { ...def(), numFmt: '0' });
|
setNum(X, ws, rowIdx, 30, 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.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.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.snow > 0 ? r.snow : null, { ...def(), numFmt: '0.00' });
|
||||||
setNum(X, ws, rowIdx, 33, r.visKm, { ...def(), numFmt: '0.0' });
|
setNum(X, ws, rowIdx, 34, r.visKm, { ...def(), numFmt: '0.0' });
|
||||||
|
|
||||||
// Air Quality
|
// 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.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.grassPollen, { ...def(), numFmt: '0' });
|
||||||
setNum(X, ws, rowIdx, 36, r.birchPollen, { ...def(), numFmt: '0' });
|
setNum(X, ws, rowIdx, 37, r.birchPollen, { ...def(), numFmt: '0' });
|
||||||
setNum(X, ws, rowIdx, 37, r.alderPollen, { ...def(), numFmt: '0' });
|
setNum(X, ws, rowIdx, 38, r.alderPollen, { ...def(), numFmt: '0' });
|
||||||
setNum(X, ws, rowIdx, 38, r.mugwortPollen, { ...def(), numFmt: '0' });
|
setNum(X, ws, rowIdx, 39, r.mugwortPollen, { ...def(), numFmt: '0' });
|
||||||
setNum(X, ws, rowIdx, 39, r.olivePollen, { ...def(), numFmt: '0' });
|
setNum(X, ws, rowIdx, 40, r.olivePollen, { ...def(), numFmt: '0' });
|
||||||
setNum(X, ws, rowIdx, 40, r.ragweedPollen, { ...def(), numFmt: '0' });
|
setNum(X, ws, rowIdx, 41, r.ragweedPollen, { ...def(), numFmt: '0' });
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Sheet metadata ────────────────────────────────────────────────────
|
// ── Sheet metadata ────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
// calcIndoorTempPass(TaArr, globArr, elevArr, buildingType) passive indoor temp
|
// calcIndoorTempPass(TaArr, globArr, elevArr, buildingType) passive indoor temp
|
||||||
// calcManagedIndoorTempPass(TaArr, globArr, elevArr, buildingType) managed indoor temp
|
// calcManagedIndoorTempPass(TaArr, globArr, elevArr, buildingType) managed indoor temp
|
||||||
// calcVehicleInteriorTemp(Ta, globalRad, solElev, vehicleType, ventilated, speedMph)
|
// calcVehicleInteriorTemp(Ta, globalRad, solElev, vehicleType, ventilated, speedMph)
|
||||||
|
// calcShadeAirTemp(Ta, effectiveRad, va, elev, shade) per-env shade air temp
|
||||||
// vaporPressureHpa(Ta, RH) Magnus formula - hPa
|
// vaporPressureHpa(Ta, RH) Magnus formula - hPa
|
||||||
// solarElevationDeg(lat, lon, dateUTC) NOAA simplified solar position (-)
|
// solarElevationDeg(lat, lon, dateUTC) NOAA simplified solar position (-)
|
||||||
// calcTmrt(Ta, dirRad, diffRad, globalRad, solElev) Mean radiant temperature
|
// calcTmrt(Ta, dirRad, diffRad, globalRad, solElev) Mean radiant temperature
|
||||||
@@ -469,6 +470,53 @@ export function calcVehicleInteriorTemp(Ta, globalRad, solElev, vehicleType = 'c
|
|||||||
return Math.max(Ta, Math.min(Ti, 90));
|
return Math.max(Ta, Math.min(Ti, 90));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// SHADE AIR TEMPERATURE (per-environment microclimate)
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Estimates the AIR temperature you'd actually experience sitting in
|
||||||
|
// the typical shade of the selected Solar Model - NOT the felt temp.
|
||||||
|
//
|
||||||
|
// The official Air column is the standardised open-ground, 2 m,
|
||||||
|
// shaded-screen value for a ~2 km model grid cell. A real sitting
|
||||||
|
// spot is a sheltered sun-trap: surrounding surfaces (walls, sand,
|
||||||
|
// rock) re-radiate heat into the still air pocket, while wind mixing
|
||||||
|
// pulls the pocket back toward the official reading. The kind of
|
||||||
|
// shelter and the heat of the surroundings differ by environment,
|
||||||
|
// which is what the per-env `shade` block captures.
|
||||||
|
//
|
||||||
|
// IMPORTANT: this is an air-temperature nudge, deliberately gentle.
|
||||||
|
// The radiant load of direct sun is already handled by Tmrt/SunSoak,
|
||||||
|
// so we do NOT re-add it here - that would double-count the sun.
|
||||||
|
//
|
||||||
|
// shade.offset - base air-temp delta for this environment's shade (-C).
|
||||||
|
// Warmer for urban/desert (heat island, baking ground),
|
||||||
|
// cooler for river/forest (evaporative, canopy shade).
|
||||||
|
// shade.shelter - 0-1 wind shelter. High = enclosed (canyon, canopy),
|
||||||
|
// so wind mixing has little effect. Low = breezy
|
||||||
|
// (open water, single beach umbrella).
|
||||||
|
// shade.sun - 0-~1.2 surface solar gain. How sun-baked the
|
||||||
|
// surroundings are: hot concrete/sand high, shaded
|
||||||
|
// forest floor / cool water low. Drives how much the
|
||||||
|
// still air warms on a sunny hour.
|
||||||
|
//
|
||||||
|
// Behaviour: a calm sunny hour in a sheltered, sun-baked environment
|
||||||
|
// reads a degree or two above official Air; a windy or overcast hour
|
||||||
|
// collapses back toward it.
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
export const SHADE_K_SUN = 1.5; // surface-warming strength (per full-sun, fully sun-baked)
|
||||||
|
export const SHADE_K_WIND = 0.15; // wind-mixing strength (per m/s, fully exposed)
|
||||||
|
|
||||||
|
export function calcShadeAirTemp(Ta, effectiveRad, va, elev, shade) {
|
||||||
|
if (Ta == null || !shade) return Ta ?? null;
|
||||||
|
const rad = Math.max(0, Math.min(1, (effectiveRad || 0) / 800)); // 0-1, saturates ~800 W/m-
|
||||||
|
const daytime = (elev != null && elev > 0) ? 1 : 0;
|
||||||
|
const sunTerm = SHADE_K_SUN * (shade.sun ?? 0) * rad * daytime;
|
||||||
|
const windTerm = SHADE_K_WIND * (1 - (shade.shelter ?? 0)) * Math.max(0, va || 0);
|
||||||
|
const adj = (shade.offset ?? 0) + sunTerm - windTerm;
|
||||||
|
// Keep the nudge sane - this is air temp, not a felt-temp delta.
|
||||||
|
return Ta + Math.max(-5, Math.min(6, adj));
|
||||||
|
}
|
||||||
|
|
||||||
// -- FUTURE FEATURE v2: Pets Profile - Fur Temperature & Heat Stress ----------
|
// -- FUTURE FEATURE v2: Pets Profile - Fur Temperature & Heat Stress ----------
|
||||||
// Dogs and cats experience heat very differently from humans:
|
// Dogs and cats experience heat very differently from humans:
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user