fix: hide probability bands on temperature chart

This commit is contained in:
2569718930@qq.com
2026-06-06 18:00:28 +08:00
parent 89f77b8db4
commit c2f9bc5994
6 changed files with 21 additions and 104 deletions
@@ -41,7 +41,7 @@ function hasDrawableTemperatureChartContent({
probabilityOverlay: ProbabilityOverlay | null;
zoomedData: Array<Record<string, any>>;
}) {
if (probabilityOverlay?.muLine || probabilityOverlay?.bands.length) return true;
void probabilityOverlay;
return activeSeries.some((series) =>
zoomedData.some((point, index) => {
const value = point?.[series.key] ?? series.values[index];
@@ -261,29 +261,6 @@ function TemperatureChartCanvasComponent({
</label>
)}
{probabilityOverlay && (
<span
className={clsx(
"inline-flex items-center gap-1.5 rounded border border-violet-200 bg-violet-50 px-1.5 py-0.5 text-[10px] font-bold text-violet-700",
canToggleRunwayDetails ? "" : "ml-auto",
)}
title={
probabilityOverlay.muLine
? probabilityOverlay.muLine.label
: isEn
? "Legacy Gaussian probability bands"
: "Legacy 高斯概率温度带"
}
>
<span className="h-2 w-2 rounded-full bg-violet-500/70" />
<span>{isEn ? "Gaussian" : "高斯概率"}</span>
{probabilityOverlay.muLine && (
<span className="font-mono text-violet-600">
μ {probabilityOverlay.muLine.value.toFixed(1)}{tempSymbol}
</span>
)}
</span>
)}
</div>
<div ref={chartHostRef} className={clsx("relative flex-1", compact ? "min-h-[120px]" : "min-h-[220px]")}>
{!shouldRenderChart && <TemperatureChartSkeleton compact={compact} />}
@@ -316,16 +293,6 @@ function TemperatureChartCanvasComponent({
domain={chartDomain}
ticks={intDegreeTicks ?? undefined}
/>
{timeframe === "1D" && probabilityOverlay?.bands.map((band) => (
<ReferenceArea
key={band.key}
y1={band.lower}
y2={band.upper}
strokeOpacity={0}
fill="#8b5cf6"
fillOpacity={band.opacity}
/>
))}
{timeframe === "1D" && cityThresholds.map((t, idx) => {
const isSelected = row && (Number(row.target_threshold ?? row.target_value) === t.threshold);
const labelText = isEn
@@ -348,20 +315,6 @@ function TemperatureChartCanvasComponent({
/>
);
})}
{timeframe === "1D" && probabilityOverlay?.muLine && (
<ReferenceLine
y={probabilityOverlay.muLine.value}
stroke="#7c3aed"
strokeDasharray="2 3"
strokeWidth={1.4}
label={{
value: compact ? undefined : probabilityOverlay.muLine.label,
fill: "#7c3aed",
fontSize: 9,
position: "insideTopLeft",
}}
/>
)}
<Tooltip
filterNull={false}
cursor={{ stroke: "#94a3b8", strokeWidth: 1 }}
@@ -378,7 +331,7 @@ function TemperatureChartCanvasComponent({
payload={props.payload as ReadonlyArray<{ payload?: Record<string, any> }> | undefined}
data={zoomedData}
series={activeSeries}
probabilityOverlay={probabilityOverlay}
probabilityOverlay={null}
tempSymbol={tempSymbol}
isEn={isEn}
/>
@@ -118,42 +118,15 @@ function buildTooltipRows(
.filter((item): item is TooltipRow => item !== null);
}
function formatProbabilityTemp(value: number, tempSymbol: string) {
return `${value.toFixed(1)}${tempSymbol}`;
}
function buildTooltipProbabilityRows(
probabilityOverlay: ProbabilityOverlay | null | undefined,
tempSymbol: string,
isEn: boolean,
): TooltipProbabilityRow[] {
if (!probabilityOverlay) return [];
const rows: TooltipProbabilityRow[] = [];
if (probabilityOverlay.muLine) {
rows.push({
key: "gaussian_mu",
label: isEn ? "Gaussian μ" : "高斯 μ",
value: formatProbabilityTemp(probabilityOverlay.muLine.value, tempSymbol),
color: "#7c3aed",
});
}
probabilityOverlay.bands
.slice()
.sort((left, right) => right.probability - left.probability)
.forEach((band) => {
const range = `${band.lower.toFixed(1)}-${band.upper.toFixed(1)}${tempSymbol}`;
const probability = `${Math.round(band.probability * 100)}%`;
rows.push({
key: band.key,
label: isEn ? "Probability band" : "概率温度带",
value: `${range} ${probability}`,
color: "#8b5cf6",
});
});
return rows;
void probabilityOverlay;
void tempSymbol;
void isEn;
return [];
}
export const __buildTemperatureTooltipRowsForTest = buildTooltipRows;
@@ -38,8 +38,8 @@ const quickStart: Record<"zh" | "en", GuideCopy[]> = {
body: "橙色 DEB Forecast 是融合模型和日内修正后的路径,用来判断后续升温或降温空间。",
},
{
title: "最后看概率",
body: "紫色区域和虚线表示高概率温度带,适合判断当前实测是否偏离主预期。",
title: "最后看图层",
body: "按需打开模型线或跑道明细,用来确认当前实测是否偏离 DEB 主路径。",
},
],
en: [
@@ -56,8 +56,8 @@ const quickStart: Record<"zh" | "en", GuideCopy[]> = {
body: "The orange DEB Forecast blends model context with intraday correction to frame the remaining move.",
},
{
title: "Check probability",
body: "The purple band and dotted line show the high-probability temperature zone for fast deviation checks.",
title: "Check layers",
body: "Use model lines or runway details only when needed to confirm whether live observations are drifting away from the DEB path.",
},
],
};
@@ -66,7 +66,7 @@ const legendItems: Record<"zh" | "en", GuideCopy[]> = {
zh: [
{ title: "实测 / 结算线", body: "优先展示结算跑道、官方站或城市核心实况,用于判断已兑现温度。" },
{ title: "DEB Forecast", body: "橙色预测路径,重点看它和实测线在峰值窗口前后的分歧。" },
{ title: "概率", body: "紫色带表示当前概率分布的主要落点,虚线是概率均值附近。" },
{ title: "市场概率", body: "概率仍用于市场判断和后台分析,但默认不再占用温度主图空间。" },
{ title: "机场报文", body: "METAR / MGM 作为机场站参考,默认只在适合的城市自动显示。" },
{ title: "模型线", body: "ECMWF、GFS、ICON、GEM 等提供背景,默认弱化为辅助判断。" },
{ title: "跑道明细", body: "打开后可查看各跑道传感器,关闭后仍保留结算跑道温度。" },
@@ -74,7 +74,7 @@ const legendItems: Record<"zh" | "en", GuideCopy[]> = {
en: [
{ title: "Live / settlement", body: "Settlement runway, official station, or core live observation used as the realized anchor." },
{ title: "DEB Forecast", body: "Orange forecast path; focus on its gap versus live observations near the peak window." },
{ title: "Probability band", body: "Purple band marks the main probability zone, with the dotted line near the probability mean." },
{ title: "Market probability", body: "Probability remains available for market analysis, but no longer occupies the main temperature chart by default." },
{ title: "Airport reports", body: "METAR / MGM are airport references and are auto-shown only where they are useful by default." },
{ title: "Model lines", body: "ECMWF, GFS, ICON, GEM, and related model layers provide background context." },
{ title: "Runway details", body: "When disabled, the chart still keeps the settlement runway temperature visible." },
@@ -160,8 +160,8 @@ export function UsageGuideDashboard({ isEn }: { isEn: boolean }) {
</h1>
<p className="mt-2 max-w-3xl text-sm leading-6 text-slate-500">
{isEn
? "Start from live evidence, compare DEB, then use probability and layer toggles to confirm whether the city is moving away from the main path."
: "先看实况锚点,再对照 DEB 路径,最后用概率带和图层显隐确认城市是否偏离主预期。"}
? "Start from live evidence, compare DEB, then use layer toggles to confirm whether the city is moving away from the main path."
: "先看实况锚点,再对照 DEB 路径,最后用图层显隐确认城市是否偏离主预期。"}
</p>
</div>
<div className="grid grid-cols-3 gap-2 text-center text-[11px] font-bold text-slate-600">
@@ -1637,7 +1637,7 @@ export function runTests() {
const firstBand = bandPoints[0].runway_band;
assert(Array.isArray(firstBand) && firstBand[0] === 24.0 && firstBand[1] === 26.0, "runway_band tuple values should match input limits");
// ── Legacy Gaussian probability overlay test ──
// ── Legacy Gaussian probability data should not render as a visible chart overlay ──
const gaussianOverlayChart = __buildTemperatureChartDataForTest(
{
city: "toronto",
@@ -1665,14 +1665,7 @@ export function runTests() {
) as any;
const gaussianOverlay = gaussianOverlayChart.probabilityOverlay;
assert(gaussianOverlay, "legacy Gaussian probabilities should be exposed as a chart overlay");
assert(gaussianOverlay.muLine?.value === 27.4, "legacy Gaussian μ should become a reference line");
assert(
gaussianOverlay.bands.some(
(band: any) => band.value === 27 && band.lower === 26.5 && band.upper === 27.5 && band.probability === 0.42,
),
"legacy Gaussian buckets should become horizontal probability temperature bands",
);
assert(gaussianOverlay === null, "legacy Gaussian probabilities should not create visible chart overlays");
assert(
!gaussianOverlayChart.series.some((series: any) => String(series.key || "").includes("probability")),
"legacy Gaussian probability distribution should not be rendered as a time-series line",
@@ -73,11 +73,7 @@ export function runTests() {
);
assert(
probabilityRows.some((row) => row.key === "gaussian_mu" && row.value === "27.4°C"),
"tooltip should expose the Gaussian μ line when the purple probability overlay is present",
);
assert(
probabilityRows.some((row) => row.value.includes("26.5-27.5°C") && row.value.includes("42%")),
"tooltip should expose purple probability-band range and probability values",
probabilityRows.length === 0,
"temperature tooltip should not show Gaussian μ or probability-band rows",
);
}
@@ -2168,7 +2168,9 @@ function buildFullDayChartData(
return point;
});
const probabilityOverlay = buildLegacyGaussianProbabilityOverlay(row, hourly);
// Keep probability data in the payload for market views, but do not draw
// legacy Gaussian bands on the live temperature chart.
const probabilityOverlay = null;
return { data, series, probabilityOverlay };
}