temperature chart and table alignments

This commit is contained in:
Vincent van der Wal
2026-07-25 12:43:41 +02:00
parent d78ac84a12
commit cd98b7a5cc
16 changed files with 621 additions and 133 deletions
@@ -165,6 +165,21 @@
unit: string;
showCredit: boolean;
series: ChartSeries[];
zeroBaseLeft?: boolean;
yMin?: number;
yMax?: number;
}
// Sensible axis behaviour per variable so the y-scale stays readable (e.g.
// pressure never anchored to zero; percentages pinned to 0-100).
function axisForVar(v: string): { zeroBaseLeft: boolean; yMin?: number; yMax?: number } {
if (['pressure_msl', 'surface_pressure', 'temperature_2m', 'dew_point_2m'].includes(v)) {
return { zeroBaseLeft: false };
}
if (['relative_humidity_2m', 'cloud_cover', 'precipitation_probability'].includes(v)) {
return { zeroBaseLeft: true, yMin: 0, yMax: 100 };
}
return { zeroBaseLeft: true };
}
const BAND_COLOR = 'rgba(115, 192, 222, 0.9)';
@@ -202,33 +217,40 @@
const isColumn = isColumnUnit(unit);
const memberCount = varData.members.length;
// Trim to the valid horizon so the axis scale ignores the trailing
// zeros the service pads past the model's range.
const vMax = varData.max.slice(0, validLength);
const vMin = varData.min.slice(0, validLength);
const vAvg = varData.average.slice(0, validLength);
// Min/max spread band + mean, instead of every individual member
const series: ChartSeries[] = [
{
name: 'Max',
type: 'line',
color: BAND_COLOR,
data: varData.max,
data: vMax,
width: 1,
fill: true,
fillOpacity: 0.25,
bandTo: varData.min,
bandTo: vMin,
format: (v) => `${v.toFixed(1)} ${unit}`
},
{
name: 'Mean',
type: isColumn ? 'bar' : 'line',
color: CHART_COLORS.average,
data: varData.average,
width: 3,
data: vAvg,
width: 3.5,
dashed: !isColumn,
outline: !isColumn,
format: (v) => `${v.toFixed(1)} ${unit}`
},
{
name: 'Min',
type: 'line',
color: BAND_COLOR,
data: varData.min,
data: vMin,
width: 1,
format: (v) => `${v.toFixed(1)} ${unit}`
}
@@ -237,6 +259,7 @@
const isFirst = vi === 0;
const isLast = vi === variables.length - 1;
const axis = axisForVar(variable);
defs.push({
// each chart is labelled so the variable is obvious at a glance
title: `${varLabel(variable)}${isColumn ? '' : ' spread'}`,
@@ -245,7 +268,10 @@
: `min · mean · max (${unit})`,
unit,
showCredit: isLast,
series
series,
zeroBaseLeft: axis.zeroBaseLeft,
yMin: axis.yMin,
yMax: axis.yMax
});
}
@@ -255,7 +281,7 @@
<!-- ─── Page hero: location + ensemble model selection ─────────────────────── -->
<div class="mb-5 flex flex-wrap items-center justify-between gap-x-6 gap-y-3">
<div class="relative mb-3 flex flex-wrap items-center justify-between gap-x-6 gap-y-3 md:mb-5">
<div class="flex min-w-0 items-center gap-3">
<img
class="h-10 w-10 shrink-0 rounded-full shadow-sm ring-2 ring-border"
@@ -275,7 +301,7 @@
</div>
</div>
<div class="flex w-full items-center gap-3 sm:w-auto">
<div class="lg:absolute lg:right-0 lg:top-0 flex w-full items-center gap-3 sm:w-auto">
<ModelSelector
selectedModel={params.models?.[0] ?? 'ncep_gefs_seamless'}
groups={ensembleModelGroups}
@@ -322,7 +348,7 @@
</div>
{/if}
<ChartContainer {loading} chartCount={params.hourly?.length || 1} chartHeight={300}>
<ChartContainer {loading} chartCount={params.hourly?.length || 1} chartHeight={300} bleed={false}>
{#if fetchedData}
{#each chartDefs as def, i (i)}
<CanvasChart
@@ -335,6 +361,9 @@
title={def.title}
subtitle={def.subtitle}
showCredit={def.showCredit}
zeroBaseLeft={def.zeroBaseLeft ?? true}
yMin={def.yMin}
yMax={def.yMax}
{showLegend}
height={300}
group={CHART_GROUP}