85 lines
2.4 KiB
TypeScript
85 lines
2.4 KiB
TypeScript
import type { CityDetail } from "@/lib/dashboard-types";
|
|
|
|
export type MonitorTemperatureSource =
|
|
| "amsc_awos_runway_max"
|
|
| "amsc_awos_runway"
|
|
| "amos_runway_median"
|
|
| "amos_runway"
|
|
| "amos"
|
|
| "airport_primary"
|
|
| "airport_current"
|
|
| "current"
|
|
| "missing";
|
|
|
|
export type MonitorTemperature = {
|
|
source: MonitorTemperatureSource;
|
|
value: number | null;
|
|
};
|
|
|
|
function finiteNumber(value: unknown): number | null {
|
|
const num = Number(value);
|
|
return Number.isFinite(num) ? num : null;
|
|
}
|
|
|
|
function median(values: number[]) {
|
|
if (!values.length) return null;
|
|
const sorted = [...values].sort((a, b) => a - b);
|
|
const mid = Math.floor(sorted.length / 2);
|
|
return sorted.length % 2 === 1
|
|
? sorted[mid]
|
|
: (sorted[mid - 1] + sorted[mid]) / 2;
|
|
}
|
|
|
|
export function getAmosRunwayTemperature(detail?: CityDetail | null) {
|
|
const runwayTemps =
|
|
detail?.amos?.runway_obs?.temperatures ||
|
|
detail?.amos?.runway_temps ||
|
|
[];
|
|
const values = runwayTemps
|
|
.map((pair) => finiteNumber(pair?.[0]))
|
|
.filter((value): value is number => value != null);
|
|
if (detail?.amos?.source === "amsc_awos") {
|
|
if (!values.length) return null;
|
|
return {
|
|
source: values.length > 1 ? "amsc_awos_runway_max" : "amsc_awos_runway",
|
|
value: Math.max(...values),
|
|
} satisfies MonitorTemperature;
|
|
}
|
|
const value = median(values);
|
|
if (value == null) return null;
|
|
return {
|
|
source: values.length > 1 ? "amos_runway_median" : "amos_runway",
|
|
value,
|
|
} satisfies MonitorTemperature;
|
|
}
|
|
|
|
export function resolveMonitorTemperature(
|
|
detail?: CityDetail | null,
|
|
options?: { ignoreRunway?: boolean },
|
|
): MonitorTemperature {
|
|
if (!options?.ignoreRunway) {
|
|
const runway = getAmosRunwayTemperature(detail);
|
|
if (runway) return runway;
|
|
}
|
|
|
|
const amosTemp = finiteNumber(detail?.amos?.temp ?? detail?.amos?.temp_c);
|
|
if (
|
|
!options?.ignoreRunway &&
|
|
amosTemp != null &&
|
|
detail?.amos?.temp_source === "runway_median"
|
|
) {
|
|
return { source: "amos", value: amosTemp };
|
|
}
|
|
|
|
const airportPrimary = finiteNumber(detail?.airport_primary?.temp);
|
|
if (airportPrimary != null) return { source: "airport_primary", value: airportPrimary };
|
|
|
|
const airport = finiteNumber(detail?.airport_current?.temp);
|
|
if (airport != null) return { source: "airport_current", value: airport };
|
|
|
|
const current = finiteNumber(detail?.current?.temp);
|
|
if (current != null) return { source: "current", value: current };
|
|
|
|
return { source: "missing", value: null };
|
|
}
|