feat: implement real-time observation patch normalization and live temperature threshold visualization logic
This commit is contained in:
@@ -15,7 +15,7 @@ import {
|
||||
} from "recharts";
|
||||
import type { ScanOpportunityRow } from "@/lib/dashboard-types";
|
||||
import { TemperatureTooltipContent } from "@/components/dashboard/scan-terminal/TemperatureTooltipContent";
|
||||
import type { EvidenceSeries } from "@/components/dashboard/scan-terminal/temperature-chart-logic";
|
||||
import type { EvidenceSeries, ProbabilityOverlay } from "@/components/dashboard/scan-terminal/temperature-chart-logic";
|
||||
|
||||
type CityThreshold = {
|
||||
threshold: number;
|
||||
@@ -32,6 +32,7 @@ export function TemperatureChartCanvas({
|
||||
cityThresholds,
|
||||
chartSeries,
|
||||
activeSeries,
|
||||
probabilityOverlay,
|
||||
zoomedData,
|
||||
chartDomain,
|
||||
intDegreeTicks,
|
||||
@@ -55,6 +56,7 @@ export function TemperatureChartCanvas({
|
||||
cityThresholds: CityThreshold[];
|
||||
chartSeries: EvidenceSeries[];
|
||||
activeSeries: EvidenceSeries[];
|
||||
probabilityOverlay: ProbabilityOverlay | null;
|
||||
zoomedData: Array<Record<string, any>>;
|
||||
chartDomain: [number, number] | ["auto", "auto"];
|
||||
intDegreeTicks: number[] | null;
|
||||
@@ -160,6 +162,30 @@ export function TemperatureChartCanvas({
|
||||
<span>{isEn ? "Show Runway Details" : "显示跑道明细"}</span>
|
||||
</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="relative min-h-[220px] flex-1">
|
||||
{canRenderChart && (
|
||||
@@ -191,6 +217,16 @@ export function TemperatureChartCanvas({
|
||||
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
|
||||
@@ -213,6 +249,20 @@ export function TemperatureChartCanvas({
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{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 }}
|
||||
|
||||
Reference in New Issue
Block a user