Update landing and weather observation policies

This commit is contained in:
2569718930@qq.com
2026-05-29 20:46:35 +08:00
parent 2f039abb2c
commit 5228d2b3fd
8 changed files with 469 additions and 279 deletions
@@ -192,8 +192,34 @@ export function runTests() {
"settlement/HKO observations should be visible by default",
);
assert(
__isTemperatureSeriesVisibleByDefaultForTest("guangzhou", "metar"),
"METAR observations should be visible by default",
!__isTemperatureSeriesVisibleByDefaultForTest("guangzhou", "metar"),
"METAR observations should be hidden by default outside Hong Kong and Shenzhen",
);
assert(
!activeDefaultSeries.some((item) => item.key === "metar"),
"non-Hong Kong/Shenzhen airport METAR observations should not affect the active chart series by default",
);
assert(
__getVisibleTemperatureSeriesForTest("guangzhou", series, { metar: true }).some(
(item) => item.key === "metar",
),
"users should still be able to enable the hidden airport METAR series from the legend",
);
assert(
__isTemperatureSeriesVisibleByDefaultForTest("hong kong", "metar"),
"Hong Kong METAR/HKO observations should remain visible by default",
);
assert(
__isTemperatureSeriesVisibleByDefaultForTest("Lau Fau Shan", "madis"),
"Lau Fau Shan HKO observations should remain visible by default",
);
assert(
__isTemperatureSeriesVisibleByDefaultForTest("shenzhen", "madis"),
"Shenzhen HKO observations should remain visible by default",
);
assert(
!__isTemperatureSeriesVisibleByDefaultForTest("new york", "madis"),
"non-Hong Kong/Shenzhen airport-primary observations should be hidden by default",
);
assert(
!__isTemperatureSeriesVisibleByDefaultForTest("guangzhou", "model_curve_ECMWF"),
@@ -1162,6 +1188,50 @@ export function runTests() {
"long-lived chart with only one fresh observation should keep a renderable current reference line instead of an invisible single-point series",
);
const istanbulMgmOnlySeries = __buildTemperatureChartDataForTest(
{
city: "istanbul",
local_date: "2026-05-29",
local_time: "15:10",
tz_offset_seconds: 3 * 60 * 60,
current_temp: 18,
current_max_so_far: 18,
airport: "LTFM",
} as any,
{
localTime: "15:10",
times: [],
temps: [],
airportPrimary: {
source_code: "mgm",
source_label: "MGM",
temp: 18.2,
obs_time: "2026-05-29T12:10:00Z",
},
airportCurrent: {
source_code: "metar",
source_label: "METAR",
temp: 17,
obs_time: "14:50",
},
airportPrimaryTodayObs: [
{ time: "2026-05-29T12:00:00Z", temp: 18 },
{ time: "2026-05-29T12:05:00Z", temp: 18.1 },
],
} as any,
"1D",
);
const istanbulMgmSeries = seriesByKey(istanbulMgmOnlySeries.series as any, "madis") as any;
assert(istanbulMgmSeries?.label === "MGM", "Istanbul airport-primary series should be labeled MGM");
assert(
istanbulMgmSeries.values.some((value: number | null) => value === 18.2),
"MGM series should append the latest MGM airport-primary observation",
);
assert(
!istanbulMgmSeries.values.some((value: number | null) => value === 17),
"MGM series should not mix METAR airportCurrent points into the MGM airport-station line",
);
const chengduMergedHourly = __mergePatchIntoHourlyForTest(
{
localTime: "05:25",
@@ -86,6 +86,10 @@ function isTemperatureSeriesVisibleByDefault(city: string, seriesKey: string) {
if (seriesKey.startsWith("model_curve_")) {
return normalizeCityKey(city) === "paris" && seriesKey === "model_curve_AROME HD";
}
if (seriesKey === "metar" || seriesKey === "madis") {
const cityKey = normalizeCityKey(city);
return cityKey === "hongkong" || cityKey === "laufaushan" || cityKey === "shenzhen";
}
return true;
}
@@ -606,6 +610,24 @@ function appendLatestAirportObservation(
return merged;
}
function isMgmAirportPrimary(hourly: HourlyForecast) {
const primary = hourly?.airportPrimary;
const sourceTokens = [
primary?.source_code,
primary?.source_label,
(primary as any)?.source,
].map((value) => String(value || "").toLowerCase());
return sourceTokens.some((value) => value === "mgm" || value.includes("turkey_mgm"));
}
function airportPrimaryObservationPoints(hourly: HourlyForecast) {
return appendLatestAirportObservation(
hourly?.airportPrimaryTodayObs,
hourly?.airportPrimary,
...(isMgmAirportPrimary(hourly) ? [] : [hourly?.airportCurrent]),
);
}
function seriesStats(values: Array<number | null>) {
const nums = values.filter((v): v is number => validNumber(v) !== null);
const latest = nums.length ? nums[nums.length - 1] : null;
@@ -680,7 +702,7 @@ function getObservationDisplayMetrics(
const settlementObs = normObs(hourly?.settlementTodayObs || row?.settlement_today_obs || row?.metar_context?.settlement_today_obs, tzOffset, MAX_OBS_POINTS, localDateStr);
const metarObs = normObs(hourly?.metarTodayObs || row?.metar_today_obs || row?.metar_context?.today_obs || row?.metar_recent_obs || row?.metar_context?.recent_obs, tzOffset, MAX_OBS_POINTS, localDateStr);
const madisObs = normObs(
appendLatestAirportObservation(hourly?.airportPrimaryTodayObs, hourly?.airportPrimary, hourly?.airportCurrent),
airportPrimaryObservationPoints(hourly),
tzOffset,
MAX_OBS_POINTS,
localDateStr,
@@ -1611,7 +1633,7 @@ function buildFullDayChartData(
);
const madisObs = filterTimelinePointsToLocalDay(
normObs(
appendLatestAirportObservation(hourly?.airportPrimaryTodayObs, hourly?.airportPrimary, hourly?.airportCurrent),
airportPrimaryObservationPoints(hourly),
tzOffset,
MAX_OBS_POINTS,
localDateStr,