Clarify METAR anchor labeling in weather forecasts
This commit is contained in:
@@ -2949,10 +2949,33 @@
|
||||
|
||||
.root :global(.future-v2-meteorology-copy) {
|
||||
display: grid;
|
||||
gap: 9px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.root :global(.future-v2-anchor-row) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.root :global(.future-v2-anchor-source) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 24px;
|
||||
padding: 4px 8px;
|
||||
border: 1px solid rgba(34, 211, 238, 0.28);
|
||||
border-radius: 8px;
|
||||
background: rgba(8, 47, 73, 0.36);
|
||||
color: #67e8f9;
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
line-height: 1.25;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.root :global(.future-v2-meteorology-copy h3) {
|
||||
margin: 0;
|
||||
color: #f8fafc;
|
||||
@@ -2962,6 +2985,14 @@
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.root :global(.future-v2-anchor-rule) {
|
||||
margin: 0;
|
||||
max-width: 860px;
|
||||
color: var(--text-muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.root :global(.future-v2-meteorology-meta) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -2981,6 +3012,73 @@
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.root :global(.future-v2-decision-rail) {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 0.88fr) minmax(0, 1.35fr);
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.root :global(.future-v2-decision-anchor) {
|
||||
min-width: 0;
|
||||
padding: 12px;
|
||||
border: 1px solid rgba(96, 165, 250, 0.2);
|
||||
border-radius: 8px;
|
||||
background: rgba(2, 6, 23, 0.5);
|
||||
}
|
||||
|
||||
.root :global(.future-v2-decision-anchor span),
|
||||
.root :global(.future-v2-decision-grid span) {
|
||||
display: block;
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.root :global(.future-v2-decision-anchor strong) {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
color: #f8fafc;
|
||||
font-size: 14px;
|
||||
font-weight: 800;
|
||||
line-height: 1.35;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.root :global(.future-v2-decision-anchor small) {
|
||||
display: block;
|
||||
margin-top: 6px;
|
||||
color: #67e8f9;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.root :global(.future-v2-decision-grid) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.root :global(.future-v2-decision-grid div) {
|
||||
min-width: 0;
|
||||
padding: 10px;
|
||||
border: 1px solid rgba(34, 211, 238, 0.18);
|
||||
border-radius: 8px;
|
||||
background: rgba(2, 6, 23, 0.5);
|
||||
}
|
||||
|
||||
.root :global(.future-v2-decision-grid strong) {
|
||||
display: block;
|
||||
margin-top: 7px;
|
||||
color: #67e8f9;
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.root :global(.future-v2-meteorology-paths) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
@@ -4127,6 +4225,14 @@
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.root :global(.future-v2-decision-rail) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.root :global(.future-v2-decision-grid) {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.root :global(.future-v2-layout) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
@@ -4160,6 +4266,15 @@
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.root :global(.future-v2-anchor-row) {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.root :global(.future-v2-decision-grid) {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.root :global(.future-v2-meteorology-copy h3) {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@@ -1197,6 +1197,47 @@ export function FutureForecastModal() {
|
||||
String(intradayMeteorology.peak_window || "").trim() ||
|
||||
paceView?.peakWindowText ||
|
||||
"--";
|
||||
const settlementSourceCode = String(
|
||||
detail.current?.settlement_source || "",
|
||||
).trim().toLowerCase();
|
||||
const settlementStationCode = String(
|
||||
detail.current?.station_code || detail.risk?.icao || "",
|
||||
)
|
||||
.trim()
|
||||
.toUpperCase();
|
||||
const settlementStationName =
|
||||
String(detail.current?.station_name || detail.risk?.airport || "").trim() ||
|
||||
settlementStationCode ||
|
||||
(locale === "en-US" ? "Anchor station" : "锚点站");
|
||||
const airportMetarAnchor =
|
||||
settlementSourceCode === "metar" ||
|
||||
settlementSourceCode === "wunderground" ||
|
||||
Boolean(settlementStationCode && /^[A-Z]{4}$/.test(settlementStationCode));
|
||||
const anchorSourceLabel = airportMetarAnchor
|
||||
? settlementStationCode
|
||||
? `${settlementStationCode} METAR`
|
||||
: "METAR"
|
||||
: detail.current?.settlement_source_label ||
|
||||
detail.current?.settlement_source ||
|
||||
(locale === "en-US" ? "Official observation" : "官方观测");
|
||||
const anchorRuleText = airportMetarAnchor
|
||||
? locale === "en-US"
|
||||
? `Airport contract anchor: use the ${anchorSourceLabel} reports. Wunderground is only a history display page when present.`
|
||||
: `机场合约锚点:以 ${anchorSourceLabel} 报文为准;若出现 Wunderground,它只是历史展示页面。`
|
||||
: locale === "en-US"
|
||||
? `Official anchor: use ${anchorSourceLabel} observations for this contract.`
|
||||
: `官方锚点:该合约按 ${anchorSourceLabel} 观测口径判断。`;
|
||||
const nextObservationLabel = airportMetarAnchor
|
||||
? locale === "en-US"
|
||||
? "Next METAR watch"
|
||||
: "下一次 METAR 观察"
|
||||
: locale === "en-US"
|
||||
? "Next anchor watch"
|
||||
: "下一次锚点观察";
|
||||
const gapToBaseText =
|
||||
gapToBaseBucket == null
|
||||
? "--"
|
||||
: `${gapToBaseBucket.toFixed(1)}${detail.temp_symbol || "°C"}`;
|
||||
const syncStatusItems = [
|
||||
{
|
||||
key: "base",
|
||||
@@ -1334,35 +1375,50 @@ export function FutureForecastModal() {
|
||||
{isToday && (
|
||||
<section className="future-v2-meteorology-brief">
|
||||
<div className="future-v2-meteorology-copy">
|
||||
<div className="modal-section-kicker">
|
||||
{locale === "en-US" ? "Professional meteorology read" : "专业气象判断"}
|
||||
<div className="future-v2-anchor-row">
|
||||
<div className="modal-section-kicker">
|
||||
{locale === "en-US" ? "Professional meteorology read" : "专业气象判断"}
|
||||
</div>
|
||||
<span className="future-v2-anchor-source">{anchorSourceLabel}</span>
|
||||
</div>
|
||||
<h3>{meteorologyHeadline}</h3>
|
||||
<p className="future-v2-anchor-rule">{anchorRuleText}</p>
|
||||
<div className="future-v2-meteorology-meta">
|
||||
<span>
|
||||
{locale === "en-US" ? "Confidence" : "置信度"} ·{" "}
|
||||
{formatConfidenceLabel(intradayMeteorology.confidence, locale)}
|
||||
</span>
|
||||
<span>
|
||||
{locale === "en-US" ? "Peak window" : "峰值窗口"} · {peakWindowText}
|
||||
{locale === "en-US" ? "Path state" : "路径状态"} · {pathStatus}
|
||||
</span>
|
||||
<span>
|
||||
{locale === "en-US" ? "Next observation" : "下一观测"} · {nextObservationTime}
|
||||
{nextObservationLabel} · {nextObservationTime}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="future-v2-meteorology-paths">
|
||||
<div>
|
||||
<span>{locale === "en-US" ? "Base case" : "基准路径"}</span>
|
||||
<strong>{baseCaseBucket || "--"}</strong>
|
||||
<div className="future-v2-decision-rail">
|
||||
<div className="future-v2-decision-anchor">
|
||||
<span>{locale === "en-US" ? "Anchor" : "锚点"}</span>
|
||||
<strong>{settlementStationName}</strong>
|
||||
<small>{anchorSourceLabel}</small>
|
||||
</div>
|
||||
<div>
|
||||
<span>{locale === "en-US" ? "Upside" : "上修路径"}</span>
|
||||
<strong>{intradayMeteorology.upside_bucket || "--"}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>{locale === "en-US" ? "Downside" : "下修路径"}</span>
|
||||
<strong>{intradayMeteorology.downside_bucket || "--"}</strong>
|
||||
<div className="future-v2-decision-grid">
|
||||
<div>
|
||||
<span>{locale === "en-US" ? "Base" : "基准"}</span>
|
||||
<strong>{baseCaseBucket || "--"}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>{locale === "en-US" ? "Upside" : "上修"}</span>
|
||||
<strong>{intradayMeteorology.upside_bucket || "--"}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>{locale === "en-US" ? "Downside" : "下修"}</span>
|
||||
<strong>{intradayMeteorology.downside_bucket || "--"}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>{locale === "en-US" ? "Gap to base" : "距基准还差"}</span>
|
||||
<strong>{gapToBaseText}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -1653,8 +1709,8 @@ export function FutureForecastModal() {
|
||||
</div>
|
||||
<h3>
|
||||
{locale === "en-US"
|
||||
? "Today's temperature forecast (obs + market)"
|
||||
: "今日气温预测(观测 + 市场)"}
|
||||
? "Today's temperature path (anchor obs + models)"
|
||||
: "今日气温路径(锚点观测 + 模型)"}
|
||||
</h3>
|
||||
</div>
|
||||
<DailyTemperatureChart dateStr={dateStr} />
|
||||
@@ -1740,8 +1796,12 @@ export function FutureForecastModal() {
|
||||
? confirmationRules
|
||||
: [
|
||||
locale === "en-US"
|
||||
? "Keep watching the next settlement-source observation."
|
||||
: "继续观察下一次结算源报文。",
|
||||
? airportMetarAnchor
|
||||
? "Keep watching the next anchor METAR report."
|
||||
: "Keep watching the next official anchor observation."
|
||||
: airportMetarAnchor
|
||||
? "继续观察下一次锚点 METAR 报文。"
|
||||
: "继续观察下一次官方锚点观测。",
|
||||
]
|
||||
).map((rule, index) => (
|
||||
<li key={`${rule}-${index}`}>{rule}</li>
|
||||
|
||||
@@ -158,7 +158,22 @@ export function HeroSummary() {
|
||||
const { weatherIcon, weatherText } = getWeatherSummary(data, locale);
|
||||
const metaItems = getHeroMetaItems(data, locale);
|
||||
const current = data.current || {};
|
||||
const settlementSource = String(current.settlement_source || "metar").toUpperCase();
|
||||
const settlementSourceCode = String(current.settlement_source || "metar")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
const settlementIcao = String(
|
||||
current.station_code || data.risk?.icao || "",
|
||||
)
|
||||
.trim()
|
||||
.toUpperCase();
|
||||
const settlementSource =
|
||||
settlementSourceCode === "wunderground"
|
||||
? settlementIcao
|
||||
? `${settlementIcao} METAR`
|
||||
: "METAR"
|
||||
: String(current.settlement_source_label || current.settlement_source || "METAR")
|
||||
.trim()
|
||||
.toUpperCase();
|
||||
const isMax =
|
||||
current.max_so_far != null &&
|
||||
current.temp != null &&
|
||||
@@ -198,8 +213,8 @@ export function HeroSummary() {
|
||||
<div className="hero-item">
|
||||
<span className="label">
|
||||
{locale === "en-US"
|
||||
? `${settlementSource} Settlement Ref`
|
||||
: `${settlementSource} 结算参考`}
|
||||
? `${settlementSource} Anchor`
|
||||
: `${settlementSource} 锚点`}
|
||||
</span>
|
||||
<span className="value highlight">
|
||||
{current.wu_settlement != null
|
||||
|
||||
@@ -106,7 +106,12 @@ function getObservationSourceTag(detail: CityDetail): string {
|
||||
if (code === "hko") return "HKO";
|
||||
if (code === "cwa") return "CWA";
|
||||
if (code === "noaa") return "NOAA";
|
||||
if (code === "wunderground") return "WUNDERGROUND";
|
||||
if (code === "wunderground") {
|
||||
const icao = String(detail.risk?.icao || detail.current?.station_code || "")
|
||||
.trim()
|
||||
.toUpperCase();
|
||||
return icao ? `${icao} METAR` : "METAR";
|
||||
}
|
||||
if (code === "mgm") return "MGM";
|
||||
return "METAR";
|
||||
}
|
||||
@@ -819,7 +824,7 @@ export function getTemperatureChartData(
|
||||
: observationCode === "wunderground" && usingMetarObservationSource
|
||||
? metarFallbackTag
|
||||
: observationCode === "wunderground"
|
||||
? `WU ${String(detail.risk?.icao || "").trim().toUpperCase() || "STATION"}`
|
||||
? metarFallbackTag
|
||||
: useSettlementObservationSource && shouldUseMetarFallback
|
||||
? metarFallbackTag
|
||||
: observationCode === "noaa"
|
||||
@@ -3190,8 +3195,7 @@ export function getCityProfileStats(detail: CityDetail, locale: Locale = "zh-CN"
|
||||
const isOfficialSource =
|
||||
sourceCode === "hko" ||
|
||||
sourceCode === "cwa" ||
|
||||
sourceCode === "noaa" ||
|
||||
sourceCode === "wunderground";
|
||||
sourceCode === "noaa";
|
||||
|
||||
const sourceDisplay = (() => {
|
||||
if (sourceCode === "hko") {
|
||||
@@ -3211,6 +3215,15 @@ export function getCityProfileStats(detail: CityDetail, locale: Locale = "zh-CN"
|
||||
? `${noaaName}${noaaCode ? ` (${noaaCode})` : ""}`
|
||||
: `${noaaName}${noaaCode ? `(${noaaCode})` : ""}`;
|
||||
}
|
||||
if (sourceCode === "wunderground") {
|
||||
const icao = String(detail.risk?.icao || detail.current?.station_code || "")
|
||||
.trim()
|
||||
.toUpperCase();
|
||||
const stationName = String(
|
||||
detail.current?.station_name || detail.risk?.airport || "",
|
||||
).trim();
|
||||
return `${stationName || icao || "Airport"}${icao ? ` (${icao} METAR)` : " METAR"}`;
|
||||
}
|
||||
const stationName = String(
|
||||
detail.current?.station_name || detail.risk?.airport || "",
|
||||
).trim();
|
||||
@@ -3343,8 +3356,7 @@ export function getSettlementRiskNarrative(
|
||||
const stationTerm =
|
||||
sourceCode === "hko" ||
|
||||
sourceCode === "cwa" ||
|
||||
sourceCode === "noaa" ||
|
||||
sourceCode === "wunderground"
|
||||
sourceCode === "noaa"
|
||||
? isEnglish(locale)
|
||||
? "settlement reference station"
|
||||
: "结算参考站"
|
||||
|
||||
Reference in New Issue
Block a user