Show observed temp and format observation updates
This commit is contained in:
@@ -2837,9 +2837,151 @@ export function getHistorySummary(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toFiniteNumber(value: unknown): number | null {
|
||||||
|
const numeric = Number(value);
|
||||||
|
return Number.isFinite(numeric) ? numeric : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function asRecord(value: unknown): Record<string, unknown> | null {
|
||||||
|
return value && typeof value === "object"
|
||||||
|
? (value as Record<string, unknown>)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function firstFiniteNumber(values: unknown[]) {
|
||||||
|
for (const value of values) {
|
||||||
|
const numeric = toFiniteNumber(value);
|
||||||
|
if (numeric != null) return numeric;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function firstNonEmptyString(values: unknown[]) {
|
||||||
|
for (const value of values) {
|
||||||
|
const text = String(value ?? "").trim();
|
||||||
|
if (text) return text;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatObservationUpdate(value: unknown, locale: Locale) {
|
||||||
|
const raw = String(value ?? "").trim();
|
||||||
|
if (!raw) return null;
|
||||||
|
|
||||||
|
const looksDated =
|
||||||
|
/^\d{4}-\d{2}-\d{2}/.test(raw) ||
|
||||||
|
raw.includes("T") ||
|
||||||
|
/[+-]\d{2}:?\d{2}$/.test(raw);
|
||||||
|
if (looksDated) {
|
||||||
|
const parsed = new Date(raw);
|
||||||
|
if (!Number.isNaN(parsed.getTime())) {
|
||||||
|
return new Intl.DateTimeFormat(isEnglish(locale) ? "en-US" : "zh-CN", {
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
hour12: false,
|
||||||
|
minute: "2-digit",
|
||||||
|
month: "2-digit",
|
||||||
|
})
|
||||||
|
.format(parsed)
|
||||||
|
.replace(",", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizeHm(raw) || raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOfficialObservationCandidates(detail: CityDetail) {
|
||||||
|
const officialNearby = Array.isArray(detail.official_nearby)
|
||||||
|
? detail.official_nearby
|
||||||
|
: [];
|
||||||
|
const mgmNearby = Array.isArray(detail.mgm_nearby) ? detail.mgm_nearby : [];
|
||||||
|
const officialAnchor =
|
||||||
|
officialNearby.find(
|
||||||
|
(station) => station.is_settlement_anchor || station.is_airport_station,
|
||||||
|
) || officialNearby[0];
|
||||||
|
|
||||||
|
return {
|
||||||
|
centerStation: detail.center_station_candidate,
|
||||||
|
mgmNearby,
|
||||||
|
officialAnchor,
|
||||||
|
officialNearby,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getObservedTemperatureProfile(detail: CityDetail, locale: Locale) {
|
||||||
|
const { centerStation, officialAnchor, officialNearby, mgmNearby } =
|
||||||
|
getOfficialObservationCandidates(detail);
|
||||||
|
const observedTemp = firstFiniteNumber([
|
||||||
|
detail.current?.temp,
|
||||||
|
detail.airport_primary?.temp,
|
||||||
|
detail.airport_current?.temp,
|
||||||
|
centerStation?.temp,
|
||||||
|
officialAnchor?.temp,
|
||||||
|
officialNearby[0]?.temp,
|
||||||
|
mgmNearby[0]?.temp,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (observedTemp == null) {
|
||||||
|
return isEnglish(locale) ? "Unavailable" : "未提供";
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceLabel = firstNonEmptyString([
|
||||||
|
detail.current?.settlement_source_label,
|
||||||
|
detail.airport_primary?.source_label,
|
||||||
|
detail.airport_current?.source_label,
|
||||||
|
centerStation?.source_label,
|
||||||
|
officialAnchor?.source_label,
|
||||||
|
officialNearby[0]?.source_label,
|
||||||
|
mgmNearby[0]?.source_label,
|
||||||
|
getObservationSourceTag(detail),
|
||||||
|
]);
|
||||||
|
const rounded =
|
||||||
|
Math.abs(observedTemp - Math.round(observedTemp)) < 0.05
|
||||||
|
? String(Math.round(observedTemp))
|
||||||
|
: observedTemp.toFixed(1);
|
||||||
|
|
||||||
|
return `${rounded}${detail.temp_symbol || "°C"}${
|
||||||
|
sourceLabel ? ` · ${sourceLabel}` : ""
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getObservationUpdateProfile(detail: CityDetail, locale: Locale) {
|
||||||
|
const { centerStation, officialAnchor, officialNearby, mgmNearby } =
|
||||||
|
getOfficialObservationCandidates(detail);
|
||||||
|
const centerRecord = asRecord(centerStation);
|
||||||
|
const officialAnchorRecord = asRecord(officialAnchor);
|
||||||
|
const officialFirstRecord = asRecord(officialNearby[0]);
|
||||||
|
const mgmFirstRecord = asRecord(mgmNearby[0]);
|
||||||
|
const rawValue = firstNonEmptyString([
|
||||||
|
detail.current?.obs_time,
|
||||||
|
detail.current?.report_time,
|
||||||
|
detail.airport_primary?.obs_time,
|
||||||
|
detail.airport_primary?.report_time,
|
||||||
|
detail.airport_current?.obs_time,
|
||||||
|
detail.airport_current?.report_time,
|
||||||
|
centerRecord?.obs_time,
|
||||||
|
centerRecord?.report_time,
|
||||||
|
centerRecord?.time,
|
||||||
|
officialAnchorRecord?.obs_time,
|
||||||
|
officialAnchorRecord?.report_time,
|
||||||
|
officialAnchorRecord?.time,
|
||||||
|
officialFirstRecord?.obs_time,
|
||||||
|
officialFirstRecord?.report_time,
|
||||||
|
officialFirstRecord?.time,
|
||||||
|
mgmFirstRecord?.obs_time,
|
||||||
|
mgmFirstRecord?.report_time,
|
||||||
|
mgmFirstRecord?.time,
|
||||||
|
detail.updated_at,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
formatObservationUpdate(rawValue, locale) ||
|
||||||
|
(isEnglish(locale) ? "Unavailable" : "未提供")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function getCityProfileStats(detail: CityDetail, locale: Locale = "zh-CN") {
|
export function getCityProfileStats(detail: CityDetail, locale: Locale = "zh-CN") {
|
||||||
const risk = detail.risk || {};
|
const risk = detail.risk || {};
|
||||||
const current = detail.current || {};
|
|
||||||
const nearbyCount = Array.isArray(detail.mgm_nearby) ? detail.mgm_nearby.length : 0;
|
const nearbyCount = Array.isArray(detail.mgm_nearby) ? detail.mgm_nearby.length : 0;
|
||||||
const nearbySource = String(detail.nearby_source || "").trim().toLowerCase();
|
const nearbySource = String(detail.nearby_source || "").trim().toLowerCase();
|
||||||
const sourceCode = getObservationSourceCode(detail);
|
const sourceCode = getObservationSourceCode(detail);
|
||||||
@@ -2911,12 +3053,13 @@ export function getCityProfileStats(detail: CityDetail, locale: Locale = "zh-CN"
|
|||||||
? "Not marked"
|
? "Not marked"
|
||||||
: "未标注",
|
: "未标注",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: isEnglish(locale) ? "Observed temp" : "实测温度",
|
||||||
|
value: getObservedTemperatureProfile(detail, locale),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: isEnglish(locale) ? "Observation update" : "观测更新",
|
label: isEnglish(locale) ? "Observation update" : "观测更新",
|
||||||
value:
|
value: getObservationUpdateProfile(detail, locale),
|
||||||
current.obs_time ||
|
|
||||||
detail.updated_at ||
|
|
||||||
(isEnglish(locale) ? "Unavailable" : "未提供"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: isEnglish(locale) ? "Nearby stations" : "周边站点",
|
label: isEnglish(locale) ? "Nearby stations" : "周边站点",
|
||||||
|
|||||||
Reference in New Issue
Block a user