2026-07-11 00:29:47 +00:00
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
|
<html lang="en">
|
|
|
|
|
|
<head>
|
|
|
|
|
|
<meta charset="utf-8" />
|
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
|
|
|
|
<title>Thermograph — reading the grades</title>
|
|
|
|
|
|
<link rel="stylesheet" href="style.css" />
|
|
|
|
|
|
</head>
|
|
|
|
|
|
<body>
|
|
|
|
|
|
<header>
|
|
|
|
|
|
<div class="brand">
|
|
|
|
|
|
<span class="logo">▚</span>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<h1>Thermograph · Guide</h1>
|
|
|
|
|
|
<p class="tag">What the metrics and percentile grades mean</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<nav class="view-nav" aria-label="Views">
|
|
|
|
|
|
<a href="./" data-view="map">Weekly</a>
|
|
|
|
|
|
<a href="calendar" data-view="calendar">Calendar</a>
|
|
|
|
|
|
<a href="day" data-view="day">Day</a>
|
Scope app to /thermograph, free the domain root for a portfolio (#10)
- Drop the app-level "/" redirect so the FastAPI app no longer claims the
domain root; it stays scoped to THERMOGRAPH_BASE (/thermograph). Root now
404s at the app, leaving it for another service behind the proxy.
- Caddyfile: serve a static portfolio at / and reverse-proxy /thermograph* to
uvicorn, on emigriffith.dev with automatic Let's Encrypt TLS.
- Default THERMOGRAPH_BASE to /thermograph in the env example to match. (#11)
* Add dev CI/CD pipeline deploying to a LAN server (#6)
* Weekly page: no-rain visuals adopt the dry-streak look
On the Weekly page, no-rain (0") days now warm with the dry-streak ramp
(tan→red, deepening to a 14-day cap) instead of a flat blue/tan, matching
the Dry chart and calendar's dry-period language:
- Precip trend chart: no-rain day dots colored by drynessColor(dsr).
- "Daily, graded" timeline strip: Rain-row dry cells tinted by dsr via a
new optional color override on rdCell.
Rain days keep their intensity-tier colors; unknown streaks fall back to
the prior flat color, so nothing regresses.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019VP23wKmjS2ozk1g5a9g1Z
* Calendar totals: compact per-category status lines
Replace the stacked share bar + labeled percentage chips above the
calendar grid with a compact deviation strip: one thin status-colored
line per category (height scaled to the tallest non-median category),
with each share printed above it in that category's own status color.
The median tier — the neutral "Normal" center of the diverging
temperature scale — draws no line, just a faint baseline tick, so it
reads as the reference the other categories deviate from. Precip and
dry-streak have no natural median, so every category there keeps a line.
Percentage text is lifted toward the theme text color (color-mix) so
even the darkest/lightest tiers stay legible in both light and dark.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Calendar totals strip: wider bars, 0.1%-precision small shares (#5)
Widen the per-category deviation bars (3px→10px) so each reads
clearly, and print sub-1% shares as e.g. "0.4%" instead of "<1%".
Claude-Session: https://claude.ai/code/session_019VP23wKmjS2ozk1g5a9g1Z
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* Weekly timeline: label dry streak instead of a dot
On the precip row of the recent/forecast timeline, dry days now show the
running dry-streak count ("3d" = 3 days since measurable rain), tinted by
the same dryness ramp as the chart, rather than a bare "·".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Add dev CI/CD pipeline deploying to a LAN server
PRs into dev run a build + boot/health check, auto-merge on green, and
deploy the merged branch to a self-hosted runner on the LAN box, which
runs the app as a sudo-free systemd --user service on 0.0.0.0:8137.
- ci-cd.yml: build -> auto-merge -> deploy (self-hosted)
- deploy-dev.yml: deploy on direct pushes / manual dispatch
- deploy-dev.sh + thermograph-dev.service + provision-dev-lan.sh
- CLAUDE.md: dev is the PR base branch; commit/PR message conventions
- DEPLOY-DEV.md: pipeline docs
* Match runner service name in docs to the installed user unit
* Pin CI Python to 3.12 for prebuilt pyarrow/pandas wheels
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* Build LAN dev venv on pinned Python 3.12 via uv (#7)
This machine's default python3 is 3.14, which has no prebuilt pyarrow/pandas
wheels, so plain venv + pip fell back to a failing source build. Use uv to pin
the interpreter (fetching a managed CPython 3.12 if needed), independent of the
runner's PATH and pyenv state.
* Add comfort-temperature compare view; simplify Feels calendar filter (#9)
Compare page (frontend/compare.{html,js} + /thermograph/compare route):
line up several places over a date range and rank which best matches a
comfort temperature. Per location it pulls the same daily record the
Calendar uses and, per day, takes a chosen temperature (daytime high /
daily mean / overnight low / feels-like) against the comfort target. A
day "hits comfort" when it lands within an adjustable band; otherwise it
counts as colder or warmer and the average miss is tracked. Results are
ranked by comfort-day share (tie-broken by the smaller typical miss),
with a diverging below/comfort/above bar and per-bucket stats. Comfort,
band and judged temperature re-rank instantly client-side; only the
location set or date range trigger a data load (shared calendar cache).
Feels calendar filter: drop the comfort-temperature slider and the
client-side felt-high/felt-low re-pick. The Feels metric now colors by
the server's combined feels-like value like the other metrics, so its
tab matches the rest of the metric selector.
* Scope app to /thermograph, free the domain root for a portfolio (#10)
- Drop the app-level "/" redirect so the FastAPI app no longer claims the
domain root; it stays scoped to THERMOGRAPH_BASE (/thermograph). Root now
404s at the app, leaving it for another service behind the proxy.
- Caddyfile: serve a static portfolio at / and reverse-proxy /thermograph* to
uvicorn, on emigriffith.dev with automatic Let's Encrypt TLS.
- Default THERMOGRAPH_BASE to /thermograph in the env example to match.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 03:03:39 +00:00
|
|
|
|
<a href="compare" data-view="compare">Compare</a>
|
2026-07-11 00:29:47 +00:00
|
|
|
|
</nav>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
|
|
<main class="guide">
|
|
|
|
|
|
<section class="guide-block">
|
|
|
|
|
|
<h2>How the grades work</h2>
|
|
|
|
|
|
<p>Every grade is <b>relative to this place's own climate history</b>, not an absolute
|
|
|
|
|
|
temperature. For each day Thermograph builds a ±7-day seasonal distribution from
|
|
|
|
|
|
decades of local records, then reports the <b>percentile</b> where the day falls in it.</p>
|
|
|
|
|
|
<p>So a 60° day can be “Above Normal” in one place or season and
|
|
|
|
|
|
“Below Normal” in another. That's why the categories read
|
|
|
|
|
|
“Above/Below Normal”, “High/Low”, and “Near Record” —
|
|
|
|
|
|
never “hot” or “cold”.</p>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<section class="guide-block">
|
|
|
|
|
|
<h2>Grade scale — percentile of the local ±7-day history</h2>
|
|
|
|
|
|
<div class="guide-scale" id="temp-scale"></div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<section class="guide-block">
|
|
|
|
|
|
<h2>Rain intensity — percentile among rain days</h2>
|
|
|
|
|
|
<p class="muted">Precipitation is graded only across days that actually saw rain, so
|
|
|
|
|
|
“Heavy” means heavy <i>for a rainy day here</i>. Dry days are colored by
|
|
|
|
|
|
their dry streak instead (below).</p>
|
|
|
|
|
|
<div class="guide-scale" id="rain-scale"></div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<section class="guide-block">
|
|
|
|
|
|
<h2>The metrics</h2>
|
|
|
|
|
|
<dl class="guide-metrics">
|
|
|
|
|
|
<dt>High / Low</dt><dd>The day's highest and lowest air temperature (°F).</dd>
|
|
|
|
|
|
<dt>Feels</dt><dd>Apparent temperature — what the air actually felt like once humidity
|
Scope app to /thermograph, free the domain root for a portfolio (#10)
- Drop the app-level "/" redirect so the FastAPI app no longer claims the
domain root; it stays scoped to THERMOGRAPH_BASE (/thermograph). Root now
404s at the app, leaving it for another service behind the proxy.
- Caddyfile: serve a static portfolio at / and reverse-proxy /thermograph* to
uvicorn, on emigriffith.dev with automatic Let's Encrypt TLS.
- Default THERMOGRAPH_BASE to /thermograph in the env example to match. (#11)
* Add dev CI/CD pipeline deploying to a LAN server (#6)
* Weekly page: no-rain visuals adopt the dry-streak look
On the Weekly page, no-rain (0") days now warm with the dry-streak ramp
(tan→red, deepening to a 14-day cap) instead of a flat blue/tan, matching
the Dry chart and calendar's dry-period language:
- Precip trend chart: no-rain day dots colored by drynessColor(dsr).
- "Daily, graded" timeline strip: Rain-row dry cells tinted by dsr via a
new optional color override on rdCell.
Rain days keep their intensity-tier colors; unknown streaks fall back to
the prior flat color, so nothing regresses.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019VP23wKmjS2ozk1g5a9g1Z
* Calendar totals: compact per-category status lines
Replace the stacked share bar + labeled percentage chips above the
calendar grid with a compact deviation strip: one thin status-colored
line per category (height scaled to the tallest non-median category),
with each share printed above it in that category's own status color.
The median tier — the neutral "Normal" center of the diverging
temperature scale — draws no line, just a faint baseline tick, so it
reads as the reference the other categories deviate from. Precip and
dry-streak have no natural median, so every category there keeps a line.
Percentage text is lifted toward the theme text color (color-mix) so
even the darkest/lightest tiers stay legible in both light and dark.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Calendar totals strip: wider bars, 0.1%-precision small shares (#5)
Widen the per-category deviation bars (3px→10px) so each reads
clearly, and print sub-1% shares as e.g. "0.4%" instead of "<1%".
Claude-Session: https://claude.ai/code/session_019VP23wKmjS2ozk1g5a9g1Z
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* Weekly timeline: label dry streak instead of a dot
On the precip row of the recent/forecast timeline, dry days now show the
running dry-streak count ("3d" = 3 days since measurable rain), tinted by
the same dryness ramp as the chart, rather than a bare "·".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Add dev CI/CD pipeline deploying to a LAN server
PRs into dev run a build + boot/health check, auto-merge on green, and
deploy the merged branch to a self-hosted runner on the LAN box, which
runs the app as a sudo-free systemd --user service on 0.0.0.0:8137.
- ci-cd.yml: build -> auto-merge -> deploy (self-hosted)
- deploy-dev.yml: deploy on direct pushes / manual dispatch
- deploy-dev.sh + thermograph-dev.service + provision-dev-lan.sh
- CLAUDE.md: dev is the PR base branch; commit/PR message conventions
- DEPLOY-DEV.md: pipeline docs
* Match runner service name in docs to the installed user unit
* Pin CI Python to 3.12 for prebuilt pyarrow/pandas wheels
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* Build LAN dev venv on pinned Python 3.12 via uv (#7)
This machine's default python3 is 3.14, which has no prebuilt pyarrow/pandas
wheels, so plain venv + pip fell back to a failing source build. Use uv to pin
the interpreter (fetching a managed CPython 3.12 if needed), independent of the
runner's PATH and pyenv state.
* Add comfort-temperature compare view; simplify Feels calendar filter (#9)
Compare page (frontend/compare.{html,js} + /thermograph/compare route):
line up several places over a date range and rank which best matches a
comfort temperature. Per location it pulls the same daily record the
Calendar uses and, per day, takes a chosen temperature (daytime high /
daily mean / overnight low / feels-like) against the comfort target. A
day "hits comfort" when it lands within an adjustable band; otherwise it
counts as colder or warmer and the average miss is tracked. Results are
ranked by comfort-day share (tie-broken by the smaller typical miss),
with a diverging below/comfort/above bar and per-bucket stats. Comfort,
band and judged temperature re-rank instantly client-side; only the
location set or date range trigger a data load (shared calendar cache).
Feels calendar filter: drop the comfort-temperature slider and the
client-side felt-high/felt-low re-pick. The Feels metric now colors by
the server's combined feels-like value like the other metrics, so its
tab matches the rest of the metric selector.
* Scope app to /thermograph, free the domain root for a portfolio (#10)
- Drop the app-level "/" redirect so the FastAPI app no longer claims the
domain root; it stays scoped to THERMOGRAPH_BASE (/thermograph). Root now
404s at the app, leaving it for another service behind the proxy.
- Caddyfile: serve a static portfolio at / and reverse-proxy /thermograph* to
uvicorn, on emigriffith.dev with automatic Let's Encrypt TLS.
- Default THERMOGRAPH_BASE to /thermograph in the env example to match.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 03:03:39 +00:00
|
|
|
|
and wind are factored in, taking whichever apparent extreme (heat-index high or
|
|
|
|
|
|
wind-chill low) sits further from a temperate baseline, graded against its own history.</dd>
|
2026-07-11 00:29:47 +00:00
|
|
|
|
<dt>Humid</dt><dd>Absolute humidity: grams of water vapor per cubic meter of air (g/m³).</dd>
|
|
|
|
|
|
<dt>Wind</dt><dd>Average sustained wind speed (mph).</dd>
|
|
|
|
|
|
<dt>Gust</dt><dd>Peak wind gust for the day (mph).</dd>
|
|
|
|
|
|
<dt>Precip</dt><dd>Total precipitation (inches), graded by intensity among rain days.</dd>
|
|
|
|
|
|
<dt>Dry streak</dt><dd>Consecutive days since the last measurable rain — the color deepens
|
|
|
|
|
|
the longer it's been dry, and resets to blue on a rain day.</dd>
|
|
|
|
|
|
</dl>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<p class="guide-back"><a href="./">← Back to Thermograph</a></p>
|
|
|
|
|
|
</main>
|
|
|
|
|
|
|
|
|
|
|
|
<script src="nav.js"></script>
|
|
|
|
|
|
<script>
|
|
|
|
|
|
// The scale rows mirror the tiers used across the app (see calendar.js / app.js).
|
|
|
|
|
|
const TEMP = [
|
|
|
|
|
|
["rec-cold", "Near Record", "<1"], ["very-cold", "Very Low", "1–10"],
|
|
|
|
|
|
["cold", "Low", "10–25"], ["cool", "Below Normal", "25–40"],
|
|
|
|
|
|
["normal", "Normal", "40–60"], ["warm", "Above Normal", "60–75"],
|
|
|
|
|
|
["hot", "High", "75–90"], ["very-hot", "Very High", "90–99"],
|
|
|
|
|
|
["rec-hot", "Near Record", ">99"],
|
|
|
|
|
|
];
|
|
|
|
|
|
const RAIN = [
|
|
|
|
|
|
["wet-1", "Trace", "<1"], ["wet-2", "Very Light", "1–10"], ["wet-3", "Light", "10–25"],
|
|
|
|
|
|
["wet-4", "Light–Mod", "25–40"], ["wet-5", "Moderate", "40–60"], ["wet-6", "Mod–Heavy", "60–75"],
|
|
|
|
|
|
["wet-7", "Heavy", "75–90"], ["wet-8", "Very Heavy", "90–99"], ["wet-9", "Extreme", ">99"],
|
|
|
|
|
|
];
|
|
|
|
|
|
const row = ([cls, label, range]) =>
|
|
|
|
|
|
`<div class="guide-seg"><span class="guide-sw" style="background:var(--${cls})"></span>` +
|
|
|
|
|
|
`<span class="guide-lbl">${label}</span><span class="guide-rng">${range}</span></div>`;
|
|
|
|
|
|
document.getElementById("temp-scale").innerHTML = TEMP.map(row).join("");
|
|
|
|
|
|
document.getElementById("rain-scale").innerHTML = RAIN.map(row).join("");
|
|
|
|
|
|
</script>
|
|
|
|
|
|
</body>
|
|
|
|
|
|
</html>
|