fix: align hong kong compact hko stat

This commit is contained in:
2569718930@qq.com
2026-06-06 17:01:39 +08:00
parent 2766b6463c
commit 89f77b8db4
4 changed files with 103 additions and 3 deletions
@@ -31,6 +31,7 @@ import {
normObs, normObs,
prefersHighFrequencyRunwayResolution, prefersHighFrequencyRunwayResolution,
readSessionCache, readSessionCache,
selectCompactSecondaryTemp,
selectDisplayRunwayTemp, selectDisplayRunwayTemp,
seedHourlyForecastFromRow, seedHourlyForecastFromRow,
shouldPollLiveChart, shouldPollLiveChart,
@@ -635,6 +636,7 @@ export function LiveTemperatureThresholdChart({
}, [chartSeries, userToggledKeys, city, showRunwayDetails]); }, [chartSeries, userToggledKeys, city, showRunwayDetails]);
const { const {
isHKO,
isShenzhen, isShenzhen,
metarHeaderLabel, metarHeaderLabel,
metarHighLabel, metarHighLabel,
@@ -642,11 +644,17 @@ export function LiveTemperatureThresholdChart({
runwayHighLabel, runwayHighLabel,
} = useMemo(() => getLiveObservationLabels(row, chartHourly), [row, chartHourly]); } = useMemo(() => getLiveObservationLabels(row, chartHourly), [row, chartHourly]);
const { currentRunwayTemp, observedHighMetar, observedHighRunway } = useMemo( const { currentMetarTemp, currentRunwayTemp, observedHighMetar, observedHighRunway } = useMemo(
() => getObservationDisplayMetrics(row, chartHourly, settlementPlate), () => getObservationDisplayMetrics(row, chartHourly, settlementPlate),
[row, chartHourly, settlementPlate], [row, chartHourly, settlementPlate],
); );
const displayRunwayTemp = selectDisplayRunwayTemp(liveTemp, currentRunwayTemp, hasRunwayData); const displayRunwayTemp = selectDisplayRunwayTemp(liveTemp, currentRunwayTemp, hasRunwayData);
const displayMetarTemp = selectCompactSecondaryTemp({
isHKO,
isShenzhen,
displayMetarTemp: currentMetarTemp,
observedHighMetar,
});
const wundergroundDailyHigh = getWundergroundDailyHigh(chartHourly); const wundergroundDailyHigh = getWundergroundDailyHigh(chartHourly);
const localDateStr = chartLocalDate || new Date().toISOString().slice(0, 10); const localDateStr = chartLocalDate || new Date().toISOString().slice(0, 10);
@@ -805,6 +813,7 @@ export function LiveTemperatureThresholdChart({
visible_series: activeSeries.map((item) => item.key), visible_series: activeSeries.map((item) => item.key),
live_temp: liveTemp, live_temp: liveTemp,
current_runway_temp: currentRunwayTemp, current_runway_temp: currentRunwayTemp,
current_metar_temp: currentMetarTemp,
observed_high_metar: observedHighMetar, observed_high_metar: observedHighMetar,
observed_high_runway: observedHighRunway, observed_high_runway: observedHighRunway,
}); });
@@ -814,6 +823,7 @@ export function LiveTemperatureThresholdChart({
chartSeries, chartSeries,
city, city,
compact, compact,
currentMetarTemp,
currentRunwayTemp, currentRunwayTemp,
detailError, detailError,
hasRunwayData, hasRunwayData,
@@ -960,6 +970,7 @@ export function LiveTemperatureThresholdChart({
metarHighLabel={metarHighLabel} metarHighLabel={metarHighLabel}
isShenzhen={isShenzhen} isShenzhen={isShenzhen}
displayRunwayTemp={displayRunwayTemp} displayRunwayTemp={displayRunwayTemp}
displayMetarTemp={displayMetarTemp}
observedHighMetar={observedHighMetar} observedHighMetar={observedHighMetar}
observedHighRunway={observedHighRunway} observedHighRunway={observedHighRunway}
wundergroundDailyHigh={wundergroundDailyHigh} wundergroundDailyHigh={wundergroundDailyHigh}
@@ -1034,4 +1045,5 @@ export const __getInitialDetailLoadDelayMsForTest = getInitialDetailLoadDelayMs;
export const __shouldFetchCityDetailForChartForTest = shouldFetchCityDetailForChart; export const __shouldFetchCityDetailForChartForTest = shouldFetchCityDetailForChart;
export const __shouldPollLiveChartForTest = shouldPollLiveChart; export const __shouldPollLiveChartForTest = shouldPollLiveChart;
export const __mergePatchIntoHourlyForTest = mergePatchIntoHourly; export const __mergePatchIntoHourlyForTest = mergePatchIntoHourly;
export const __selectCompactSecondaryTempForTest = selectCompactSecondaryTemp;
export const __selectDisplayRunwayTempForTest = selectDisplayRunwayTemp; export const __selectDisplayRunwayTempForTest = selectDisplayRunwayTemp;
@@ -76,6 +76,7 @@ export function TemperatureStatsBars({
metarHighLabel, metarHighLabel,
isShenzhen, isShenzhen,
displayRunwayTemp, displayRunwayTemp,
displayMetarTemp,
observedHighMetar, observedHighMetar,
observedHighRunway, observedHighRunway,
wundergroundDailyHigh, wundergroundDailyHigh,
@@ -97,6 +98,7 @@ export function TemperatureStatsBars({
metarHighLabel: string; metarHighLabel: string;
isShenzhen: boolean; isShenzhen: boolean;
displayRunwayTemp: number | null; displayRunwayTemp: number | null;
displayMetarTemp: number | null;
observedHighMetar: number | null; observedHighMetar: number | null;
observedHighRunway: number | null; observedHighRunway: number | null;
wundergroundDailyHigh: number | null; wundergroundDailyHigh: number | null;
@@ -129,7 +131,7 @@ export function TemperatureStatsBars({
<span className="text-slate-300">|</span> <span className="text-slate-300">|</span>
<span className="font-semibold text-slate-500"> <span className="font-semibold text-slate-500">
{labels.compactSecondary}:{" "} {labels.compactSecondary}:{" "}
<strong className="text-blue-600 font-mono">{temp(observedHighMetar, tempSymbol)}</strong> <strong className="text-blue-600 font-mono">{temp(displayMetarTemp, tempSymbol)}</strong>
</span> </span>
</div> </div>
) : ( ) : (
@@ -9,6 +9,7 @@ import {
__getVisibleTemperatureSeriesForTest, __getVisibleTemperatureSeriesForTest,
__isTemperatureSeriesVisibleByDefaultForTest, __isTemperatureSeriesVisibleByDefaultForTest,
__mergePatchIntoHourlyForTest, __mergePatchIntoHourlyForTest,
__selectCompactSecondaryTempForTest,
__selectDisplayRunwayTempForTest, __selectDisplayRunwayTempForTest,
} from "@/components/dashboard/scan-terminal/LiveTemperatureThresholdChart"; } from "@/components/dashboard/scan-terminal/LiveTemperatureThresholdChart";
import { __buildTemperatureTooltipRowsForTest } from "@/components/dashboard/scan-terminal/TemperatureTooltipContent"; import { __buildTemperatureTooltipRowsForTest } from "@/components/dashboard/scan-terminal/TemperatureTooltipContent";
@@ -976,6 +977,68 @@ export function runTests() {
"runway header should prefer runway-history current temp even when the latest detail payload lacks runway_obs snapshot", "runway header should prefer runway-history current temp even when the latest detail payload lacks runway_obs snapshot",
); );
const hongKongMetrics = __getObservationDisplayMetricsForTest(
{
city: "hong kong",
local_date: "2026-06-06",
local_time: "16:48",
tz_offset_seconds: 8 * 60 * 60,
current_temp: 30.4,
current_max_so_far: 30.4,
temp_symbol: "°C",
} as any,
{
localTime: "16:48",
times: ["00:00", "12:00", "18:00", "23:00"],
temps: [28.0, 31.0, 29.0, 28.0],
settlementTodayObs: [
{ time: "14:00", temp: 30.4 },
{ time: "16:40", temp: 28.1 },
],
airportPrimary: {
temp: 28.7,
obs_time: "2026-06-06T16:48:00+08:00",
source_code: "cowin_obs",
source_label: "CoWIN 6087",
},
airportPrimaryTodayObs: [
{ time: "16:47", temp: 28.6 },
{ time: "16:48", temp: 28.7 },
],
} as any,
null,
);
assert(
hongKongMetrics.currentRunwayTemp === 28.7,
"Hong Kong primary compact stat should stay on the latest CoWIN reference-station point",
);
assert(
(hongKongMetrics as any).currentMetarTemp === 28.1,
"Hong Kong secondary compact stat should have the latest HKO point separate from the HKO daily high",
);
assert(
hongKongMetrics.observedHighMetar === 30.4,
"Hong Kong HKO daily high should still be available for expanded daily-high summaries",
);
assert(
__selectCompactSecondaryTempForTest({
isHKO: true,
isShenzhen: false,
displayMetarTemp: (hongKongMetrics as any).currentMetarTemp,
observedHighMetar: hongKongMetrics.observedHighMetar,
}) === 28.1,
"Hong Kong compact HKO stat should render the latest HKO point, not the HKO daily high",
);
assert(
__selectCompactSecondaryTempForTest({
isHKO: false,
isShenzhen: false,
displayMetarTemp: 72.0,
observedHighMetar: 73.9,
}) === 73.9,
"non-HKO compact secondary stat should keep the existing daily-high behavior",
);
const wuhanRunwayChart = __buildTemperatureChartDataForTest( const wuhanRunwayChart = __buildTemperatureChartDataForTest(
{ {
city: "wuhan", city: "wuhan",
@@ -845,9 +845,14 @@ function getObservationDisplayMetrics(
null; null;
} }
const currentMetarTemp =
latestSettlement ??
latestMetar ??
airportCurrentTemp ??
null;
const observedHighMetar = airportHigh ?? highSettlement ?? highMetar ?? rowMetarHigh ?? null; const observedHighMetar = airportHigh ?? highSettlement ?? highMetar ?? rowMetarHigh ?? null;
return { currentRunwayTemp, observedHighMetar, observedHighRunway }; return { currentMetarTemp, currentRunwayTemp, observedHighMetar, observedHighRunway };
} }
function selectDisplayRunwayTemp( function selectDisplayRunwayTemp(
@@ -859,6 +864,23 @@ function selectDisplayRunwayTemp(
return liveTemp; return liveTemp;
} }
function selectCompactSecondaryTemp({
isHKO,
isShenzhen,
displayMetarTemp,
observedHighMetar,
}: {
isHKO: boolean;
isShenzhen: boolean;
displayMetarTemp: number | null;
observedHighMetar: number | null;
}) {
if (isHKO && !isShenzhen && displayMetarTemp !== null) {
return displayMetarTemp;
}
return observedHighMetar;
}
function isSettlementRunway(row: ScanOpportunityRow | null, rwy: string) { function isSettlementRunway(row: ScanOpportunityRow | null, rwy: string) {
const cityKey = normalizeCityKey(row?.city); const cityKey = normalizeCityKey(row?.city);
const settlementPairs = SETTLEMENT_RUNWAY_PAIRS[cityKey] || []; const settlementPairs = SETTLEMENT_RUNWAY_PAIRS[cityKey] || [];
@@ -2485,6 +2507,7 @@ export {
normalizeCityKey, normalizeCityKey,
prefersHighFrequencyRunwayResolution, prefersHighFrequencyRunwayResolution,
readSessionCache, readSessionCache,
selectCompactSecondaryTemp,
selectDisplayRunwayTemp, selectDisplayRunwayTemp,
seedHourlyForecastFromRow, seedHourlyForecastFromRow,
seriesStats, seriesStats,