Correct 'Shade' temps for night
This commit is contained in:
fraxle
2026-08-02 17:38:59 +01:00
parent ff86b6d5ca
commit ffb5ed1d7d
8 changed files with 91 additions and 30 deletions
+1 -1
View File
@@ -146,7 +146,7 @@
<div class="profile-block"> <div class="profile-block">
<p class="profile-name"><a href="./columns.html">📊 Column reference →</a></p> <p class="profile-name"><a href="./columns.html">📊 Column reference →</a></p>
<p> <p>
Every column in the forecast table explained in plain language, from Air and Shade Air through Every column in the forecast table explained in plain language, from Air and Shade through
to Lightning Potential, air quality, pollen, and SunSoak. to Lightning Potential, air quality, pollen, and SunSoak.
</p> </p>
</div> </div>
+7 -4
View File
@@ -379,9 +379,9 @@ export function UTCIForecast() {
// Columns that mark the start of a logical group - used to draw a faint // Columns that mark the start of a logical group - used to draw a faint
// vertical border separating groups in the forecast table. // vertical border separating groups in the forecast table.
const GROUP_ORDER = { const GROUP_ORDER = {
felt: ['utciP', 'vehicleT', 'indoorT', 'managedT', 'petHomeT', 'burn', 'utci', 'delta', 'tmrt'], felt: ['utciP', 'shadeT', 'vehicleT', 'indoorT', 'managedT', 'petHomeT', 'burn', 'utci', 'delta', 'tmrt'],
surface: ['concreteT', 'soilT', 'soilT6', 'soilM', 'furSurfaceT', 'pawT'], surface: ['concreteT', 'soilT', 'soilT6', 'soilM', 'furSurfaceT', 'pawT'],
ambient: ['shadeT', 'petShadeT', 'air', 'rh', 'dew'], ambient: ['petShadeT', 'air', 'rh', 'dew'],
precip: ['precip', 'precipProb', 'lightning'], precip: ['precip', 'precipProb', 'lightning'],
sky: ['cloud', 'vis'], sky: ['cloud', 'vis'],
wind: ['wind', 'dir'], wind: ['wind', 'dir'],
@@ -1351,6 +1351,7 @@ export function UTCIForecast() {
</span> </span>
</th> </th>
${visibleCols.utciP && html`<th class=${`col-info-th ${groupStart('utciP')} ${groupColor('utciP')}`} scope="col" onClick=${(e) => handleThClick('utciP', e)} onMouseEnter=${(e) => handleThEnter('utciP', e)} onMouseLeave=${handleThLeave}>SunSoak <span class="col-unit">°C felt</span>${UTCI_ENVIRONMENTS[utciEnv]?.shortLabel ? html`<span class="col-env-badge">${UTCI_ENVIRONMENTS[utciEnv].shortLabel}</span>` : null}</th>`} ${visibleCols.utciP && html`<th class=${`col-info-th ${groupStart('utciP')} ${groupColor('utciP')}`} scope="col" onClick=${(e) => handleThClick('utciP', e)} onMouseEnter=${(e) => handleThEnter('utciP', e)} onMouseLeave=${handleThLeave}>SunSoak <span class="col-unit">°C felt</span>${UTCI_ENVIRONMENTS[utciEnv]?.shortLabel ? html`<span class="col-env-badge">${UTCI_ENVIRONMENTS[utciEnv].shortLabel}</span>` : null}</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 felt</span></th>`}
${visibleCols.vehicleT && html`<th class=${`col-info-th ${groupStart('vehicleT')} ${groupColor('vehicleT')}`} scope="col" onClick=${(e) => handleThClick('vehicleT', e)} onMouseEnter=${(e) => handleThEnter('vehicleT', e)} onMouseLeave=${handleThLeave}>Vehicle <span class="col-unit">°C peak</span></th>`} ${visibleCols.vehicleT && html`<th class=${`col-info-th ${groupStart('vehicleT')} ${groupColor('vehicleT')}`} scope="col" onClick=${(e) => handleThClick('vehicleT', e)} onMouseEnter=${(e) => handleThEnter('vehicleT', e)} onMouseLeave=${handleThLeave}>Vehicle <span class="col-unit">°C peak</span></th>`}
${indoorMode === 'on' && !indoorManaged && html`<th class=${`col-info-th ${groupStart('indoorT')} ${groupColor('indoorT')}`} scope="col" onClick=${(e) => handleThClick('indoorT', e)} onMouseEnter=${(e) => handleThEnter('indoorT', e)} onMouseLeave=${handleThLeave}>Indoors <span class="col-unit">°C est.</span></th>`} ${indoorMode === 'on' && !indoorManaged && html`<th class=${`col-info-th ${groupStart('indoorT')} ${groupColor('indoorT')}`} scope="col" onClick=${(e) => handleThClick('indoorT', e)} onMouseEnter=${(e) => handleThEnter('indoorT', e)} onMouseLeave=${handleThLeave}>Indoors <span class="col-unit">°C est.</span></th>`}
${indoorMode === 'on' && indoorManaged && html`<th class=${`col-info-th ${groupStart('managedT')} ${groupColor('managedT')}`} scope="col" onClick=${(e) => handleThClick('managedT', e)} onMouseEnter=${(e) => handleThEnter('managedT', e)} onMouseLeave=${handleThLeave}>Managed <span class="col-unit">°C est.</span></th>`} ${indoorMode === 'on' && indoorManaged && html`<th class=${`col-info-th ${groupStart('managedT')} ${groupColor('managedT')}`} scope="col" onClick=${(e) => handleThClick('managedT', e)} onMouseEnter=${(e) => handleThEnter('managedT', e)} onMouseLeave=${handleThLeave}>Managed <span class="col-unit">°C est.</span></th>`}
@@ -1365,7 +1366,6 @@ 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.petShadeT && html`<th class=${`utci-tight-head col-info-th ${groupStart('petShadeT')} ${groupColor('petShadeT')}`} scope="col" onClick=${(e) => handleThClick('petShadeT', e)} onMouseEnter=${(e) => handleThEnter('petShadeT', e)} onMouseLeave=${handleThLeave}>Pet Shade <span class="col-unit">°C</span></th>`} ${visibleCols.petShadeT && html`<th class=${`utci-tight-head col-info-th ${groupStart('petShadeT')} ${groupColor('petShadeT')}`} scope="col" onClick=${(e) => handleThClick('petShadeT', e)} onMouseEnter=${(e) => handleThEnter('petShadeT', e)} onMouseLeave=${handleThLeave}>Pet 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>`}
@@ -1521,6 +1521,10 @@ export function UTCIForecast() {
<td class=${groupStart('utciP')} style=${{ background: airTempBg(r.utciAdj, rPrev?.utciAdj, rNext?.utciAdj), color: airTempFontColor(), fontSize: '13px' }}> <td class=${groupStart('utciP')} style=${{ background: airTempBg(r.utciAdj, rPrev?.utciAdj, rNext?.utciAdj), color: airTempFontColor(), fontSize: '13px' }}>
${fmt(r.utciAdj)}${u('°C')} ${fmt(r.utciAdj)}${u('°C')}
</td>`} </td>`}
${visibleCols.shadeT && html`
<td class=${groupStart('shadeT')} style=${{ color: airTempFontColor(), background: airTempBg(r.shadeT, rPrev?.shadeT, rNext?.shadeT) }}>
${r.shadeT != null ? fmt(r.shadeT) + u('°C') : '—'}
</td>`}
${visibleCols.vehicleT && html` ${visibleCols.vehicleT && html`
<td class=${groupStart('vehicleT')} style=${{ color: airTempFontColor(), background: airTempBg(r.vehicleT, rPrev?.vehicleT, rNext?.vehicleT) }}> <td class=${groupStart('vehicleT')} style=${{ color: airTempFontColor(), background: airTempBg(r.vehicleT, rPrev?.vehicleT, rNext?.vehicleT) }}>
${fmt(r.vehicleT)}${u('°C')} ${fmt(r.vehicleT)}${u('°C')}
@@ -1571,7 +1575,6 @@ 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.petShadeT && html` ${visibleCols.petShadeT && html`
<td class=${groupStart('petShadeT')} title=${r.petShadeT != null ? (petCategory(r.petShadeT)?.label ?? '') : ''} style=${{ color: airTempFontColor(), background: petAirTempBg(r.petShadeT, rPrev?.petShadeT, rNext?.petShadeT) }}> <td class=${groupStart('petShadeT')} title=${r.petShadeT != null ? (petCategory(r.petShadeT)?.label ?? '') : ''} style=${{ color: airTempFontColor(), background: petAirTempBg(r.petShadeT, rPrev?.petShadeT, rNext?.petShadeT) }}>
${r.petShadeT != null ? fmt(r.petShadeT) + u('°C') : '—'} ${r.petShadeT != null ? fmt(r.petShadeT) + u('°C') : '—'}
+20 -12
View File
@@ -14,7 +14,7 @@
import { import {
vaporPressureHpa, solarElevationDeg, calcTmrt, utciApprox, vaporPressureHpa, solarElevationDeg, calcTmrt, utciApprox,
calcConcreteTempPass, calcVehicleInteriorTempPass, calcConcreteTempPass, calcVehicleInteriorTempPass,
calcIndoorTempPass, calcManagedIndoorTempPass, calcShadeAirTemp, calcIndoorTempPass, calcManagedIndoorTempPass, calcShadeAirTemp, calcShadeFeltTemp,
calcFurSurfaceTempPass, calcFurSurfaceTempPass,
} from './physics.js'; } from './physics.js';
import { windCompass8, uvSplit, cloudCategory, precipPenalty, sunburnMinutes, burnLabel, FUR_COLORS } from './utils.js'; import { windCompass8, uvSplit, cloudCategory, precipPenalty, sunburnMinutes, burnLabel, FUR_COLORS } from './utils.js';
@@ -84,15 +84,22 @@ export function buildHourlyRows({ forecast, airQuality, location, vehicleType, v
const difEnv = dif * env.difFactor; const difEnv = dif * env.difFactor;
const globEnv = glob * env.globFactor; const globEnv = glob * env.globFactor;
// 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...). Shares SunSoak's TaEnv
// baseline and env-reduced radiation, so the two columns stay consistent.
const shadeT = calcShadeAirTemp(TaEnv, dirEnv + difEnv * 0.2, va, elev, env.shade);
// vehicleT is stamped in the two-pass section below (thermal lag). // vehicleT is stamped in the two-pass section below (thermal lag).
const eh = vaporPressureHpa(Ta, RH); const eh = vaporPressureHpa(Ta, RH);
const Tmrt = calcTmrt(TaEnv, dirEnv, difEnv, globEnv, elev); const Tmrt = calcTmrt(TaEnv, dirEnv, difEnv, globEnv, elev);
const utci = utciApprox(TaEnv, Tmrt, va, eh); const utci = utciApprox(TaEnv, Tmrt, va, eh);
const utciAdj = utci + precipPenalty(precip, snow, va); const utciAdj = utci + precipPenalty(precip, snow, va);
// Shade air temp: what a thermometer in this Solar Model's typical shade
// (building shadow, beach umbrella, canopy...) would read. Shares
// SunSoak's TaEnv baseline and env-reduced radiation, so the two stay
// consistent. Pet Shade reads this directly; the human Shade column
// below turns it into a felt temperature first.
const shadeAirT = calcShadeAirTemp(TaEnv, dirEnv + difEnv * 0.2, va, elev, env.shade);
// Shade (felt): SunSoak with the direct beam taken away — still outdoors,
// so the same wind, humidity and rain penalty apply. See
// calcShadeFeltTemp() for why this is a felt number and not the air temp.
const shadeT = calcShadeFeltTemp(shadeAirT, difEnv, va, eh, elev, env.shade)
+ precipPenalty(precip, snow, va);
// Derived // Derived
const compass = windCompass8(wd); const compass = windCompass8(wd);
const { uvA, uvB } = uvSplit(uv, elev); const { uvA, uvB } = uvSplit(uv, elev);
@@ -154,10 +161,11 @@ export function buildHourlyRows({ forecast, airQuality, location, vehicleType, v
uv, uvA, uvB, uv, uvA, uvB,
precip, precipProb, lightning, cape, snow, precip, precipProb, lightning, cape, snow,
soilT0, soilT6, soilM, shadeT, soilT0, soilT6, soilM, shadeT,
// Pet Shade reads the same hourly value as Shade but aggregates as a // Pet Shade stays on the shade AIR temperature: the human Shade column
// MEAN rather than a worst case, so it needs its own field to diverge // is a UTCI felt number on a human comfort scale, which says nothing
// from shadeT at 2h/3h/4h. Same for pawT vs concreteT below. // useful about a cat. It also aggregates as a MEAN rather than a worst
petShadeT: shadeT, // case, so it needs its own field either way (same for pawT/concreteT).
petShadeT: shadeAirT,
effectiveRad, effectiveRad,
elev, Tmrt, utci, utciAdj, eh, compass, elev, Tmrt, utci, utciAdj, eh, compass,
visKm, aqi, visKm, aqi,
@@ -723,7 +731,7 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
// ── Pets (cats / small dogs) ──────────────────────────────────────────── // ── Pets (cats / small dogs) ────────────────────────────────────────────
if (profile === 'pets') { if (profile === 'pets') {
const peakFur = peakRow('furSurfaceT'); const peakFur = peakRow('furSurfaceT');
const peakShade = peakRow('shadeT'); const peakShade = peakRow('petShadeT');
const peakIndoor = peakRow('indoorT'); const peakIndoor = peakRow('indoorT');
const maxPollen = Math.max(0, ...todayRows.map(r => r.grassPollen ?? 0)); const maxPollen = Math.max(0, ...todayRows.map(r => r.grassPollen ?? 0));
const peakAqi = Math.max(0, ...todayRows.map(r => r.aqi ?? 0)); const peakAqi = Math.max(0, ...todayRows.map(r => r.aqi ?? 0));
@@ -740,8 +748,8 @@ export function computeGlanceSummary(todayRows, profile, variant, skinType, cols
...(show('petShadeT') ? [{ ...(show('petShadeT') ? [{
icon: '🌳', icon: '🌳',
label: 'Shade for pet', label: 'Shade for pet',
value: peakShade ? `${Math.round(peakShade.shadeT)}° at ${hhmm(peakShade.iso)}` : '-', value: peakShade ? `${Math.round(peakShade.petShadeT)}° at ${hhmm(peakShade.iso)}` : '-',
alert: !!(peakShade && peakShade.shadeT >= 26), alert: !!(peakShade && peakShade.petShadeT >= 26),
grp: 'ambient', grp: 'ambient',
}] : []), }] : []),
...(show('petHomeT') ? [{ ...(show('petHomeT') ? [{
+8 -5
View File
@@ -143,8 +143,9 @@ 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 // shade - per-environment microclimate inputs for the Shade column
// column (see calcShadeAirTemp in physics.js). The env air-temp // (calcShadeAirTemp + calcShadeFeltTemp in physics.js, where
// shelter doubles as the sky-view proxy). The env air-temp
// shift is NOT here - it comes from taOffset via the shared // shift is NOT here - it comes from taOffset via the shared
// TaEnv baseline. This block only carries local sun-trap effects: // TaEnv baseline. This block only carries local sun-trap effects:
// shelter 0-1 wind shelter (high = enclosed, calm pocket) // shelter 0-1 wind shelter (high = enclosed, calm pocket)
@@ -195,7 +196,9 @@ export function deriveProfileMain(activeProfile, outdoorsVariant) {
const isPetsProfile = activeProfile === 'pets'; const isPetsProfile = activeProfile === 'pets';
const mainConfigKey = isHomeOrOffice ? 'indoor' : isVehicleProfile ? 'vehicle' : isPetsProfile ? 'fur' : 'solar'; const mainConfigKey = isHomeOrOffice ? 'indoor' : isVehicleProfile ? 'vehicle' : isPetsProfile ? 'fur' : 'solar';
const mainField = isHomeOrOffice ? 'indoorT' : isVehicleProfile ? 'vehicleT' : isPetsProfile ? 'furSurfaceT' : 'utciAdj'; const mainField = isHomeOrOffice ? 'indoorT' : isVehicleProfile ? 'vehicleT' : isPetsProfile ? 'furSurfaceT' : 'utciAdj';
const secondaryField = isHomeOrOffice ? 'managedT' : isPetsProfile ? 'shadeT' : isVehicleProfile ? null : 'shadeT'; // Pets pairs Fur Temp with PET Shade: the human Shade column is now a UTCI
// felt number on a human comfort scale, which says nothing about a cat.
const secondaryField = isHomeOrOffice ? 'managedT' : isPetsProfile ? 'petShadeT' : isVehicleProfile ? null : 'shadeT';
const mainLabel = activeProfile === 'outdoors' const mainLabel = activeProfile === 'outdoors'
? (OUTDOORS_VARIANTS[outdoorsVariant]?.name || FILTER_PROFILES.outdoors.label) ? (OUTDOORS_VARIANTS[outdoorsVariant]?.name || FILTER_PROFILES.outdoors.label)
: (FILTER_PROFILES[activeProfile]?.label || 'SunSoak'); : (FILTER_PROFILES[activeProfile]?.label || 'SunSoak');
@@ -260,7 +263,7 @@ export const workVariantKeys = ['farming', 'construction', 'market', 'windowclea
export const COL_DESCRIPTIONS = { export const COL_DESCRIPTIONS = {
hour: { title: 'Rows', desc: 'Local wall-clock time for this forecast row. Each row covers the interval set by the stepper in this header, clock-aligned from midnight — at 3h, a row labelled 09:00 spans 09:0011:59. Totals like rainfall are summed across the block, risk figures show the block\'s peak, and smooth readings like air temperature are averaged.' }, hour: { title: 'Rows', desc: 'Local wall-clock time for this forecast row. Each row covers the interval set by the stepper in this header, clock-aligned from midnight — at 3h, a row labelled 09:00 spans 09:0011:59. Totals like rainfall are summed across the block, risk figures show the block\'s peak, and smooth readings like air temperature are averaged.' },
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', 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.' }, shadeT: { title: 'Shade', desc: 'SunSoak with the sun taken away: the felt temperature standing in the typical shade of the selected Solar Model — building shadow, beach umbrella, tree canopy, boat panels, and so on. You are still outdoors, so wind, humidity and rain hit you exactly as they do in the open; only the direct beam and part of the sky are blocked. Expect several degrees below SunSoak in daytime sun, and near-identical readings after dark, when there is no sun to stand out of. An estimate of a typical shaded spot, not a measurement.' },
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.' },
@@ -291,7 +294,7 @@ export const COL_DESCRIPTIONS = {
pollen: { title: 'Pollen', desc: 'Pollen concentration in grains per cubic metre for the selected pollen type, sourced from the Copernicus CAMS pollen forecast. Low = <10; Moderate = 1050; High = 50200; Very High = 200+. Values vary by species and season.' }, pollen: { title: 'Pollen', desc: 'Pollen concentration in grains per cubic metre for the selected pollen type, sourced from the Copernicus CAMS pollen forecast. Low = <10; Moderate = 1050; High = 50200; Very High = 200+. Values vary by species and season.' },
furSurfaceT: { title: 'Fur Temp', desc: 'Estimated fur surface temperature a cat or small dog experiences in direct sun, calibrated for a thin/short coat rather than a heavily-insulated breed. Runs hotter than air temperature on sunny, still days — set fur colour with the selector alongside this column.' }, furSurfaceT: { title: 'Fur Temp', desc: 'Estimated fur surface temperature a cat or small dog experiences in direct sun, calibrated for a thin/short coat rather than a heavily-insulated breed. Runs hotter than air temperature on sunny, still days — set fur colour with the selector alongside this column.' },
pawT: { title: 'Paw Temp', desc: 'Estimated ground/pavement surface temperature for paw contact, colour-coded by burn risk: green below 40 °C (safe), amber 4052 °C (discomfort / possible burn on prolonged contact), red above 52 °C (can burn within 60 seconds). If you can\'t hold the back of your hand on the surface for 7 seconds, it\'s too hot to walk your pet.' }, pawT: { title: 'Paw Temp', desc: 'Estimated ground/pavement surface temperature for paw contact, colour-coded by burn risk: green below 40 °C (safe), amber 4052 °C (discomfort / possible burn on prolonged contact), red above 52 °C (can burn within 60 seconds). If you can\'t hold the back of your hand on the surface for 7 seconds, it\'s too hot to walk your pet.' },
petShadeT: { title: 'Pet Shade', desc: 'Shaded air temperature for a resting pet, using the same shade estimate as the Shade column but with a lower, pet-specific alert threshold — small pets have far less capacity to dump heat than humans.' }, petShadeT: { title: 'Pet Shade', desc: 'Shaded air temperature for a resting pet, using the same shade microclimate model as the Shade column but kept as an air temperature — a human felt-temperature index says little about an animal — and with a lower, pet-specific alert threshold — small pets have far less capacity to dump heat than humans.' },
petHomeT: { title: 'Pet Home', desc: 'Indoor temperature estimate for a pet left home alone, using the same model as the Indoors column but with a lower, pet-specific alert threshold (26 °C) rather than the human heatwave guidance threshold.' }, petHomeT: { title: 'Pet Home', desc: 'Indoor temperature estimate for a pet left home alone, using the same model as the Indoors column but with a lower, pet-specific alert threshold (26 °C) rather than the human heatwave guidance threshold.' },
}; };
+1 -1
View File
@@ -55,7 +55,7 @@ const COLS = [
['Tmrt', '°C', 'right', 7, '0.0'], ['Tmrt', '°C', 'right', 7, '0.0'],
// Air (4) // Air (4)
['Air Temp', '°C', 'right', 8, '0.0'], ['Air Temp', '°C', 'right', 8, '0.0'],
['Shade Air', '°C', 'right', 9, '0.0'], ['Shade Felt', '°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)
+49 -2
View File
@@ -14,6 +14,7 @@
// calcVehicleInteriorTemp(Ta, globalRad, solElev, vehicleType, ventilated, speedMph, windMps) equilibrium cabin temp // calcVehicleInteriorTemp(Ta, globalRad, solElev, vehicleType, ventilated, speedMph, windMps) equilibrium cabin temp
// calcVehicleInteriorTempPass(TaArr, globArr, elevArr, vaArr, vehicleType, ventilated, speedMph) lagged cabin temp // calcVehicleInteriorTempPass(TaArr, globArr, elevArr, vaArr, vehicleType, ventilated, speedMph) lagged cabin temp
// calcShadeAirTemp(TaEnv, effRadEnv, va, elev, shade) per-env shade air temp // calcShadeAirTemp(TaEnv, effRadEnv, va, elev, shade) per-env shade air temp
// calcShadeFeltTemp(TaShade, difEnv, va, eh, elev, shade) felt temp in that shade
// 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
@@ -601,9 +602,55 @@ export function calcShadeAirTemp(Ta, effectiveRad, va, elev, shade) {
const daytime = (elev != null && elev > 0) ? 1 : 0; const daytime = (elev != null && elev > 0) ? 1 : 0;
const sunTerm = SHADE_K_SUN * (shade.sun ?? 0) * rad * daytime; 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 windTerm = SHADE_K_WIND * (1 - (shade.shelter ?? 0)) * Math.max(0, va || 0);
const adj = sunTerm - windTerm; // Wind can only cancel the sun-trap warming, never push the pocket BELOW
// ambient: mixing drags trapped air back toward the open-air reading, it
// does not manufacture cold. Without the floor a breezy night read a
// degree under Air for no physical reason (and Pet Shade with it).
// Genuinely cool environments still sit below Air via env.taOffset.
const adj = Math.max(0, sunTerm - windTerm);
// Local sun-trap nudge only - the env air-temp shift is already in Ta. // Local sun-trap nudge only - the env air-temp shift is already in Ta.
return Ta + Math.max(-4, Math.min(5, adj)); return Ta + Math.min(5, adj);
}
// -------------------------------------------------------------------
// SHADE FELT TEMPERATURE - "SunSoak, but out of the sun"
// -------------------------------------------------------------------
// calcShadeAirTemp() above answers "what would a thermometer in the shade
// read". That is NOT what someone standing in the shade feels: they are
// still outdoors, so wind still strips heat off them and humidity still
// blunts their sweating, exactly as in the open. Reading a shade AIR temp
// beside SunSoak's FELT temp made Shade look hotter than full sun on a
// breezy night - both numbers were right, but they were different
// quantities sitting in adjacent columns.
//
// So Shade runs the same UTCI machinery as SunSoak, changing one input:
// the radiant load. Out of the beam a person sees no direct sun, only the
// part of the sky their shade does not block, so:
//
// direct -> 0 (that is what shade IS)
// diffuse -> difEnv * skyView
// global -> the same diffuse (no beam reaching the ground to reflect)
//
// skyView derives from shade.shelter, reusing it as an enclosure proxy: a
// forest canopy or building canyon that stops the wind also hides most of
// the sky dome, while an umbrella on open sand blocks little besides the
// sun itself. It is a proxy, not a measurement - like the rest of the
// shade block, this is an estimate of a typical spot in that environment.
//
// Consequences worth knowing: at night there is no beam to remove, so
// Shade converges on SunSoak (they differ only by the sun-trap nudge in
// the air temp). In daytime sun Shade sits several degrees under SunSoak,
// which is the whole point of standing in it. On a windy hour BOTH fall
// together, because both now carry the same wind term.
// -------------------------------------------------------------------
export const SHADE_SKY_BLOCK = 0.6; // fraction of the sky an enclosed shade hides, per unit shelter
export function calcShadeFeltTemp(TaShade, difRad, va, eh, elev, shade) {
if (TaShade == null) return null;
const skyView = 1 - SHADE_SKY_BLOCK * (shade?.shelter ?? 0);
const difShade = Math.max(0, (difRad || 0) * skyView);
const Tmrt = calcTmrt(TaShade, 0, difShade, difShade, elev);
return utciApprox(TaShade, Tmrt, va, eh);
} }
// -- Pets Profile - Fur Surface Temperature ----------------------------------- // -- Pets Profile - Fur Surface Temperature -----------------------------------
+3 -3
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Column reference · SunScope</title> <title>Column reference · SunScope</title>
<meta name="description" content="Every column in the SunScope forecast table explained in plain language — from Air and Shade Air through wind, UV, rain probability, lightning potential, air quality, pollen, and SunSoak." /> <meta name="description" content="Every column in the SunScope forecast table explained in plain language — from Air and Shade through wind, UV, rain probability, lightning potential, air quality, pollen, and SunSoak." />
<meta name="robots" content="index, follow" /> <meta name="robots" content="index, follow" />
<link rel="canonical" href="https://sunscope.net/columns.html" /> <link rel="canonical" href="https://sunscope.net/columns.html" />
@@ -86,7 +86,6 @@
</thead> </thead>
<tbody> <tbody>
<tr><td><strong>Air °C</strong></td><td>Standard air temperature at 2 m above ground — the thermometer reading. Does not account for sun, wind, or humidity.</td></tr> <tr><td><strong>Air °C</strong></td><td>Standard air temperature at 2 m above ground — the thermometer reading. Does not account for sun, wind, or humidity.</td></tr>
<tr><td><strong>Shade Air °C</strong></td><td>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. An estimate, not a measurement, and different from the standardised 2 m Air reading.</td></tr>
<tr><td><strong>RH %</strong></td><td>Relative humidity: how much moisture the air holds as a percentage of its maximum capacity at that temperature. High RH makes warm days feel stickier and cold days feel rawer.</td></tr> <tr><td><strong>RH %</strong></td><td>Relative humidity: how much moisture the air holds as a percentage of its maximum capacity at that temperature. High RH makes warm days feel stickier and cold days feel rawer.</td></tr>
<tr><td><strong>Dew Point</strong></td><td>The temperature at which air becomes saturated and moisture begins to condense. Above 16 °C it starts to feel muggy; above 21 °C it feels oppressive. A better measure of absolute humidity than RH.</td></tr> <tr><td><strong>Dew Point</strong></td><td>The temperature at which air becomes saturated and moisture begins to condense. Above 16 °C it starts to feel muggy; above 21 °C it feels oppressive. A better measure of absolute humidity than RH.</td></tr>
<tr><td><strong>Wind</strong></td><td>Mean wind speed at 10 m in mph, with peak gust in brackets where significantly higher. Gust colour follows the Beaufort scale: amber = Near Gale (32+ mph), orange = Gale (39+ mph), red = Severe Gale or above (55+ mph). Event alerts classify conditions from Strong Breeze through to Violent Storm. Wind is the primary driver of wind chill.</td></tr> <tr><td><strong>Wind</strong></td><td>Mean wind speed at 10 m in mph, with peak gust in brackets where significantly higher. Gust colour follows the Beaufort scale: amber = Near Gale (32+ mph), orange = Gale (39+ mph), red = Severe Gale or above (55+ mph). Event alerts classify conditions from Strong Breeze through to Violent Storm. Wind is the primary driver of wind chill.</td></tr>
@@ -102,6 +101,7 @@
<tr><td><strong>UV-B</strong></td><td>Estimated UV-B radiation index. Causes sunburn and drives vitamin D production. Intensity depends strongly on solar elevation and cloud cover, falling sharply at low sun angles.</td></tr> <tr><td><strong>UV-B</strong></td><td>Estimated UV-B radiation index. Causes sunburn and drives vitamin D production. Intensity depends strongly on solar elevation and cloud cover, falling sharply at low sun angles.</td></tr>
<tr><td><strong>Burn time</strong></td><td>Estimated time to reach one Minimal Erythemal Dose (MED) — the threshold for sunburn — based on the UV index and your selected Fitzpatrick skin type (chosen from a dropdown in the column bar). A guide, not a medical measurement.</td></tr> <tr><td><strong>Burn time</strong></td><td>Estimated time to reach one Minimal Erythemal Dose (MED) — the threshold for sunburn — based on the UV index and your selected Fitzpatrick skin type (chosen from a dropdown in the column bar). A guide, not a medical measurement.</td></tr>
<tr><td><strong>SunSoak °C</strong></td><td>SunScope's signature felt-temperature index. Starts from UTCI then adds three layers: a rain and snow penalty (wet clothing can drop felt temperature by up to 8 °C), a Solar Model for where you actually are (forest, alpine, beach, river, open water, desert), and full mean radiant temperature from surrounding surfaces. The most honest single answer to: what will it actually feel like out there?</td></tr> <tr><td><strong>SunSoak °C</strong></td><td>SunScope's signature felt-temperature index. Starts from UTCI then adds three layers: a rain and snow penalty (wet clothing can drop felt temperature by up to 8 °C), a Solar Model for where you actually are (forest, alpine, beach, river, open water, desert), and full mean radiant temperature from surrounding surfaces. The most honest single answer to: what will it actually feel like out there?</td></tr>
<tr><td><strong>Shade °C felt</strong></td><td>SunSoak with the sun taken away — the felt temperature standing in the typical shade of the selected Solar Model (building shadow, beach umbrella, tree canopy, boat panels, and so on). You are still outdoors, so wind, humidity and rain affect you exactly as they do in the open; only the direct beam and part of the sky are blocked. Expect several degrees below SunSoak in daytime sun, and near-identical readings after dark, when there is no sun to stand out of.</td></tr>
<tr><td><strong>Precip mm</strong></td><td>Expected rainfall in mm per hour; snowfall is shown in cm. Even light drizzle meaningfully reduces felt temperature when combined with wind.</td></tr> <tr><td><strong>Precip mm</strong></td><td>Expected rainfall in mm per hour; snowfall is shown in cm. Even light drizzle meaningfully reduces felt temperature when combined with wind.</td></tr>
<tr><td><strong>Rain %</strong></td><td>Probability of precipitation for this hour (0100%), derived from Open-Meteo ensemble model runs. A high value means rain is likely even if the expected rate is low — useful for spotting showers the deterministic forecast may miss.</td></tr> <tr><td><strong>Rain %</strong></td><td>Probability of precipitation for this hour (0100%), derived from Open-Meteo ensemble model runs. A high value means rain is likely even if the expected rate is low — useful for spotting showers the deterministic forecast may miss.</td></tr>
<tr><td><strong>Lightning</strong></td><td>Lightning Potential Index (LPI) from Open-Meteo in J/kg — a convective energy index. Values above 5 suggest moderate lightning risk, above 25 suggest high risk.</td></tr> <tr><td><strong>Lightning</strong></td><td>Lightning Potential Index (LPI) from Open-Meteo in J/kg — a convective energy index. Values above 5 suggest moderate lightning risk, above 25 suggest high risk.</td></tr>
@@ -114,7 +114,7 @@
<tr><td><strong>Managed °C</strong></td><td>The same building model with two interventions: curtains closed by day to block solar gain, and windows opened whenever outdoor air is cooler than inside — the standard UK heatwave advice. Toggle with the Managed tick next to the building dropdown.</td></tr> <tr><td><strong>Managed °C</strong></td><td>The same building model with two interventions: curtains closed by day to block solar gain, and windows opened whenever outdoor air is cooler than inside — the standard UK heatwave advice. Toggle with the Managed tick next to the building dropdown.</td></tr>
<tr><td><strong>Fur Temp °C</strong></td><td>Estimated fur surface temperature a cat or small dog experiences in direct sun, calibrated for a thin/short coat. Choose your pet's fur colour — Black/Dark, Brown/Tabby, Golden/Tan, or White/Pale — from the dropdown; darker coats absorb more solar heat and run hotter.</td></tr> <tr><td><strong>Fur Temp °C</strong></td><td>Estimated fur surface temperature a cat or small dog experiences in direct sun, calibrated for a thin/short coat. Choose your pet's fur colour — Black/Dark, Brown/Tabby, Golden/Tan, or White/Pale — from the dropdown; darker coats absorb more solar heat and run hotter.</td></tr>
<tr><td><strong>Paw Temp °C</strong></td><td>Estimated ground/pavement surface temperature for paw contact, colour-coded by burn risk: green below 40 °C (safe), amber 4052 °C (discomfort or possible burn on prolonged contact), red above 52 °C (can burn within 60 seconds). If you can't hold the back of your hand on the surface for 7 seconds, it's too hot to walk your pet.</td></tr> <tr><td><strong>Paw Temp °C</strong></td><td>Estimated ground/pavement surface temperature for paw contact, colour-coded by burn risk: green below 40 °C (safe), amber 4052 °C (discomfort or possible burn on prolonged contact), red above 52 °C (can burn within 60 seconds). If you can't hold the back of your hand on the surface for 7 seconds, it's too hot to walk your pet.</td></tr>
<tr><td><strong>Pet Shade °C</strong></td><td>Shaded air temperature for a resting pet, using the same shade estimate as Shade Air but with a lower, pet-specific alert threshold — small pets have far less capacity to dump heat than humans.</td></tr> <tr><td><strong>Pet Shade °C</strong></td><td>Shaded air temperature for a resting pet, using the same shade microclimate model as the Shade column but kept as an air temperature and with a lower, pet-specific alert threshold — small pets have far less capacity to dump heat than humans.</td></tr>
<tr><td><strong>Pet Home °C</strong></td><td>Indoor temperature estimate for a pet left home alone, using the same model as the Indoors column but with a lower, pet-specific alert threshold (26 °C) rather than the human heatwave guidance threshold.</td></tr> <tr><td><strong>Pet Home °C</strong></td><td>Indoor temperature estimate for a pet left home alone, using the same model as the Indoors column but with a lower, pet-specific alert threshold (26 °C) rather than the human heatwave guidance threshold.</td></tr>
<tr><td><strong>Visibility km</strong></td><td>Horizontal visibility in kilometres, sourced from the Copernicus CAMS air quality model. Below 1 km indicates fog or very thick haze; below 10 km suggests mist, smoke, or significant pollution. Relevant for driving, flying, cycling, and photography.</td></tr> <tr><td><strong>Visibility km</strong></td><td>Horizontal visibility in kilometres, sourced from the Copernicus CAMS air quality model. Below 1 km indicates fog or very thick haze; below 10 km suggests mist, smoke, or significant pollution. Relevant for driving, flying, cycling, and photography.</td></tr>
<tr><td><strong>AQI</strong></td><td>European Air Quality Index (0100+), combining PM2.5, PM10, ozone, nitrogen dioxide, and sulphur dioxide. 020 = Good; 2040 = Fair; 4060 = Moderate; 6080 = Poor; 80100 = Very Poor; 100+ = Extremely Poor. Sourced from Copernicus CAMS.</td></tr> <tr><td><strong>AQI</strong></td><td>European Air Quality Index (0100+), combining PM2.5, PM10, ozone, nitrogen dioxide, and sulphur dioxide. 020 = Good; 2040 = Fair; 4060 = Moderate; 6080 = Poor; 80100 = Very Poor; 100+ = Extremely Poor. Sourced from Copernicus CAMS.</td></tr>
+2 -2
View File
@@ -34,7 +34,7 @@
{"@type":"Question","name":"What UV level is dangerous?","acceptedAnswer":{"@type":"Answer","text":"WHO UV index scale: 1-2 Low, no protection needed for short exposure. 3-5 Moderate, wear sunscreen and a hat for over 30 minutes outside. 6-7 High, seek shade during midday, apply SPF 30+. 8-10 Very High, minimise direct sun 11am-3pm. 11+ Extreme, avoid outdoor exposure during peak hours. In the UK, UV 6+ is common on clear summer days May-August."}}, {"@type":"Question","name":"What UV level is dangerous?","acceptedAnswer":{"@type":"Answer","text":"WHO UV index scale: 1-2 Low, no protection needed for short exposure. 3-5 Moderate, wear sunscreen and a hat for over 30 minutes outside. 6-7 High, seek shade during midday, apply SPF 30+. 8-10 Very High, minimise direct sun 11am-3pm. 11+ Extreme, avoid outdoor exposure during peak hours. In the UK, UV 6+ is common on clear summer days May-August."}},
{"@type":"Question","name":"How long until I get sunburn today?","acceptedAnswer":{"@type":"Answer","text":"It depends on the UV index and your skin type. SunScope shows a Burn time column estimating minutes to the sunburn threshold (one Minimal Erythemal Dose). At UV 8+, fair skin types I and II can burn in under 10 minutes. Medium skin types have around 20-25 minutes. Darker skin has more natural protection but is not immune. Burn time assumes no sunscreen and no shade."}}, {"@type":"Question","name":"How long until I get sunburn today?","acceptedAnswer":{"@type":"Answer","text":"It depends on the UV index and your skin type. SunScope shows a Burn time column estimating minutes to the sunburn threshold (one Minimal Erythemal Dose). At UV 8+, fair skin types I and II can burn in under 10 minutes. Medium skin types have around 20-25 minutes. Darker skin has more natural protection but is not immune. Burn time assumes no sunscreen and no shade."}},
{"@type":"Question","name":"What is the difference between UV-A and UV-B?","acceptedAnswer":{"@type":"Answer","text":"UV-A has a longer wavelength, penetrates deeper into skin, is present throughout all daylight hours, passes through glass, and is the primary cause of long-term skin ageing. UV-B causes sunburn directly and drives vitamin D production. UV-B is strongest when the sun is high and falls sharply in morning and evening. SunScope shows both indices separately."}}, {"@type":"Question","name":"What is the difference between UV-A and UV-B?","acceptedAnswer":{"@type":"Answer","text":"UV-A has a longer wavelength, penetrates deeper into skin, is present throughout all daylight hours, passes through glass, and is the primary cause of long-term skin ageing. UV-B causes sunburn directly and drives vitamin D production. UV-B is strongest when the sun is high and falls sharply in morning and evening. SunScope shows both indices separately."}},
{"@type":"Question","name":"How does shade affect how hot I feel?","acceptedAnswer":{"@type":"Answer","text":"On a sunny day shade can reduce felt temperature by 8-12 degrees because it removes the direct solar radiation load. However shade does not reduce air temperature or humidity, so on very humid days you will still feel uncomfortable in shade. SunScope shows UTCI for open sun exposure, so felt temperature in shade will be noticeably lower than the figure shown."}}, {"@type":"Question","name":"How does shade affect how hot I feel?","acceptedAnswer":{"@type":"Answer","text":"On a sunny day shade can reduce felt temperature by 8-12 degrees because it removes the direct solar radiation load. However shade does not reduce air temperature or humidity, so on very humid days you will still feel uncomfortable in shade. SunSoak assumes open sun exposure, so SunScope also has a Shade column: the same felt-temperature calculation with the direct beam removed, giving you both numbers side by side."}},
{"@type":"Question","name":"How hot does a car get in the sun?","acceptedAnswer":{"@type":"Answer","text":"On a mild 20 degree day with strong sunshine, the inside of a sealed car can reach 40-45 degrees within 20 minutes and over 50 degrees within an hour. On a hot 30 degree day it can hit 60 degrees. The glass acts like a greenhouse. SunScope calculates cabin temperature hour by hour. Above 35 degrees is dangerous for a child or pet. Above 45 degrees can be fatal within minutes."}}, {"@type":"Question","name":"How hot does a car get in the sun?","acceptedAnswer":{"@type":"Answer","text":"On a mild 20 degree day with strong sunshine, the inside of a sealed car can reach 40-45 degrees within 20 minutes and over 50 degrees within an hour. On a hot 30 degree day it can hit 60 degrees. The glass acts like a greenhouse. SunScope calculates cabin temperature hour by hour. Above 35 degrees is dangerous for a child or pet. Above 45 degrees can be fatal within minutes."}},
{"@type":"Question","name":"Does opening car windows reduce the temperature inside?","acceptedAnswer":{"@type":"Answer","text":"Yes, significantly. Open windows allow convective airflow to flush hot cabin air out, reducing heat build-up by roughly 60-80% compared to a sealed vehicle. SunScope has a Ventilation toggle in the Vehicle column that shows the estimated cabin temperature with windows open alongside the sealed figure."}}, {"@type":"Question","name":"Does opening car windows reduce the temperature inside?","acceptedAnswer":{"@type":"Answer","text":"Yes, significantly. Open windows allow convective airflow to flush hot cabin air out, reducing heat build-up by roughly 60-80% compared to a sealed vehicle. SunScope has a Ventilation toggle in the Vehicle column that shows the estimated cabin temperature with windows open alongside the sealed figure."}},
{"@type":"Question","name":"Is it safe to leave a dog in a parked car?","acceptedAnswer":{"@type":"Answer","text":"No. On any warm or sunny day a sealed vehicle heats up rapidly to temperatures that can cause heatstroke and death in animals within minutes. Dogs are at risk at cabin temperatures above 32 degrees. SunScope Vehicle column shows hourly cabin temperature. Amber (above 35 degrees) or red (above 45 degrees) means not safe for a dog, cat, or young child, even for a few minutes."}}, {"@type":"Question","name":"Is it safe to leave a dog in a parked car?","acceptedAnswer":{"@type":"Answer","text":"No. On any warm or sunny day a sealed vehicle heats up rapidly to temperatures that can cause heatstroke and death in animals within minutes. Dogs are at risk at cabin temperatures above 32 degrees. SunScope Vehicle column shows hourly cabin temperature. Amber (above 35 degrees) or red (above 45 degrees) means not safe for a dog, cat, or young child, even for a few minutes."}},
@@ -199,7 +199,7 @@
<details class="faq-block"> <details class="faq-block">
<summary class="faq-q">How does shade affect how hot I feel?</summary> <summary class="faq-q">How does shade affect how hot I feel?</summary>
<p class="faq-a">On a sunny day shade can make a very significant difference - often 8 to 12 degrees in UTCI terms - because it removes the direct solar radiation load entirely. However shade does not reduce the air temperature or humidity, so on very humid days you will still feel uncomfortably warm even in full shade.<br><br>SunScope shows the UTCI value for open sun exposure. If you are planning to stay in shade, the felt temperature you actually experience will be noticeably lower than the figure shown.</p> <p class="faq-a">On a sunny day shade can make a very significant difference - often 8 to 12 degrees in UTCI terms - because it removes the direct solar radiation load entirely. However shade does not reduce the air temperature or humidity, so on very humid days you will still feel uncomfortably warm even in full shade.<br><br><strong>SunSoak</strong> assumes open sun exposure. If you are planning to stay in shade, switch on the <strong>Shade</strong> column (or the Shade tab in Quick view): it runs the same felt-temperature calculation with the direct beam removed and part of the sky blocked by whatever is casting the shade, while wind, humidity and rain still count in full. In Quick view the cards and the temperature curve both redraw for whichever you pick.<br><br>After dark the two readings converge, as they should — there is no sun to stand out of at midnight.</p>
</details> </details>
<h2>Pets, Heat, Vehicles and the Urban Environment</h2> <h2>Pets, Heat, Vehicles and the Urban Environment</h2>