Align Taipei settlement to NOAA RCTP across web and extension
This commit is contained in:
@@ -452,6 +452,10 @@ export function FutureForecastModal() {
|
||||
"--score-position": scorePosition,
|
||||
} as CSSProperties & { "--score-position": string };
|
||||
const weatherSummary = getWeatherSummary(detail, locale);
|
||||
const isTaipeiNoaa =
|
||||
store.selectedCity === "taipei" &&
|
||||
(detail.current?.settlement_source === "noaa" ||
|
||||
detail.current?.settlement_source_label === "NOAA");
|
||||
const marketMidpoint = formatMarketPercent(
|
||||
marketScan?.market_price ?? marketScan?.yes_token?.implied_probability,
|
||||
);
|
||||
@@ -551,11 +555,15 @@ export function FutureForecastModal() {
|
||||
? locale === "en-US"
|
||||
? "Hong Kong Observatory (HKO)"
|
||||
: "香港天文台 (HKO)"
|
||||
: settlementSourceCode === "cwa"
|
||||
: settlementSourceCode === "noaa"
|
||||
? locale === "en-US"
|
||||
? "Central Weather Administration (CWA)"
|
||||
: "交通部中央气象署 (CWA)"
|
||||
: risk.airport
|
||||
? "NOAA RCTP (Taiwan Taoyuan International Airport)"
|
||||
: "NOAA RCTP(台湾桃园国际机场)"
|
||||
: settlementSourceCode === "cwa"
|
||||
? locale === "en-US"
|
||||
? "Central Weather Administration (CWA)"
|
||||
: "交通部中央气象署 (CWA)"
|
||||
: risk.airport
|
||||
? `${risk.airport}${risk.icao ? ` (${risk.icao})` : ""}`
|
||||
: "--";
|
||||
|
||||
@@ -652,6 +660,24 @@ export function FutureForecastModal() {
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body future-modal-body">
|
||||
{isTaipeiNoaa && (
|
||||
<div
|
||||
style={{
|
||||
marginBottom: "16px",
|
||||
padding: "12px 14px",
|
||||
border: "1px solid rgba(56, 189, 248, 0.24)",
|
||||
borderRadius: "12px",
|
||||
background: "rgba(14, 165, 233, 0.08)",
|
||||
color: "var(--text-secondary)",
|
||||
fontSize: "13px",
|
||||
lineHeight: 1.6,
|
||||
}}
|
||||
>
|
||||
{locale === "en-US"
|
||||
? "Taipei now settles against NOAA RCTP (Taiwan Taoyuan International Airport). The market uses the highest rounded whole-degree Celsius reading in the Temp column after the day is finalized."
|
||||
: "台北当前按 NOAA RCTP(台湾桃园国际机场)结算。市场最终采用该日 Temp 列完成质控后的最高整度摄氏值,不按小数温度结算。"}
|
||||
</div>
|
||||
)}
|
||||
{isToday ? (
|
||||
<div className="future-v2-layout">
|
||||
<aside className="future-v2-left">
|
||||
|
||||
@@ -12,6 +12,7 @@ function HistoryChart() {
|
||||
const store = useDashboardStore();
|
||||
const { locale } = useI18n();
|
||||
const { data } = useHistoryData();
|
||||
const isTaipei = store.selectedCity === "taipei";
|
||||
const summary = useMemo(
|
||||
() => getHistorySummary(data, store.selectedDetail?.local_date),
|
||||
[data, store.selectedDetail?.local_date],
|
||||
@@ -33,7 +34,13 @@ function HistoryChart() {
|
||||
borderColor: "#f87171",
|
||||
borderWidth: 2,
|
||||
data: summary.actuals,
|
||||
label: locale === "en-US" ? "Observed High" : "实测最高温",
|
||||
label: isTaipei
|
||||
? locale === "en-US"
|
||||
? "NOAA Settled High (RCTP)"
|
||||
: "NOAA 结算最高温 (RCTP)"
|
||||
: locale === "en-US"
|
||||
? "Observed High"
|
||||
: "实测最高温",
|
||||
pointBackgroundColor: "#f87171",
|
||||
pointBorderColor: "#fff",
|
||||
pointHoverRadius: 7,
|
||||
@@ -135,7 +142,7 @@ function HistoryChart() {
|
||||
},
|
||||
type: "line",
|
||||
} satisfies ChartConfiguration<"line">;
|
||||
}, [hasBestBaseline, hasMgm, summary, locale]);
|
||||
}, [hasBestBaseline, hasMgm, isTaipei, summary, locale]);
|
||||
|
||||
if (!summary.recentData.length) return null;
|
||||
|
||||
@@ -152,6 +159,7 @@ export function HistoryModal() {
|
||||
const { data, error, isLoading, isOpen } = useHistoryData();
|
||||
const isPro = store.proAccess.subscriptionActive;
|
||||
const isProLoading = store.proAccess.loading;
|
||||
const isTaipei = store.selectedCity === "taipei";
|
||||
const summary = useMemo(
|
||||
() => getHistorySummary(data, store.selectedDetail?.local_date),
|
||||
[data, store.selectedDetail?.local_date],
|
||||
@@ -200,6 +208,24 @@ export function HistoryModal() {
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
{isTaipei && (
|
||||
<div
|
||||
style={{
|
||||
marginBottom: "16px",
|
||||
padding: "12px 14px",
|
||||
border: "1px solid rgba(56, 189, 248, 0.24)",
|
||||
borderRadius: "12px",
|
||||
background: "rgba(14, 165, 233, 0.08)",
|
||||
color: "var(--text-secondary)",
|
||||
fontSize: "13px",
|
||||
lineHeight: 1.6,
|
||||
}}
|
||||
>
|
||||
{t("lang") === "en-US"
|
||||
? "Taipei historical actuals are aligned to NOAA RCTP settlement rules: use the highest rounded whole-degree Celsius reading after the date is finalized."
|
||||
: "台北历史对账已按 NOAA RCTP 结算口径对齐:采用该日最终完成质控后的最高整度摄氏值。"}
|
||||
</div>
|
||||
)}
|
||||
<div className="history-stats">
|
||||
{isLoading ? (
|
||||
<span style={{ color: "var(--text-muted)" }}>
|
||||
|
||||
@@ -60,8 +60,8 @@ const CITY_SPECIFIC_SOURCES: Record<string, OfficialSourceLink[]> = {
|
||||
],
|
||||
taipei: [
|
||||
{
|
||||
label: "CWA 中央气象署",
|
||||
href: "https://www.cwa.gov.tw/V8/E/",
|
||||
label: "NOAA RCTP Timeseries",
|
||||
href: "https://www.weather.gov/wrh/timeseries?site=RCTP",
|
||||
kind: "agency",
|
||||
},
|
||||
{
|
||||
@@ -70,8 +70,8 @@ const CITY_SPECIFIC_SOURCES: Record<string, OfficialSourceLink[]> = {
|
||||
kind: "airport",
|
||||
},
|
||||
{
|
||||
label: "RCSS METAR",
|
||||
href: "https://aviationweather.gov/data/metar/?id=RCSS&decoded=1&taf=1",
|
||||
label: "RCTP METAR",
|
||||
href: "https://aviationweather.gov/data/metar/?id=RCTP&decoded=1&taf=1",
|
||||
kind: "metar",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -40,7 +40,7 @@ function getObservationSourceCode(detail: CityDetail): string {
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
if (city === "hong kong") return "hko";
|
||||
if (city === "taipei") return "cwa";
|
||||
if (city === "taipei") return "noaa";
|
||||
return "metar";
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ function getObservationSourceTag(detail: CityDetail): string {
|
||||
const code = getObservationSourceCode(detail);
|
||||
if (code === "hko") return "HKO";
|
||||
if (code === "cwa") return "CWA";
|
||||
if (code === "noaa") return "NOAA";
|
||||
if (code === "mgm") return "MGM";
|
||||
return "METAR";
|
||||
}
|
||||
@@ -239,7 +240,7 @@ export function getTemperatureChartData(
|
||||
const observationTag = getObservationSourceTag(detail);
|
||||
const observationCode = getObservationSourceCode(detail);
|
||||
const settlementSource =
|
||||
observationCode === "hko" || observationCode === "cwa";
|
||||
observationCode === "hko" || observationCode === "cwa" || observationCode === "noaa";
|
||||
const officialObservationSource =
|
||||
settlementSource
|
||||
? detail.settlement_today_obs?.length
|
||||
@@ -267,7 +268,11 @@ export function getTemperatureChartData(
|
||||
return `${icao} METAR`;
|
||||
})();
|
||||
const observationDisplayTag =
|
||||
settlementSource && shouldUseMetarFallback ? metarFallbackTag : observationTag;
|
||||
settlementSource && shouldUseMetarFallback
|
||||
? metarFallbackTag
|
||||
: observationCode === "noaa"
|
||||
? "NOAA RCTP"
|
||||
: observationTag;
|
||||
|
||||
const metarPoints = new Array(times.length).fill(null);
|
||||
observationSource.forEach((item) => {
|
||||
@@ -361,6 +366,12 @@ export function getTemperatureChartData(
|
||||
? `Official ${observationTag} feed is sparse today, so the continuous observation line switches to ${metarFallbackTag}.`
|
||||
: `今日官方 ${observationTag} 点位较稀疏,连续实测线改用 ${metarFallbackTag}。`,
|
||||
);
|
||||
} else if (observationCode === "noaa") {
|
||||
legendParts.push(
|
||||
isEnglish(locale)
|
||||
? "Taipei settles on NOAA RCTP using the finalized highest rounded whole-degree Celsius reading; the plotted line is a settlement reference."
|
||||
: "台北按 NOAA RCTP 最终完成质控后的最高整度摄氏值结算;图中曲线仅作为结算参考线。",
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -374,9 +385,14 @@ export function getTemperatureChartData(
|
||||
offset,
|
||||
temps,
|
||||
},
|
||||
observationLabel: isEnglish(locale)
|
||||
? `${observationDisplayTag} Observation`
|
||||
: `${observationDisplayTag} 实况`,
|
||||
observationLabel:
|
||||
observationCode === "noaa" && !shouldUseMetarFallback
|
||||
? isEnglish(locale)
|
||||
? `${observationDisplayTag} Settlement Reference`
|
||||
: `${observationDisplayTag} 结算参考`
|
||||
: isEnglish(locale)
|
||||
? `${observationDisplayTag} Observation`
|
||||
: `${observationDisplayTag} 实况`,
|
||||
legendText: legendParts.join(" | "),
|
||||
max,
|
||||
min,
|
||||
@@ -1416,7 +1432,7 @@ export function getCityProfileStats(detail: CityDetail, locale: Locale = "zh-CN"
|
||||
const current = detail.current || {};
|
||||
const nearbyCount = Array.isArray(detail.mgm_nearby) ? detail.mgm_nearby.length : 0;
|
||||
const sourceCode = getObservationSourceCode(detail);
|
||||
const isOfficialSource = sourceCode === "hko" || sourceCode === "cwa";
|
||||
const isOfficialSource = sourceCode === "hko" || sourceCode === "cwa" || sourceCode === "noaa";
|
||||
|
||||
const sourceDisplay = (() => {
|
||||
if (sourceCode === "hko") {
|
||||
@@ -1429,6 +1445,11 @@ export function getCityProfileStats(detail: CityDetail, locale: Locale = "zh-CN"
|
||||
? "Central Weather Administration (CWA)"
|
||||
: "交通部中央气象署 (CWA)";
|
||||
}
|
||||
if (sourceCode === "noaa") {
|
||||
return isEnglish(locale)
|
||||
? "NOAA RCTP (Taiwan Taoyuan)"
|
||||
: "NOAA RCTP(台湾桃园国际机场)";
|
||||
}
|
||||
const tag = getObservationSourceTag(detail);
|
||||
if (sourceCode === "mgm") {
|
||||
return isEnglish(locale) ? `MGM (${tag})` : `MGM (${tag})`;
|
||||
@@ -1491,7 +1512,7 @@ export function getSettlementRiskNarrative(
|
||||
) {
|
||||
const risk = detail.risk || {};
|
||||
const sourceCode = getObservationSourceCode(detail);
|
||||
const stationTerm = sourceCode === "hko" || sourceCode === "cwa"
|
||||
const stationTerm = sourceCode === "hko" || sourceCode === "cwa" || sourceCode === "noaa"
|
||||
? isEnglish(locale)
|
||||
? "settlement reference station"
|
||||
: "结算参考站"
|
||||
|
||||
@@ -52,7 +52,7 @@ const MESSAGES: Record<Locale, Record<string, string>> = {
|
||||
"guide.title": "📎 PolyWeather 系统技术说明",
|
||||
"guide.closeAria": "关闭技术说明",
|
||||
"guide.footer":
|
||||
"数据源以 METAR、香港天文台(HKO)、中央气象署(CWA)、Turkish MGM、Open-Meteo、weather.gov 为主。",
|
||||
"数据源以 METAR、香港天文台(HKO)、NOAA RCTP、Turkish MGM、Open-Meteo、weather.gov 为主。",
|
||||
|
||||
"history.title": "📊 历史准确率对账 - {city}",
|
||||
"history.closeAria": "关闭历史对账",
|
||||
@@ -214,7 +214,7 @@ const MESSAGES: Record<Locale, Record<string, string>> = {
|
||||
"guide.title": "📎 PolyWeather Technical Overview",
|
||||
"guide.closeAria": "Close technical overview",
|
||||
"guide.footer":
|
||||
"Primary data sources are METAR, Hong Kong Observatory (HKO), CWA (Taiwan), Turkish MGM, Open-Meteo, and weather.gov.",
|
||||
"Primary data sources are METAR, Hong Kong Observatory (HKO), NOAA RCTP, Turkish MGM, Open-Meteo, and weather.gov.",
|
||||
|
||||
"history.title": "📊 Historical Reconciliation - {city}",
|
||||
"history.closeAria": "Close history reconciliation",
|
||||
|
||||
Reference in New Issue
Block a user